defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL
attr EVU_Tibber_connect DbLogExclude .*
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc._trigger.*
attr EVU_Tibber_connect DbLogValueFn {\
  if ($READING =~ /fc0_trigger/) {\
    my ($sec, $min, $hour, $mday, $mon, $year) = localtime(time_str2num($TIMESTAMP));;\
   $year += 1900;;\
   $mon  += 1;;\
   my $quarter = int($min / 15) * 15;;  # Minuten auf 0, 15, 30 oder 45 runden\
\
   $TIMESTAMP = sprintf("%04d-%02d-%02d %02d:%02d:00", $year, $mon, $mday, $hour, $quarter);;\
\
  }\
}
attr EVU_Tibber_connect comment Version 2025.10.30 11:00 \
https://developer.tibber.com/explorer\
\
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\
\
{KeyValue("store","EVU_Tibber_connect_token","5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE")}\
{KeyValue("store","EVU_Tibber_connect_homeID","96a14971-525a-4420-aae9-e5aedaa129ff")}\
\
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\
setreading EVU_Tibber compensation_grid <Eure Einspeisevergütung>\
\
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\
    (fc_avg - fc_min)  /2 + fc_min\
\
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\
    (fc_avg - compensation_grid) *0.85
attr EVU_Tibber_connect disable 0
attr EVU_Tibber_connect enableControlSet 1
attr EVU_Tibber_connect get01-1Name current_currency
attr EVU_Tibber_connect get01-2Name current_level
attr EVU_Tibber_connect get01-3Name current_date
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get01-4Name current_price
attr EVU_Tibber_connect get01-4OExpr $val *100
attr EVU_Tibber_connect get01Data { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo (resolution: QUARTER_HOURLY){current {total startsAt currency level}}}}}}" }
attr EVU_Tibber_connect get01Header01 Content-Type: application/json
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current
attr EVU_Tibber_connect get01Name 01_priceInfo
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get02-101Name fc0_0545_total
attr EVU_Tibber_connect get02-105Name fc0_0600_total
attr EVU_Tibber_connect get02-109Name fc0_0615_total
attr EVU_Tibber_connect get02-113Name fc0_0630_total
attr EVU_Tibber_connect get02-117Name fc0_0645_total
attr EVU_Tibber_connect get02-11Name fc0_0000_total
attr EVU_Tibber_connect get02-121Name fc0_0700_total
attr EVU_Tibber_connect get02-125Name fc0_0715_total
attr EVU_Tibber_connect get02-129Name fc0_0730_total
attr EVU_Tibber_connect get02-12Name fc0_0000_startsAt
attr EVU_Tibber_connect get02-12OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-133Name fc0_0745_total
attr EVU_Tibber_connect get02-137Name fc0_0800_total
attr EVU_Tibber_connect get02-13Name fc0_0015_total
attr EVU_Tibber_connect get02-141Name fc0_0815_total
attr EVU_Tibber_connect get02-145Name fc0_0830_total
attr EVU_Tibber_connect get02-149Name fc0_0845_total
attr EVU_Tibber_connect get02-153Name fc0_0900_total
attr EVU_Tibber_connect get02-157Name fc0_0915_total
attr EVU_Tibber_connect get02-161Name fc0_0930_total
attr EVU_Tibber_connect get02-165Name fc0_0945_total
attr EVU_Tibber_connect get02-169Name fc0_1000_total
attr EVU_Tibber_connect get02-173Name fc0_1015_total
attr EVU_Tibber_connect get02-177Name fc0_1030_total
attr EVU_Tibber_connect get02-17Name fc0_0030_total
attr EVU_Tibber_connect get02-181Name fc0_1045_total
attr EVU_Tibber_connect get02-185Name fc0_1100_total
attr EVU_Tibber_connect get02-189Name fc0_1115_total
attr EVU_Tibber_connect get02-193Name fc0_1130_total
attr EVU_Tibber_connect get02-197Name fc0_1145_total
attr EVU_Tibber_connect get02-1Name current_price
attr EVU_Tibber_connect get02-1OExpr $val *100
attr EVU_Tibber_connect get02-201Name fc0_1200_total
attr EVU_Tibber_connect get02-205Name fc0_1215_total
attr EVU_Tibber_connect get02-209Name fc0_1230_total
attr EVU_Tibber_connect get02-213Name fc0_1245_total
attr EVU_Tibber_connect get02-217Name fc0_1300_total
attr EVU_Tibber_connect get02-21Name fc0_0045_total
attr EVU_Tibber_connect get02-221Name fc0_1315_total
attr EVU_Tibber_connect get02-225Name fc0_1330_total
attr EVU_Tibber_connect get02-229Name fc0_1345_total
attr EVU_Tibber_connect get02-233Name fc0_1400_total
attr EVU_Tibber_connect get02-237Name fc0_1415_total
attr EVU_Tibber_connect get02-241Name fc0_1430_total
attr EVU_Tibber_connect get02-245Name fc0_1445_total
attr EVU_Tibber_connect get02-249Name fc0_1500_total
attr EVU_Tibber_connect get02-253Name fc0_1515_total
attr EVU_Tibber_connect get02-257Name fc0_1530_total
attr EVU_Tibber_connect get02-25Name fc0_0100_total
attr EVU_Tibber_connect get02-261Name fc0_1545_total
attr EVU_Tibber_connect get02-265Name fc0_1600_total
attr EVU_Tibber_connect get02-269Name fc0_1615_total
attr EVU_Tibber_connect get02-273Name fc0_1630_total
attr EVU_Tibber_connect get02-277Name fc0_1645_total
attr EVU_Tibber_connect get02-281Name fc0_1700_total
attr EVU_Tibber_connect get02-285Name fc0_1715_total
attr EVU_Tibber_connect get02-289Name fc0_1730_total
attr EVU_Tibber_connect get02-293Name fc0_1745_total
attr EVU_Tibber_connect get02-297Name fc0_1800_total
attr EVU_Tibber_connect get02-29Name fc0_0115_total
attr EVU_Tibber_connect get02-301Name fc0_1815_total
attr EVU_Tibber_connect get02-305Name fc0_1830_total
attr EVU_Tibber_connect get02-309Name fc0_1845_total
attr EVU_Tibber_connect get02-313Name fc0_1900_total
attr EVU_Tibber_connect get02-317Name fc0_1915_total
attr EVU_Tibber_connect get02-321Name fc0_1930_total
attr EVU_Tibber_connect get02-325Name fc0_1945_total
attr EVU_Tibber_connect get02-329Name fc0_2000_total
attr EVU_Tibber_connect get02-333Name fc0_2015_total
attr EVU_Tibber_connect get02-337Name fc0_2030_total
attr EVU_Tibber_connect get02-33Name fc0_0130_total
attr EVU_Tibber_connect get02-341Name fc0_2045_total
attr EVU_Tibber_connect get02-345Name fc0_2100_total
attr EVU_Tibber_connect get02-349Name fc0_2115_total
attr EVU_Tibber_connect get02-353Name fc0_2130_total
attr EVU_Tibber_connect get02-357Name fc0_2145_total
attr EVU_Tibber_connect get02-361Name fc0_2200_total
attr EVU_Tibber_connect get02-365Name fc0_2215_total
attr EVU_Tibber_connect get02-369Name fc0_2230_total
attr EVU_Tibber_connect get02-373Name fc0_2245_total
attr EVU_Tibber_connect get02-377Name fc0_2300_total
attr EVU_Tibber_connect get02-37Name fc0_0145_total
attr EVU_Tibber_connect get02-381Name fc0_2315_total
attr EVU_Tibber_connect get02-385Name fc0_2330_total
attr EVU_Tibber_connect get02-389Name fc0_2345_total
attr EVU_Tibber_connect get02-395Name fc1_0000_total
attr EVU_Tibber_connect get02-396Name fc1_0000_startsAt
attr EVU_Tibber_connect get02-396OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-397Name fc1_0015_total
attr EVU_Tibber_connect get02-401Name fc1_0030_total
attr EVU_Tibber_connect get02-405Name fc1_0045_total
attr EVU_Tibber_connect get02-409Name fc1_0100_total
attr EVU_Tibber_connect get02-413Name fc1_0115_total
attr EVU_Tibber_connect get02-417Name fc1_0130_total
attr EVU_Tibber_connect get02-41Name fc0_0200_total
attr EVU_Tibber_connect get02-421Name fc1_0145_total
attr EVU_Tibber_connect get02-425Name fc1_0200_total
attr EVU_Tibber_connect get02-429Name fc1_0215_total
attr EVU_Tibber_connect get02-433Name fc1_0230_total
attr EVU_Tibber_connect get02-437Name fc1_0245_total
attr EVU_Tibber_connect get02-441Name fc1_0300_total
attr EVU_Tibber_connect get02-445Name fc1_0315_total
attr EVU_Tibber_connect get02-449Name fc1_0330_total
attr EVU_Tibber_connect get02-453Name fc1_0345_total
attr EVU_Tibber_connect get02-457Name fc1_0400_total
attr EVU_Tibber_connect get02-45Name fc0_0215_total
attr EVU_Tibber_connect get02-461Name fc1_0415_total
attr EVU_Tibber_connect get02-465Name fc1_0430_total
attr EVU_Tibber_connect get02-469Name fc1_0445_total
attr EVU_Tibber_connect get02-473Name fc1_0500_total
attr EVU_Tibber_connect get02-477Name fc1_0515_total
attr EVU_Tibber_connect get02-481Name fc1_0530_total
attr EVU_Tibber_connect get02-485Name fc1_0545_total
attr EVU_Tibber_connect get02-489Name fc1_0600_total
attr EVU_Tibber_connect get02-493Name fc1_0615_total
attr EVU_Tibber_connect get02-497Name fc1_0630_total
attr EVU_Tibber_connect get02-49Name fc0_0230_total
attr EVU_Tibber_connect get02-501Name fc1_0645_total
attr EVU_Tibber_connect get02-505Name fc1_0700_total
attr EVU_Tibber_connect get02-509Name fc1_0715_total
attr EVU_Tibber_connect get02-513Name fc1_0730_total
attr EVU_Tibber_connect get02-517Name fc1_0745_total
attr EVU_Tibber_connect get02-521Name fc1_0800_total
attr EVU_Tibber_connect get02-525Name fc1_0815_total
attr EVU_Tibber_connect get02-529Name fc1_0830_total
attr EVU_Tibber_connect get02-533Name fc1_0845_total
attr EVU_Tibber_connect get02-537Name fc1_0900_total
attr EVU_Tibber_connect get02-53Name fc0_0245_total
attr EVU_Tibber_connect get02-541Name fc1_0915_total
attr EVU_Tibber_connect get02-545Name fc1_0930_total
attr EVU_Tibber_connect get02-549Name fc1_0945_total
attr EVU_Tibber_connect get02-553Name fc1_1000_total
attr EVU_Tibber_connect get02-557Name fc1_1015_total
attr EVU_Tibber_connect get02-561Name fc1_1030_total
attr EVU_Tibber_connect get02-565Name fc1_1045_total
attr EVU_Tibber_connect get02-569Name fc1_1100_total
attr EVU_Tibber_connect get02-573Name fc1_1115_total
attr EVU_Tibber_connect get02-577Name fc1_1130_total
attr EVU_Tibber_connect get02-57Name fc0_0300_total
attr EVU_Tibber_connect get02-581Name fc1_1145_total
attr EVU_Tibber_connect get02-585Name fc1_1200_total
attr EVU_Tibber_connect get02-589Name fc1_1215_total
attr EVU_Tibber_connect get02-593Name fc1_1230_total
attr EVU_Tibber_connect get02-597Name fc1_1245_total
attr EVU_Tibber_connect get02-601Name fc1_1300_total
attr EVU_Tibber_connect get02-605Name fc1_1315_total
attr EVU_Tibber_connect get02-609Name fc1_1330_total
attr EVU_Tibber_connect get02-613Name fc1_1345_total
attr EVU_Tibber_connect get02-617Name fc1_1400_total
attr EVU_Tibber_connect get02-61Name fc0_0315_total
attr EVU_Tibber_connect get02-621Name fc1_1415_total
attr EVU_Tibber_connect get02-625Name fc1_1430_total
attr EVU_Tibber_connect get02-629Name fc1_1445_total
attr EVU_Tibber_connect get02-633Name fc1_1500_total
attr EVU_Tibber_connect get02-637Name fc1_1515_total
attr EVU_Tibber_connect get02-641Name fc1_1530_total
attr EVU_Tibber_connect get02-645Name fc1_1545_total
attr EVU_Tibber_connect get02-649Name fc1_1600_total
attr EVU_Tibber_connect get02-653Name fc1_1615_total
attr EVU_Tibber_connect get02-657Name fc1_1630_total
attr EVU_Tibber_connect get02-65Name fc0_0330_total
attr EVU_Tibber_connect get02-661Name fc1_1645_total
attr EVU_Tibber_connect get02-665Name fc1_1700_total
attr EVU_Tibber_connect get02-669Name fc1_1715_total
attr EVU_Tibber_connect get02-673Name fc1_1730_total
attr EVU_Tibber_connect get02-677Name fc1_1745_total
attr EVU_Tibber_connect get02-681Name fc1_1800_total
attr EVU_Tibber_connect get02-685Name fc1_1815_total
attr EVU_Tibber_connect get02-689Name fc1_1830_total
attr EVU_Tibber_connect get02-693Name fc1_1845_total
attr EVU_Tibber_connect get02-697Name fc1_1900_total
attr EVU_Tibber_connect get02-69Name fc0_0345_total
attr EVU_Tibber_connect get02-6Name current_date
attr EVU_Tibber_connect get02-6OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-701Name fc1_1915_total
attr EVU_Tibber_connect get02-705Name fc1_1930_total
attr EVU_Tibber_connect get02-709Name fc1_1945_total
attr EVU_Tibber_connect get02-713Name fc1_2000_total
attr EVU_Tibber_connect get02-717Name fc1_2015_total
attr EVU_Tibber_connect get02-721Name fc1_2030_total
attr EVU_Tibber_connect get02-725Name fc1_2045_total
attr EVU_Tibber_connect get02-729Name fc1_2100_total
attr EVU_Tibber_connect get02-733Name fc1_2115_total
attr EVU_Tibber_connect get02-737Name fc1_2130_total
attr EVU_Tibber_connect get02-73Name fc0_0400_total
attr EVU_Tibber_connect get02-741Name fc1_2145_total
attr EVU_Tibber_connect get02-745Name fc1_2200_total
attr EVU_Tibber_connect get02-749Name fc1_2215_total
attr EVU_Tibber_connect get02-753Name fc1_2230_total
attr EVU_Tibber_connect get02-757Name fc1_2245_total
attr EVU_Tibber_connect get02-761Name fc1_2300_total
attr EVU_Tibber_connect get02-765Name fc1_2315_total
attr EVU_Tibber_connect get02-769Name fc1_2330_total
attr EVU_Tibber_connect get02-773Name fc1_2345_total
attr EVU_Tibber_connect get02-77Name fc0_0415_total
attr EVU_Tibber_connect get02-81Name fc0_0430_total
attr EVU_Tibber_connect get02-85Name fc0_0445_total
attr EVU_Tibber_connect get02-89Name fc0_0500_total
attr EVU_Tibber_connect get02-93Name fc0_0515_total
attr EVU_Tibber_connect get02-97Name fc0_0530_total
attr EVU_Tibber_connect get02Data { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo (resolution: QUARTER_HOURLY)  {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}" }
attr EVU_Tibber_connect get02DeleteIfUnmatched 1
attr EVU_Tibber_connect get02Header01 Content-Type: application/json
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get02Name 02_priceAll
attr EVU_Tibber_connect get02RegOpt g
attr EVU_Tibber_connect get02Regex "total":(-*\d+.\d+)|"startsAt":"(\d+-\d+-\d+T\d+:\d+:\d+).*(?="today")|\[\{"total":(-*\d+.\d+),"startsAt":"(\d+-\d+-\d+T\d+:\d+:\d+)
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get03-1Name nodes_00_00_from
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost
attr EVU_Tibber_connect get03-2OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption
attr EVU_Tibber_connect get03-4Name nodes_00_01_from
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-50OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost
attr EVU_Tibber_connect get03-5OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption
attr EVU_Tibber_connect get03-7Name nodes_00_02_from
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost
attr EVU_Tibber_connect get03-8OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption
attr EVU_Tibber_connect get03Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}"}
attr EVU_Tibber_connect get03Header01 Content-Type: application/json
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get03Name 03_consumption_hour
attr EVU_Tibber_connect get03RegOpt g
attr EVU_Tibber_connect get03Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|-*\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get04-10Name nodes_24_03_from
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost
attr EVU_Tibber_connect get04-11OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption
attr EVU_Tibber_connect get04-13Name nodes_24_04_from
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost
attr EVU_Tibber_connect get04-14OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption
attr EVU_Tibber_connect get04-16Name nodes_24_05_from
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost
attr EVU_Tibber_connect get04-17OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption
attr EVU_Tibber_connect get04-19Name nodes_24_06_from
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-1Name nodes_24_00_from
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost
attr EVU_Tibber_connect get04-20OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption
attr EVU_Tibber_connect get04-22Name nodes_24_07_from
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost
attr EVU_Tibber_connect get04-23OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption
attr EVU_Tibber_connect get04-25Name nodes_24_08_from
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost
attr EVU_Tibber_connect get04-26OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption
attr EVU_Tibber_connect get04-28Name nodes_24_09_from
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost
attr EVU_Tibber_connect get04-29OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost
attr EVU_Tibber_connect get04-2OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption
attr EVU_Tibber_connect get04-31Name nodes_24_10_from
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost
attr EVU_Tibber_connect get04-32OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption
attr EVU_Tibber_connect get04-34Name nodes_24_11_from
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost
attr EVU_Tibber_connect get04-35OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption
attr EVU_Tibber_connect get04-37Name nodes_24_12_from
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost
attr EVU_Tibber_connect get04-38OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption
attr EVU_Tibber_connect get04-40Name nodes_24_13_from
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost
attr EVU_Tibber_connect get04-41OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption
attr EVU_Tibber_connect get04-43Name nodes_24_14_from
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost
attr EVU_Tibber_connect get04-44OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption
attr EVU_Tibber_connect get04-46Name nodes_24_15_from
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost
attr EVU_Tibber_connect get04-47OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption
attr EVU_Tibber_connect get04-49Name nodes_24_16_from
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-4Name nodes_24_01_from
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost
attr EVU_Tibber_connect get04-50OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption
attr EVU_Tibber_connect get04-52Name nodes_24_17_from
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost
attr EVU_Tibber_connect get04-53OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption
attr EVU_Tibber_connect get04-55Name nodes_24_18_from
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost
attr EVU_Tibber_connect get04-56OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption
attr EVU_Tibber_connect get04-58Name nodes_24_19_from
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost
attr EVU_Tibber_connect get04-59OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost
attr EVU_Tibber_connect get04-5OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption
attr EVU_Tibber_connect get04-61Name nodes_24_20_from
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost
attr EVU_Tibber_connect get04-62OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption
attr EVU_Tibber_connect get04-64Name nodes_24_21_from
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost
attr EVU_Tibber_connect get04-65OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption
attr EVU_Tibber_connect get04-67Name nodes_24_22_from
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost
attr EVU_Tibber_connect get04-68OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption
attr EVU_Tibber_connect get04-70Name nodes_24_23_from
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost
attr EVU_Tibber_connect get04-71OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption
attr EVU_Tibber_connect get04-7Name nodes_24_02_from
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost
attr EVU_Tibber_connect get04-8OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption
attr EVU_Tibber_connect get04Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}"}
attr EVU_Tibber_connect get04Header01 Content-Type: application/json
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get04Name 04_consumption_hour_24
attr EVU_Tibber_connect get04RegOpt g
attr EVU_Tibber_connect get04Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|-*\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get05AutoNumLen 4
attr EVU_Tibber_connect get05Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}"}
attr EVU_Tibber_connect get05Header01 Content-Type: application/json
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get05JSON data_viewer_home
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort
attr EVU_Tibber_connect get06-3Name Adresse_06_Land
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort
attr EVU_Tibber_connect get06-5Name Adresse_06_Land
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail
attr EVU_Tibber_connect get06Data { "query": "{viewer {home(id:\"%%homeID%%\") {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}" }
attr EVU_Tibber_connect get06Header01 Content-Type: application/json
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get06JSON data_viewer_home
attr EVU_Tibber_connect get06Name 06_address
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get07-10Name nodes_24_03_from
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost
attr EVU_Tibber_connect get07-11OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption
attr EVU_Tibber_connect get07-13Name nodes_24_04_from
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost
attr EVU_Tibber_connect get07-14OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption
attr EVU_Tibber_connect get07-16Name nodes_24_05_from
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost
attr EVU_Tibber_connect get07-17OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption
attr EVU_Tibber_connect get07-19Name nodes_24_06_from
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost
attr EVU_Tibber_connect get07-20OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption
attr EVU_Tibber_connect get07-22Name nodes_24_07_from
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost
attr EVU_Tibber_connect get07-23OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption
attr EVU_Tibber_connect get07-25Name nodes_24_08_from
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost
attr EVU_Tibber_connect get07-26OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption
attr EVU_Tibber_connect get07-28Name nodes_24_09_from
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost
attr EVU_Tibber_connect get07-29OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-2Name features_id
attr EVU_Tibber_connect get07-2OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption
attr EVU_Tibber_connect get07-31Name nodes_24_10_from
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost
attr EVU_Tibber_connect get07-32OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption
attr EVU_Tibber_connect get07-34Name nodes_24_11_from
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost
attr EVU_Tibber_connect get07-35OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption
attr EVU_Tibber_connect get07-37Name nodes_24_12_from
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost
attr EVU_Tibber_connect get07-38OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption
attr EVU_Tibber_connect get07-40Name nodes_24_13_from
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost
attr EVU_Tibber_connect get07-41OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption
attr EVU_Tibber_connect get07-43Name nodes_24_14_from
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost
attr EVU_Tibber_connect get07-44OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption
attr EVU_Tibber_connect get07-46Name nodes_24_15_from
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost
attr EVU_Tibber_connect get07-47OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption
attr EVU_Tibber_connect get07-49Name nodes_24_16_from
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-4Name nodes_24_01_from
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost
attr EVU_Tibber_connect get07-50OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption
attr EVU_Tibber_connect get07-52Name nodes_24_17_from
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost
attr EVU_Tibber_connect get07-53OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption
attr EVU_Tibber_connect get07-55Name nodes_24_18_from
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost
attr EVU_Tibber_connect get07-56OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption
attr EVU_Tibber_connect get07-58Name nodes_24_19_from
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost
attr EVU_Tibber_connect get07-59OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost
attr EVU_Tibber_connect get07-5OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption
attr EVU_Tibber_connect get07-61Name nodes_24_20_from
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost
attr EVU_Tibber_connect get07-62OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption
attr EVU_Tibber_connect get07-64Name nodes_24_21_from
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost
attr EVU_Tibber_connect get07-65OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption
attr EVU_Tibber_connect get07-67Name nodes_24_22_from
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost
attr EVU_Tibber_connect get07-68OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption
attr EVU_Tibber_connect get07-70Name nodes_24_23_from
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost
attr EVU_Tibber_connect get07-71OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption
attr EVU_Tibber_connect get07-7Name nodes_24_02_from
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost
attr EVU_Tibber_connect get07-8OExpr ($val ne "0" and $val ne "null")? round($val*1.19,4) : $val
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption
attr EVU_Tibber_connect get07Data { "query": "{viewer {home(id:\"%%homeID%%\") {id features{realTimeConsumptionEnabled} } } }" }
attr EVU_Tibber_connect get07Header01 Content-Type: application/json
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get07JSON data_viewer_home
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled
attr EVU_Tibber_connect get07RegOpt g
attr EVU_Tibber_connect get07Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|-*\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect group PV Steuerung EVU
attr EVU_Tibber_connect icon stromzaehler_icon
attr EVU_Tibber_connect replacement01Mode expression
attr EVU_Tibber_connect replacement01Regex %%token%%
attr EVU_Tibber_connect replacement01Value {KeyValue("read","EVU_Tibber_connect_token")}
attr EVU_Tibber_connect replacement02Mode expression
attr EVU_Tibber_connect replacement02Regex %%homeID%%
attr EVU_Tibber_connect replacement02Value {KeyValue("read","EVU_Tibber_connect_homeID")}
attr EVU_Tibber_connect requestData { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}" }
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%
attr EVU_Tibber_connect room Strom->Boerse
attr EVU_Tibber_connect showBody 0
attr EVU_Tibber_connect showError 1
attr EVU_Tibber_connect sortby 313
attr EVU_Tibber_connect timeout 30
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\
    my $hash = $defs{$name};;\
    my $devState = DevIo_IsOpen($hash);;\
    return "Device already open" if (defined($devState));;\
    \
    # establish connection to websocket\
    # format must also include portnumber if a path is to be specified\
    $hash->{DeviceName} = AttrVal($name, "ws_websocketURL", "wss:echo.websocket.org:443");;\
    \
    # special headers needed for Tibber, see also Developer Tools in Browser\
    $hash->{header}{'Sec-WebSocket-Protocol'} = 'graphql-transport-ws';;\
    $hash->{header}{'Host'} = 'websocket-api.tibber.com';;\
    $hash->{header}{'Origin'} = 'https://developer.tibber.com';;\
    \
    # callback function when "select()" signals data for us\
    # websocket Ping/Pongs are treated in DevIo but still call this function\
    $hash->{directReadFn} = sub () {\
        my $hash = $defs{$name};;\
        \
        # we can read without closing the DevIo, because select() signalled data\
        my $buf = DevIo_SimpleRead($hash);;\
        \
        # if read fails, close device\
        if(!defined($buf)) {\
            DevIo_CloseDev($hash);;\
            $buf = "not_connected";;\
        }\
        \
        #Log(3, "$name:$reading: websocket data: >>>$buf<<<");;\
        \
        # only update our reading if buffer is not empty and if last update is older than minInterval\
        if ($buf ne "") {\
            my $websocketDataAge = ReadingsAge($name, "ws_websocketData", 3600);;\
            my $minInterval = AttrVal($name, "ws_minInterval", 0);;\
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\
            \
            readingsBeginUpdate($hash);;\
            readingsBulkUpdate($hash, "ws_websocketData", "$buf") if ($isNext && $websocketDataAge > $minInterval);;\
            readingsBulkUpdate($hash, "ws_websocketData", "$buf") if (!$isNext);;\
            readingsEndUpdate($hash, 1);;\
            #Log(3, "$name:$reading: websocket data written to reading");;\
        }\
    };;\
    \
    # open DevIo websocket\
    DevIo_OpenDev($hash, 0, undef, sub(){\
        my ($hash, $error) = @_;;\
        return "$error" if ($error);;\
        \
        my $token = AttrVal($name, "ws_token", "???");;\
           $token = KeyValue("read","EVU_Tibber_connect_token") if ($token eq "???");;\
\
        DevIo_SimpleWrite($hash, '{"type":"connection_init","payload":{"token":"'.$token.'"}}', 2);;\
    });;\
    readingsBulkUpdate($hash, "ws_websocketData", "");;\
    #Log(3, "$name:$reading: websocket data cleared in reading");;\
      \
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_disconnect:ws_cmd:.disconnect {\
    Log(3, "$name: disconnect");;\
    my $hash = $defs{$name};;\
    RemoveInternalTimer($hash);;\
    DevIo_SimpleRead($hash);;\
    DevIo_CloseDev($hash);;\
\
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onDisconnect {\
    my $myState = ReadingsVal($name, "state", "???");;\
    my $myData = ReadingsVal($name, "ws_websocketData", "???");;\
    return if ($myState ne "disconnected" and $myData ne "not_connected");;\
    \
	## timer callback function, called after a few seconds to initiate a reconnect\
	my $timerFunction = sub() {\
		my ($arg) = @_;;\
		my $hash = $defs{$name};;\
		my $devState = DevIo_IsOpen($hash);;\
		\
		# only re-connect if device is not connected\
		readingsSingleUpdate($hash, "ws_cmd", "connect", 1) if (!defined($devState));;\
	};;\
	RemoveInternalTimer($name.$reading.'Timer');;\
	\
	# wait a random time before reconnect (exponential backoff TBD):\
	my $rwait = int(rand(200)) + 30;;\
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.'Timer');;\
	\
	#set cmd to a new value, informs user and allows to retrigger when timer expires\
	my $hash = $defs{$name};;\
	readingsBulkUpdate($hash, "ws_cmd", "reconnect attempt in $rwait seconds");;\
	\
	return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onTimeout:ws_websocketData:.* {\
	#re-establish websocket connection if no data received in the past ten minutes\
	#but only if our reading "ws_cmd" was not set to the value "disconnect"\
\
        #Log(3, "$name:$reading: websocket data triggert ws_onTimeout");;\
\
	#timeout in seconds when the connection is considered dead\
	my $timeoutTime = 600;;\
	\
	# function to execute when timeout expired\
	# defining the function here in the userReading, allows us to insert variables directly\
	my $timerFunction = sub() {\
		my ($arg) = @_;;\
		my $hash = $defs{$name};;\
		my $rCmd = ReadingsVal($name, "ws_cmd", "???");;\
		my $age  = ReadingsAge($name, "ws_websocketData", 0);;\
		\
		Log(3, "$name: onTimeoutTimer triggered >>$arg<<");;\
		\
		#do not do anything further if disconnect is on purpose\
		if ( $rCmd eq "disconnect" ) {\
			Log(3, "$name: ws_cmd was set to disconnect");;\
			return;;\
		}\
		\
		# for whatever reason, we triggered to soon (80%)\
		if ( $age < $timeoutTime*0.8 ) {\
			Log(3, "$name: ws_websocketData is not outdated");;\
			return;;\
		}\
		\
		DevIo_CloseDev($hash);;\
		Log(3, "$name: onTimeoutTimer closed DevIo...");;\
		\
		readingsSingleUpdate($hash, "ws_cmd", "connect", 1);;\
		Log(3, "$name: onTimeoutTimer set ws_cmd to value 'connect'");;\
	};;\
\
        #Log(3, "$name:$reading: onTimeout function defined");;\
\
	#remove/cancel previous timers, because we got fresh data and countdown starts again\
	RemoveInternalTimer($name.$reading.'Timer');;\
	\
	#set timer to expire and execute function defined above, give special arg as identifier\
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.'Timer');;\
	\
	return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\
    #ws_websocketData contains the string "connection_ack"\
    Log(3, "$name:$reading: got connection ack");;\
    \
    # do not proceed if connection is lost\
    my $hash = $defs{$name};;\
    my $devState = DevIo_IsOpen($hash);;\
    return "Device not open" if (!defined($devState));;\
\
    readingsBulkUpdate($hash, "ws_cmd", "got connection ack");;\
    \
    my $homeId = AttrVal($name, "ws_homeId", "???");;\
       $homeId = KeyValue("read","EVU_Tibber_connect_homeID") if ($homeId eq "???");;\
\
    my $myId = AttrVal($name, "ws_myId", "???");;\
       $myId = KeyValue("read","EVU_Tibber_connect_homeID") if ($myId eq "???");;\
    \
    # build the query, do it in pieces, the comma at the end caused perl errors\
    # so we put it together in this not very elegant way\
    my $json = '{ "id":"'. $myId .'", "type":"subscribe"'.", ";;\
    $json .= '"payload":{';;\
    $json .= '"variables":{}'.", ";;\
    $json .= '"extensions":{}'.", ";;\
    $json .= '"query":"subscription { liveMeasurement( homeId: \"'.$homeId.'\" ) ';;\
    #$json .= '{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}"';;\
    $json .= '{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction ';;\
    $json .= 'accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower ';;\
    $json .= 'powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction ';;\
    $json .= 'powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}"';;\
    $json .= '}}';;\
    \
    #send the string via websocket as ASCII\
    #Log(3, "$name:$reading: sending JSON: >>>$json<<<");;\
    DevIo_SimpleWrite($hash, $json, 2);;\
        \
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\
    #websocketData contains next-live-measurement-data\
    my $val = ReadingsVal($name, "ws_websocketData", "{}");;\
    my %res = %{json2nameValue($val, undef, undef, "payload_data_liveMeasurement.*")};;\
    \
    my $ret = "got values for:\n";;\
    foreach my $k (sort keys %res) {\
        $ret .= "$k\n";;\
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\
    }\
    return $ret;;\
},\
\
nodes_TIMESTAMP:nodes_00_00_cost.* {\
my ($timestamp,$value) = 2x0;;\
my $tmp = 0;;\
\
for (my $loop_last = 0;; $loop_last <= 2;; $loop_last++) {\
  $timestamp = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_from","null");;\
  $value = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_cost","null");;\
\
  if ( $value ne "null" ) {\
    # Eintragen der Kosten für die Stunde\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Eintragen des Verbrauchs für die Stunde\
    $value = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_consumption","null");;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  } else {\
    $tmp = "null";;\
  }\
} # end for\
if ($tmp eq "null") {\
  $timestamp = $tmp\
}\
$timestamp;;\
},\
\
nodes_TIMESTAMP:nodes_24_00_cost.* {\
my ($timestamp,$value) = 2x0;;\
my $tmp = 0;;\
\
for (my $loop_last = 0;; $loop_last <= 23;; $loop_last++) {\
  $timestamp = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_from","null");;\
  $value = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_cost","null");;\
\
  if ( $value ne "null" ) {\
    # Eintragen der Kosten für die Stunde\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Eintragen des Verbrauchs für die Stunde\
    $value = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_consumption","null");;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  } else {\
    $tmp = "null";;\
  }\
} # end for\
if ($tmp eq "null") {\
  $timestamp = $tmp\
}\
$timestamp;;\
},\
\
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\
my ($timestamp,$value) = 2x0;;\
\
for (my $loop_last = 1;; $loop_last <= 300;; $loop_last += 3) {\
\
  $timestamp =  ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last+2),"null");; # timestamp\
  $timestamp =~ s/T/ /g ;;\
  $timestamp =  substr($timestamp,0,19);;\
\
  # cost aus tibber-Api ohne MwSt\
  $value = ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last+1),"null");;\
\
  if ( $value ne "" ) {\
\
    # Berechnung der Kosten inkl. 19% MwSt für die Stunde\
    $value = round($value*1.19,4);;\
# print $timestamp." ".$value."\n";;\
\
    # Schreiben der Kosten für die Stunde in die Datenbank\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Schreiben des Verbrauchs für die Stunde in die Datenbank\
    $value = ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last),"null");;  # consumption\
    $value = round($value,4);;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  }\
} # end for\
\
$timestamp;;\
},\
\
nodes_cost_avg:nodes_TIMESTAMP.* {\
## Berechnung des Tages Wertes\
  if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
  } else {\
    ReadingsVal("$NAME","nodes_cost_avg","null")\
  }\
},\
\
nodes_cost_min:nodes_TIMESTAMP.* {\
##  Ermittlung des minimal Wertes\
if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
  ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
} else {\
  ReadingsVal("$NAME","nodes_cost_min","null")\
}\
},\
\
nodes_cost_max:nodes_TIMESTAMP.* {\
## Ermittlung des maximal Wertes\
  if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
  } else {\
    ReadingsVal("$NAME","nodes_cost_max","null")\
  }\
},\
\
nodes_consumption_day:nodes_TIMESTAMP.* {\
## Berechnung des Tages Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND date(TIMESTAMP) = curdate() ;;") ;;\
},\
\
nodes_consumption_month:nodes_TIMESTAMP.* {\
## Berechnung des Monats Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND YEAR(TIMESTAMP) = YEAR(curdate())\
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;") ;;\
},\
\
nodes_consumption_year:nodes_TIMESTAMP.* {\
## Berechnung des Jahres Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;") ;;\
},\
\
current_level:fc._0000_total.* {\
  ## Aktueller Preis\
  my $hash = $defs{$name};;\
\
   # --- Readings setzen ---\
  readingsBeginUpdate($hash);;\
  readingsBulkUpdate($hash,"current_price",ReadingsVal("$NAME",sprintf("fc0_%02d%02d_total",strftime("%H",localtime),int(strftime("%M",localtime)  / 15) * 15),0) );;\
  readingsBulkUpdate($hash,"current_date",sprintf("%s:%02d:00",strftime("%Y-%m-%d %H",localtime),int(strftime("%M",localtime)  / 15) * 15) );;\
  if (AttrVal("$NAME","verbose",0) >=3) {\
    Log 3, "$NAME  ur_2  : Daten für current Werte gesetzt";;\
  }\
  readingsBulkUpdate($hash,"fc0_0000_startsAt",strftime("%Y-%m-%d 00:00:00",localtime) );;\
  if (AttrVal("$NAME","verbose",0) >=3) {\
    Log 3, "$NAME  ur_2  : Daten für fc0 Startzeit gesetzt";;\
  }\
  if (ReadingsVal("$NAME","fc1_0000_total","null") ne "null") {       ## Der nächste Tag ist bereits da\
    readingsBulkUpdate($hash,"fc1_0000_startsAt",strftime("%Y-%m-%d 00:00:00",localtime(time+86400)) );;\
    if (AttrVal("$NAME","verbose",0) >=3) {\
      Log 3, "$NAME  ur_2  : Daten für fc1 Startzeit gesetzt";;\
    }\
  } else {\
    if (AttrVal("$NAME","verbose",0) >=3) {\
      Log 3, "$NAME  ur_2  : Daten für fc1 sind noch nicht da";;\
    }\
    fhem("deletereading ".$NAME." fc1_0000_startsAt");;\
  } # end if\
  return "null";;\
},\
\
fc_avg:current_price.* {\
## Berechnung des durchschnitt Wertes über alle Werte von fc0 und fc1\
  my $fc = 0;; # fc0_ = heute, passt sich an wenn es über Mitternacht geht\
  my $fc_avg = 0;;\
\
  if (ReadingsVal("$NAME","fc1_0000_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    if (AttrVal("$NAME","verbose",0) >=3) {\
      Log 3, "$NAME cmd_1  : Tibber Daten für fc1 sind bereits da";;\
    }\
    $fc  = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc ;;$j++){ # heute und eventuell morgen durchlaufen\
      for (my $k=0;;$k<=23;;$k++) {                                          ## Summe  berechnen\
        for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
          $fc_avg += ReadingsVal("$NAME",sprintf("fc%d_%02d%02d_total",$j, $k, $quarter*15),0);;\
        } # end $quarter\
      } # end $k\
    } # end $j\
\
    return round($fc_avg / (96 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\
},\
\
fc_med:current_price.* {\
## Berechnung des median Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT\
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), ',', floor(1+((count(VALUE)-1) / 2)))  , ',', -1))\
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), ',', ceiling(1+((count(VALUE)-1) / 2))), ',', -1))\
                     )/2 *100\
             AS DECIMAL(4,2))\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND (   READING='fc0_total' AND TIMESTAMP >= DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')\
                     OR READING='fc1_total' AND TIMESTAMP >= DATE_FORMAT(NOW() + INTERVAL 1 DAY, '%Y-%m-%d 00:00:00') ) ;;") ;;\
},\
\
fc_min:current_price.* {\
##  Ermittlung des minimal Wertes\
  my $fc = 0;; # fc0_ = heute, passt sich an wenn es über Mitternacht geht\
  my $fc_min = 0;;\
  my $fc_tmp = 0;;\
\
  if (ReadingsVal("$NAME","fc1_0000_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    $fc = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc;;$j++){\
      for (my $k=0;;$k<=23;;$k++) {\
        for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
          $fc_tmp = ReadingsVal("$NAME",sprintf("fc%d_%02d%02d_total",$j,$k, $quarter*15),0);;\
          if (($fc_tmp < $fc_min) or ($j == 0 and $k == 0 and $quarter == 0)) {\
            $fc_min = $fc_tmp;;\
          }\
        } # end $quarter\
      } # end $k\
    } # end $j\
\
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\
},\
\
fc_max:current_price.* {\
##  Ermittlung des minimal Wertes\
  my $fc = 0;; # fc0_ = heute, passt sich an wenn es über Mitternacht geht\
  my $fc_max = 0;;\
  my $fc_tmp = 0;;\
\
  if (ReadingsVal("$NAME","fc1_0000_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    $fc = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc;;$j++){\
      for (my $k=0;;$k<=23;;$k++) {\
        for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
          $fc_tmp = ReadingsVal("$NAME",sprintf("fc%d_%02d%02d_total",$j,$k, $quarter*15),0);;\
          if (($fc_tmp > $fc_max) or ($j == 0 and $k == 0 and $quarter == 0)) {\
            $fc_max = $fc_tmp;;\
          }\
        } # end $quarter\
      } # end $k\
    } # end $j\
\
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\
},\
\
fc_trigger_price:fc_avg.* {\
## fc_trigger_price:[fc_avg|compensation_grid].* {\
  my $fc_avg = ReadingsVal("$NAME","fc_avg",0);;\
  my $fc_min = ReadingsVal("$NAME","fc_min",0);;\
\
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\
  \
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\
  if ( ReadingsVal("$NAME","compensation_grid",0) != 0 ) {\
    my $price_level_battery = round( ($fc_avg - ReadingsVal("$NAME","compensation_grid",0)) *0.85 , 1) ;;\
    if ( $price_level > $price_level_battery ) {\
      $price_level = $price_level_battery;;\
    }\
  }\
 return($price_level);;\
},\
\
fc0_trigger_start:fc_trigger_price.* {\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_total = 0;;\
\
    for (my $loop_hour = $hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc0_%02d%02d_total",$loop_hour , $quarter*15),0);;\
# print sprintf("fc0 Start %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_price."\n";;\
        if ( $fc_total < $fc_trigger_price ) {\
          return(sprintf("%02d:%02d",$loop_hour ,$quarter*15)) ;;\
        }\
      } # end $quarter\
    } # end  for loop_hour\
\
  return("null");;\
},\
\
fc0_trigger_stop:fc0_trigger_start.* {\
  my $fc = 0;;\
  my $loop_hour = 0;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_start = ReadingsVal("$NAME","fc0_trigger_start","null");;\
  my $fc_trigger_stop = $fc_trigger_start;;\
\
  my ($fc_trigger_start_hour, $fc_trigger_start_quarter);;\
\
  if ( $fc_trigger_start ne "null" ) {\
    $fc_trigger_start =~ /(\d\d):(\d\d)/;;\
    $fc_trigger_start_hour = $1;;\
    $fc_trigger_start_quarter = $2 / 15;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_start_hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = $fc_trigger_start_quarter;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc0_%02d%02d_total",$loop_hour , $quarter*15),0);;\
# print sprintf("fc0 Stop  %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_price."\n";;\
        if ( $fc_total < $fc_trigger_price ) {\
          $fc_trigger_stop = sprintf("%02d:%02d",$loop_hour, $quarter*15) ;;\
        } else {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15));;\
        }\
\
      # wenn es 23:45 is wechsel zum nächsten Tag\
       if ($loop_hour == 23 and $fc == 0 and $quarter == 3) {\
         if (ReadingsVal("$NAME","fc1_0000_total","null") ne "null") {   # Der nächste Tag ist bereits da\
           $fc = 1;\
           $loop_hour = -1;                                                                # beginne wieder bei 00:00\
         } else {\
           $fc_trigger_stop = "23:59";                                                 # fc0 stop ist um 00:00\
         }\
       }\
\
       if ( $quarter == 3 ) { $fc_trigger_start_quarter = 0 }\
\
      } # end $quarter\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_stop);;\
},\
\
fc0_trigger:fc0_trigger_stop.* {\
  # Setzen des Triggers für die aktuelle viertel Stunde\
  if ( ReadingsVal("$NAME","current_price",100)  < ReadingsVal("$NAME","fc_trigger_price",0) ) {\
    return("on")\
  } else {\
    return("off")\
  }\
},\
\
fc1_trigger_start:fc_trigger_price.* {\
  if (ReadingsVal("$NAME","fc1_0000_startsAt","null") ne "null") {\
    my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
    # Ermitteln des nächsten Trigger Fensters\
    my $fc_total = 0;;\
\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc1_%02d%02d_total",$loop_hour , $quarter*15),0);;\
# print sprintf("fc1 Start %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_price."\n";;\
        if ( $fc_total < $fc_trigger_price ) {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15)) ;;\
        }\
      } # end $quarter\
    } # end  for loop_hour\
  }\
  return("null");;\
},\
\
fc1_trigger_stop:fc0_trigger_start.* {\
  # Ermitteln des nächsten Trigger Fensters\
  my $loop_hour;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  my $fc_trigger_start = ReadingsVal("$NAME","fc1_trigger_start","null");;\
  my $fc_trigger_stop = $fc_trigger_start;;\
\
  my ($fc_trigger_start_hour, $fc_trigger_start_quarter);;\
\
  if ( $fc_trigger_start ne "null" ) {\
    $fc_trigger_start =~ /(\d\d):(\d\d)/;;\
    $fc_trigger_start_hour = $1;;\
    $fc_trigger_start_quarter = $2 / 15;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_start_hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = $fc_trigger_start_quarter;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc1_%02d%02d_total", $loop_hour, $quarter*15),0);;\
# print sprintf("fc1 Stop  %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_price."\n";;\
        if ( $fc_total < $fc_trigger_price ) {\
          $fc_trigger_stop = sprintf("%02d:%02d",$loop_hour, $quarter*15) ;;\
        } else {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15));;\
        }\
\
       if ( $quarter == 3 ) { $fc_trigger_start_quarter = 0 }\
\
      } # end $quarter\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_stop);;\
},\
\
fc_trigger_max:fc_avg.* {\
  my $fc_avg = ReadingsVal("$NAME","fc_avg",0);;\
  my $fc_max = ReadingsVal("$NAME","fc_max",0);;\
\
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\
  \
$price_level;;\
},\
\
fc0_trigger_max_start:fc_trigger_max.* {\
  # Ermitteln des nächsten Trigger_max Fensters\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
  my $fc_total = 0;;\
\
    for (my $loop_hour = $hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc0_%02d%02d_total",$loop_hour , $quarter*15),0);;\
# print sprintf("fc0 Start max %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_max."\n";;\
        if ( $fc_total > $fc_trigger_max ) {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15)) ;;\
        }\
      } # end $quarter\
    } # end  for loop_hour\
\
  return("null");;\
},\
\
fc0_trigger_max_stop:fc0_trigger_max_start.* {\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc = 0;;\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
  my $fc_trigger_max_start = ReadingsVal("$NAME","fc0_trigger_max_start","null");;\
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\
\
  my ($fc_trigger_max_start_hour, $fc_trigger_max_start_quarter);;\
\
  if ( $fc_trigger_max_start ne "null" ) {\
    $fc_trigger_max_start =~ /(\d\d):(\d\d)/;;\
    $fc_trigger_max_start_hour = $1;;\
    $fc_trigger_max_start_quarter = $2 / 15;;\
    my $fc_total = 0;;\
\
    for (my $loop_hour = $fc_trigger_max_start_hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = $fc_trigger_max_start_quarter;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc%d_%02d%02d_total", $fc, $loop_hour, $quarter*15),0);;\
# print sprintf("fc0 Stop  max %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_max."\n";;\
        if ( $fc_total > $fc_trigger_max ) {\
          $fc_trigger_max_stop = sprintf("%02d:%02d",$loop_hour, $quarter*15) ;;\
        } else {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15));;\
        }\
\
       if ( $quarter == 3 ) { $fc_trigger_max_start_quarter = 0 }\
\
      } # end $quarter\
\
     # wechsel zum nächsten Tag\
     if ( $loop_hour == 23 and $fc == 0 ) {\
       $fc = 1;;\
       $loop_hour = -1;;\
     }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_max_stop);;\
},\
\
fc0_trigger_max:fc0_trigger_max_stop.* {\
\
  # Setzen des maximum Triggers für die aktuelle Stunde\
  if ( ReadingsVal("$NAME","current_price",0)  > ReadingsVal("$NAME","fc_trigger_max",0) ) {\
    return("on")\
  } else {\
    return("off")\
  }\
},\
\
fc1_trigger_max_start:fc_trigger_max.* {\
  if (ReadingsVal("$NAME","fc1_0000_startsAt","null") ne "null") {\
    my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
    # Ermitteln des nächsten Trigger Fensters\
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
    my $fc_total = 0;;\
\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc1_%02d%02d_total",$loop_hour , $quarter*15),0);;\
# print sprintf("fc1 Start max %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_max."\n";;\
        if ( $fc_total > $fc_trigger_max ) {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15)) ;;\
        }\
      } # end $quarter\
    } # end  for loop_hour\
  }\
  return("null");;\
},\
\
fc1_trigger_max_stop:fc1_trigger_max_start.* {\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
  my $fc_trigger_max_start = ReadingsVal("$NAME","fc1_trigger_max_start","null");;\
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\
\
  my ($fc_trigger_max_start_hour, $fc_trigger_max_start_quarter);;\
\
  if ( $fc_trigger_max_start ne "null" ) {\
    $fc_trigger_max_start =~ /(\d\d):(\d\d)/;;\
    $fc_trigger_max_start_hour = $1;;\
    $fc_trigger_max_start_quarter = $2 / 15;;\
    my $fc_total = 0;;\
\
    for (my $loop_hour = $fc_trigger_max_start_hour;; $loop_hour <= 23;; $loop_hour++) {\
      for (my $quarter = $fc_trigger_max_start_quarter;; $quarter <= 3;; $quarter++) {\
        $fc_total = ReadingsVal("$NAME",sprintf("fc1_%02d%02d_total", $loop_hour, $quarter*15),0);;\
# print sprintf("fc1 Stop  max %02d:%02d",$loop_hour, $quarter*15)." ".$fc_total." ".$fc_trigger_max."\n";;\
        if ( $fc_total > $fc_trigger_max ) {\
          $fc_trigger_max_stop = sprintf("%02d:%02d",$loop_hour, $quarter*15) ;;\
        } else {\
          return(sprintf("%02d:%02d",$loop_hour, $quarter*15));;\
        }\
\
       if ( $quarter == 3 ) { $fc_trigger_max_start_quarter = 0 }\
\
      } # end $quarter\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_max_stop);;\
},\
\
total_cost_day:nodes_TIMESTAMP.* {\
## Berechnung des Tages Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND date(TIMESTAMP) = curdate() ;;") ;;\
},\
\
total_cost_month:nodes_TIMESTAMP.* {\
## Berechnung des monats Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND YEAR(TIMESTAMP) = YEAR(curdate())\
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;") ;;\
},\
\
total_cost_year:nodes_TIMESTAMP.* {\
## Berechnung des Jahres Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;") ;;\
},\
\
fc_DbLog:fc0_0000_total.* {\
# Schreibt die redings fc*_hhmm_total als fc*_total mit passendem TIMESTAMP in die Datenbank\
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\
\
for (my $loop_fc = 0;; $loop_fc <= 1;; $loop_fc++) {\
  $loop_fc_next = $loop_fc +1;;\
  $date = ReadingsVal("$NAME","fc".$loop_fc."_0000_startsAt","null");;\
  if ($date ne "null" and $date ne "") {\
    $date =~ /([\d+-]+)/;; $date = $1 ;;\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      $hour = sprintf("%02d",$loop_hour);;\
      for (my $quarter = 0;; $quarter <= 3;; $quarter++) {\
        $timestamp = $date." ".$hour.":".($quarter*15).":00";;\
        $value = ReadingsVal("$NAME","fc".$loop_fc."_".$hour.sprintf("%02d",$quarter*15)."_total","null");;\
        ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
                          INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                            VALUES('".$timestamp."','$NAME','Tibber','fc".$loop_fc."_total','".$value."')\
                          ON DUPLICATE KEY UPDATE\
                            VALUE='".$value."';;") ;;\
      }\
    }\
##    if (ReadingsVal("$NAME","fc".$loop_fc."_0000_startsAt","null") eq ReadingsVal("$NAME","fc".$loop_fc_next."_0000_startsAt","null")) {\
##      fhem("deletereading $NAME fc".$loop_fc_next."_.*");;\
##    }\
  }\
## else {\
##      fhem("deletereading $NAME fc1_\\d.*");;\
##  }\
}\
ReadingsTimestamp("$NAME","fc0_0000_startsAt","null");;\
},\
\
next1_quarter:current_price.* {\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $quarter = int($min / 15);; # aktuelle Viertelstunde 0–3\
  my $next_quarter = ($quarter + 1) % 4;;  # nächste Viertelstunde\
  my $hour_offset = $hour;; # passen sich an wenn es über Mitternacht geht\
  my $fc = 0;; # fc0_ = # heute passen sich an wenn es über Mitternacht geht\
\
  if ($quarter == 3) {  # wenn wir in 23:45 sind nächste Viertelstunde gehört zur nächsten Stunde \
        $hour_offset++;;\
        if ($hour_offset >= 24) { \
            $hour_offset -= 24;;\
            $fc = 1;; # fc1_ = morgen\
        }\
    }\
\
  return round(ReadingsVal($NAME,sprintf("fc%d_%02d%02d_total",$fc, $hour_offset, $next_quarter*15),0)*100,1);;\
 },\
\
next1_hour:current_price.* {\
  my $j;;\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $quarter = int($min / 15);; # aktuelle Viertelstunde 0–3\
  my $next_quarter = ($quarter + 1) % 4;;  # nächste Viertelstunde\
  my $hour_offset = $hour;; # passt sich an wenn es über Mitternacht geht\
  my $fc = 0;; # fc0_ = # heute, passt sich an wenn es über Mitternacht geht\
\
  if ($hour_offset == 23) {  # wenn wir bei 23:00 sind gehört die nächste Stunde zum nächsten Tag\
     $hour_offset = 0;;\
     $fc = 1;; # fc1_ = morgen\
    }\
    for ($quarter = 0;; $quarter <= 3;; $quarter++) {\
      $j += ReadingsVal("$NAME",sprintf("fc%d_%02d%02d_total",$fc, $hour_offset, $quarter*15),0);;\
    }\
\
  return round($j/4*100,1);; # Für die nächste Stunde wird der Durchschnitt ausgegeben\
 }
attr EVU_Tibber_connect verbose 0
attr EVU_Tibber_connect ws_minInterval 54
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions