diff --git a/fhem/CHANGED b/fhem/CHANGED
index 8c3f2ebf0..ceb87a2f5 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - new: 98_vitoconnect: initial release
- feature: 14_CUL_TCM97001: Add Ventus W155, bugfixing
- feature: 89_FULLY: Support for HTTPS added
- change: DOIFtools: add DOIF's weekday 9 to next timer
diff --git a/fhem/FHEM/98_vitoconnect.pm b/fhem/FHEM/98_vitoconnect.pm
new file mode 100644
index 000000000..c068825f5
--- /dev/null
+++ b/fhem/FHEM/98_vitoconnect.pm
@@ -0,0 +1,604 @@
+#########################################################################
+# $Id$
+# fhem Modul für Vissmann API. Based on investigation of "thetrueavatar"
+# (https://github.com/thetrueavatar/Viessmann-Api)
+#
+# This file is part of fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+##############################################################################
+# Changelog:
+#
+# 2018-11-24 initial version
+# 2018-12-11 non-blocking
+# Reading "status" in "state" umbenannt
+# 2018-12-23 Neue Werte in der API werden unter ihrem JSON Name als Reading eingetragen
+# Neue Readings:
+# heating.boiler.sensors.temperature.commonSupply.status error
+# heating.boiler.temperature.value 48.1
+# heating.burner.modulation.value 11
+# heating.burner.statistics.hours 933.336666666667
+# heating.burner.statistics.starts 2717
+# heating.circuits.0.circulation.pump.status on
+# heating.dhw.charging.active 0
+# heating.dhw.pumps.circulation.schedule.active 1
+# heating.dhw.pumps.circulation.schedule.entries sun mode:on end:22:30 start:04:30 position:0, fri end:22:30 mode:on position:0 start:04:30,
+# mon mode:on end:22:30 start:04:30 position:0,
+# wed start:04:30 position:0 end:22:30 mode:on, thu mode:on end:22:30 position:0 start:04:30, sat end:22:30 mode:on position:0 start:04:30,
+# tue position:0 start:04:30 end:22:30 mode:on,
+# heating.dhw.pumps.circulation.status on
+# heating.dhw.pumps.primary.status off
+# heating.dhw.sensors.temperature.outlet.status error
+# heating.dhw.temperature.main.value 53
+# 2018-12-30 initial offical release
+# remove special characters from readings
+# some internal improvements suggested by CoolTux
+#
+#
+# ToDo:
+# Passwort im KeyValue speichern statt im Klartext
+# "set"s zum Steuern der Heizung
+# Dokumentation (auch auf Deutsch)
+# Nicht bei jedem Lesen neu einloggen
+# Fehlerbehandlung verbessern
+# Attribute implementieren und dokumentieren (disable, ....)
+# "sinnvolle" Readings statt 1:1 aus der API übernommene
+# ErrorListChanges implementieren
+# mapping der Readings optional machen
+#
+
+
+
+
+package main;
+use strict;
+use warnings;
+use Time::HiRes qw(gettimeofday);
+use JSON;
+use HttpUtils;
+#use Data::Dumper;
+
+my $client_id = '79742319e39245de5f91d15ff4cac2a8';
+my $client_secret = '8ad97aceb92c5892e102b093c7c083fa';
+my $authorizeURL = 'https://iam.viessmann.com/idp/v1/authorize';
+my $token_url = 'https://iam.viessmann.com/idp/v1/token';
+my $apiURLBase = 'https://api.viessmann-platform.io';
+my $general = '/general-management/installations?expanded=true&';
+my $callback_uri = "vicare://oauth-callback/everest";
+
+my %RequestList = (
+ "heating.boiler.serial.value" => "Kessel_Seriennummer",
+ "heating.boiler.temperature.value" => "Kesseltemperatur_exact",
+ "heating.boiler.sensors.temperature.commonSupply.status" => "Kessel_Common_Supply",
+ "heating.boiler.sensors.temperature.main.status" => "Kessel_Status",
+ "heating.boiler.sensors.temperature.main.value" => "Kesseltemperatur",
+ "heating.burner.active" => "Brenner_aktiv",
+ "heating.burner.automatic.status" => "Brenner_Status",
+ "heating.burner.automatic.errorCode" => "Brenner_Fehlercode",
+ "heating.burner.current.power.value" => "Brenner_Leistung",
+ "heating.burner.modulation.value" => "Brenner_Modulation",
+ "heating.burner.statistics.hours" => "Brenner_Beriebsstunden",
+ "heating.burner.statistics.starts" => "Brenner_Starts",
+ "heating.circuits.enabled" => "Aktive_Heizkreise",
+ "heating.circuits.0.active" => "HK1-aktiv",
+ "heating.circuits.0.circulation.pump.status" => "HK1-Zirkulationspumpe",
+ "heating.circuits.0.circulation.schedule.active" => "HK1-Zeitsteuerung_Zirkulation_aktiv",
+ "heating.circuits.0.circulation.schedule.entries" => "HK1-Zeitsteuerung_Zirkulation",
+ "heating.circuits.0.frostprotection.status" => "HK1-Frostschutz_Status",
+ "heating.circuits.0.heating.curve.shift" => "HK1-Heizkurve-Niveau",
+ "heating.circuits.0.heating.curve.slope" => "HK1-Heizkurve-Steigung",
+ "heating.circuits.0.heating.schedule.active" => "HK1-Zeitsteuerung_Heizung_aktiv",
+ "heating.circuits.0.heating.schedule.entries" => "HK1-Zeitsteuerung_Heizung",
+ "heating.circuits.0.operating.modes.active.value" => "HK1-Betriebsart",
+ "heating.circuits.0.operating.modes.dhw.active" => "HK1-WW_aktiv",
+ "heating.circuits.0.operating.modes.dhwAndHeating.active" => "HK1-WW_und_Heizen_aktiv",
+ "heating.circuits.0.operating.modes.forcedNormal.active" => "HK1-Solltemperatur_erzwungen",
+ "heating.circuits.0.operating.modes.forcedReduced.active" => "HK1-Reduzierte_Temperatur_erzwungen",
+ "heating.circuits.0.operating.modes.standby.active" => "HK1-Standby_aktiv",
+ "heating.circuits.0.operating.programs.active.value" => "HK1-Programmstatus",
+ "heating.circuits.0.operating.programs.comfort.active" => "HK1-Solltemperatur_comfort_aktiv",
+ "heating.circuits.0.operating.programs.comfort.temperature" => "HK1-Solltemperatur_comfort",
+ "heating.circuits.0.operating.programs.eco.active" => "HK1-Solltemperatur_eco_aktiv",
+ "heating.circuits.0.operating.programs.eco.temperature" => "HK1-Solltemperatur_eco",
+ "heating.circuits.0.operating.programs.external.active" => "HK1-External_aktiv",
+ "heating.circuits.0.operating.programs.external.temperature" => "HK1-External_Temperatur",
+ "heating.circuits.0.operating.programs.holiday.active" => "HK1-Urlaub_aktiv",
+ "heating.circuits.0.operating.programs.holiday.start" => "HK1-Urlaub_Start",
+ "heating.circuits.0.operating.programs.holiday.end" => "HK1-Urlaub_Ende",
+ "heating.circuits.0.operating.programs.normal.active" => "HK1-Solltemperatur_aktiv",
+ "heating.circuits.0.operating.programs.normal.temperature" => "HK1-Solltemperatur_normal",
+ "heating.circuits.0.operating.programs.reduced.active" => "HK1-Solltemperatur_reduziert_aktiv",
+ "heating.circuits.0.operating.programs.reduced.temperature" => "HK1-Solltemperatur_reduziert",
+ "heating.circuits.0.operating.programs.standby.active" => "HK1-Standby_aktiv",
+ "heating.circuits.0.sensors.temperature.room.status" => "HK1-Raum_Status",
+ "heating.circuits.0.sensors.temperature.supply.status" => "HK1-Vorlauftemperatur_aktiv",
+ "heating.circuits.0.sensors.temperature.supply.value" => "HK1-Vorlauftemperatur",
+ "heating.circuits.1.active" => "HK2-aktiv",
+ "heating.circuits.1.circulation.pump.status" => "HK2-Zirkulationspumpe",
+ "heating.circuits.1.circulation.schedule.active" => "HK2-Zeitsteuerung_Zirkulation_aktiv",
+ "heating.circuits.1.circulation.schedule.entries" => "HK2-Zeitsteuerung_Zirkulation",
+ "heating.circuits.1.frostprotection.status" => "HK2-Frostschutz_Status",
+ "heating.circuits.1.heating.curve.shift" => "HK2-Heizkurve-Niveau",
+ "heating.circuits.1.heating.curve.slope" => "HK2-Heizkurve-Steigung",
+ "heating.circuits.1.heating.schedule.active" => "HK2-Zeitsteuerung_Heizung_aktiv",
+ "heating.circuits.1.heating.schedule.entries" => "HK2-Zeitsteuerung_Heizung",
+ "heating.circuits.1.operating.modes.active.value" => "HK2-Betriebsart",
+ "heating.circuits.1.operating.modes.dhw.active" => "HK2-WW_aktiv",
+ "heating.circuits.1.operating.modes.dhwAndHeating.active" => "HK2-WW_und_Heizen_aktiv",
+ "heating.circuits.1.operating.modes.forcedNormal.active" => "HK2-Solltemperatur_erzwungen",
+ "heating.circuits.1.operating.modes.forcedReduced.active" => "HK2-Reduzierte_Temperatur_erzwungen",
+ "heating.circuits.1.operating.modes.standby.active" => "HK2-Standby_aktiv",
+ "heating.circuits.1.operating.programs.active.value" => "HK2-Programmstatus",
+ "heating.circuits.1.operating.programs.comfort.active" => "HK2-Solltemperatur_comfort_aktiv",
+ "heating.circuits.1.operating.programs.comfort.temperature" => "HK2-Solltemperatur_comfort",
+ "heating.circuits.1.operating.programs.eco.active" => "HK2-Solltemperatur_eco_aktiv",
+ "heating.circuits.1.operating.programs.eco.temperature" => "HK2-Solltemperatur_eco",
+ "heating.circuits.1.operating.programs.external.active" => "HK2-External_aktiv",
+ "heating.circuits.1.operating.programs.external.temperature" => "HK2-External_Temperatur",
+ "heating.circuits.1.operating.programs.holiday.active" => "HK2-Urlaub_aktiv",
+ "heating.circuits.1.operating.programs.holiday.start" => "HK2-Urlaub_Start",
+ "heating.circuits.1.operating.programs.holiday.end" => "HK2-Urlaub_Ende",
+ "heating.circuits.1.operating.programs.normal.active" => "HK2-Solltemperatur_aktiv",
+ "heating.circuits.1.operating.programs.normal.temperature" => "HK2-Solltemperatur_normal",
+ "heating.circuits.1.operating.programs.reduced.active" => "HK2-Solltemperatur_reduziert_aktiv",
+ "heating.circuits.1.operating.programs.reduced.temperature" => "HK2-Solltemperatur_reduziert",
+ "heating.circuits.1.operating.programs.standby.active" => "HK2-Standby_aktiv",
+ "heating.circuits.1.sensors.temperature.room.status" => "HK2-Raum_Status",
+ "heating.circuits.1.sensors.temperature.supply.status" => "HK2-Vorlauftemperatur_aktiv",
+ "heating.circuits.1.sensors.temperature.supply.value" => "HK2-Vorlauftemperatur",
+ "heating.configuration.multiFamilyHouse.active" => "Mehrfamilenhaus_aktiv",
+ "heating.controller.serial.value" => "Controller_Seriennummer",
+ "heating.device.time.offset.value" => "Device_Time_Offset",
+ "heating.dhw.active" => "WW-aktiv",
+ "heating.dhw.charging.active" => "WW-Aufladung",
+ "heating.dhw.oneTimeCharge.active" => "WW-onTimeCharge_aktiv",
+ "heating.dhw.pumps.circulation.schedule.active" => "WW-Zirklationspumpe_Zeitsteuerung_aktiv",
+ "heating.dhw.pumps.circulation.schedule.entries" => "WW-Zirkulationspumpe_Zeitplan",
+ "heating.dhw.pumps.circulation.status" => "WW-Zirkulationspumpe_Status",
+ "heating.dhw.pumps.primary.status" => "WW-Zirkulationspumpe_primaer",
+ "heating.dhw.sensors.temperature.outlet.status" => "WW-Sensoren_Auslauf_Status",
+ "heating.dhw.temperature.main.value" => "WW-Haupttemperatur",
+ "heating.dhw.sensors.temperature.hotWaterStorage.status" => "WW-Temperatur_aktiv",
+ "heating.dhw.sensors.temperature.hotWaterStorage.value" => "WW-Isttemperatur",
+ "heating.dhw.temperature.value" => "WW-Solltemperatur",
+ "heating.dhw.schedule.active" => "WW-zeitgesteuert_aktiv",
+ "heating.dhw.schedule.entries" => "WW-Zeitplan",
+ "heating.errors.active.entries" => "Fehlereintraege_aktive",
+ "heating.errors.history.entries" => "Fehlereintraege_Historie",
+ "heating.gas.consumption.dhw.day" => "Gasverbrauch_WW/Tag",
+ "heating.gas.consumption.dhw.week" => "Gasverbrauch_WW/Woche",
+ "heating.gas.consumption.dhw.month" => "Gasverbrauch_WW/Monat",
+ "heating.gas.consumption.dhw.year" => "Gasverbrauch_WW/Jahr",
+ "heating.gas.consumption.heating.day" => "Gasverbrauch_Heizung/Tag",
+ "heating.gas.consumption.heating.week" => "Gasverbrauch_Heizung/Woche",
+ "heating.gas.consumption.heating.month" => "Gasverbrauch_Heizung/Monat",
+ "heating.gas.consumption.heating.year" => "Gasverbrauch_Heizung/Jahr",
+ "heating.sensors.temperature.outside.status" => "Aussen_Status",
+ "heating.sensors.temperature.outside.statusWired" => "Aussen_StatusWired",
+ "heating.sensors.temperature.outside.statusWireless" => "Aussen_StatusWireless",
+ "heating.sensors.temperature.outside.value" => "Aussentemperatur",
+ "heating.service.timeBased.serviceDue" => "Service_faellig",
+ "heating.service.timeBased.serviceIntervalMonths" => "Service_Intervall_Monate",
+ "heating.service.timeBased.activeMonthSinceLastService" => "Service_Monate_aktiv_seit_letzten_Service",
+ "heating.service.timeBased.lastService" => "Service_Letzter",
+ "heating.service.burnerBased.serviceDue" => "Service_fällig_brennerbasiert",
+ "heating.service.burnerBased.serviceIntervalBurnerHours" => "Service_Intervall_Betriebsstunden",
+ "heating.service.burnerBased.activeBurnerHoursSinceLastService" => "Service_Betriebsstunden_seit_letzten",
+ "heating.service.burnerBased.lastService" => "Service_Letzter_brennerbasiert"
+);
+
+
+sub vitoconnect_Initialize($) {
+ my ($hash) = @_;
+ $hash->{DefFn} = 'vitoconnect_Define';
+ $hash->{UndefFn} = 'vitoconnect_Undef';
+ $hash->{SetFn} = 'vitoconnect_Set';
+ $hash->{GetFn} = 'vitoconnect_Get';
+ $hash->{AttrFn} = 'vitoconnect_Attr';
+ $hash->{ReadFn} = 'vitoconnect_Read';
+ $hash->{AttrList} = "disable:0,1".$readingFnAttributes;
+}
+
+sub vitoconnect_Define($$) {
+ my ($hash, $def) = @_;
+ my $name = $hash->{NAME};
+ my @param = split('[ \t]+', $def);
+
+ if(int(@param) < 5) { return "too few parameters: define vitoconnect "; }
+
+ $hash->{user} = $param[2];
+ $hash->{passwd} = $param[3];
+ $hash->{intervall} = $param[4];
+ $hash->{counter} = 0;
+
+ InternalTimer(gettimeofday()+2, "$name - _GetUpdate", $hash);
+ return undef;
+}
+
+sub vitoconnect_Undef($$) {
+ my ($hash, $arg) = @_;
+ RemoveInternalTimer($hash);
+ return undef;
+}
+
+sub vitoconnect_Get($@) {
+ my ($hash, $name, $opt, @args) = @_;
+
+ return "get $name needs at least one argument" unless (defined($opt));
+ return undef;
+}
+
+sub vitoconnect_Set($@) {
+ my ($hash, $name, $opt, @args) = @_;
+
+ return "set $name needs at least one argument" unless (defined($opt));
+ if ($opt eq "update"){ vitoconnect_GetUpdate($hash); }
+ return undef;
+}
+sub vitoconnect_Attr(@) {
+ my ($cmd,$name,$attr_name,$attr_value) = @_;
+ if($cmd eq "set") {
+ if($attr_name eq "formal") {
+ if($attr_value !~ /^yes|no$/) {
+ my $err = "Invalid argument $attr_value to $attr_name. Must be yes or no.";
+ Log 3, "$name: ".$err;
+ return $err;
+ }
+ } elsif($attr_name eq "disable") {
+
+ } elsif($attr_name eq "verbose") {
+
+ } else {
+ # return "Unknown attr $attr_name";
+ }
+ }
+ return undef;
+}
+
+# Subs
+sub vitoconnect_GetUpdate($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ Log3 $name, 4, "$name: GetUpdate called ...";
+
+ vitoconnect_getCode($hash);
+ return undef;
+}
+
+sub vitoconnect_getCode($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ my $url = "$authorizeURL?client_id=$client_id&scope=openid&redirect_uri=$callback_uri&response_type=code";
+ my @header = ("Content-Type: application/x-www-form-urlencoded");
+ my $isiwebuserid = $hash->{user};
+ my $isiwebpasswd = $hash->{passwd};
+ Log3 $name, 3, "$name - getCode went ok";
+ Log3 $name, 5, "getCode: $url";
+
+ my $param = {
+ url => $url,
+ hash => $hash,
+ header => "Content-Type: application/x-www-form-urlencoded",
+ ignoreredirects => 1,
+ user => $isiwebuserid,
+ pwd => $isiwebpasswd,
+ sslargs => {SSL_verify_mode => 0},
+ method => "POST",
+ callback => \&vitoconnect_getCodeCallback
+ };
+
+ # Log3 $name, 3, Dumper($hash);
+ HttpUtils_NonblockingGet($param);
+ return undef;
+}
+
+sub vitoconnect_getCodeCallback ($) {
+ my ($param, $err, $response_body) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+
+ if ($err eq "") {
+ Log3 $name, 3, "$name - getCodeCallback went ok";
+ Log3 $name, 5, "Received response: $response_body";
+ $response_body =~ /code=(.*)"/;
+ $hash->{code} = $1;
+ Log3 $name, 5, "code = $hash->{code}";
+ if ($hash->{code}) {
+ $hash->{login} = "ok";
+ } else {
+ $hash->{login} = "failure";
+ }
+ } else {
+ # Error code, type of error, error message
+ Log3 $name, 1, "An error happened: $err";
+ $hash->{login} = "failure";
+ }
+ if ($hash->{login} eq "ok") {
+ vitoconnect_getAccessToken($hash);
+ } else {
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "state", "login failure" );
+ readingsEndUpdate($hash, 1);
+ # neuen Timer starten in einem konfigurierten Interval.
+ InternalTimer(gettimeofday()+$hash->{intervall}, "vitoconnect_GetUpdate", $hash);
+ }
+ return undef;
+}
+
+sub vitoconnect_getAccessToken($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $param = {
+ url => $token_url,
+ hash => $hash,
+ header => "Content-Type: application/x-www-form-urlencoded;charset=utf-8",
+ data => "client_id=$client_id&client_secret=$client_secret&code=$hash->{code}&redirect_uri=$callback_uri&grant_type=authorization_code",
+ sslargs => {SSL_verify_mode => 0},
+ method => "POST",
+ callback => \&vitoconnect_getAccessTokenCallback
+ };
+ HttpUtils_NonblockingGet($param);
+ return undef;
+}
+
+sub vitoconnect_getAccessTokenCallback($) {
+ my ($param, $err, $response_body) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+
+ if ($err eq "") {
+ Log3 $name, 3, "$name - getAccessTokenCallback went ok";
+ Log3 $name, 5, "Received response: $response_body\n";
+ my $decode_json = eval{decode_json($response_body)};
+ if($@) {
+ Log3 $name, 1, "$name - JSON error while request: $@";
+ return;
+ }
+ my $access_token = $decode_json->{"access_token"};
+ if ($access_token ne "") {
+ $hash->{access_token} = $access_token;
+ Log3 $name, 5, "Access Token: $access_token";
+ vitoconnect_getGw($hash);
+ } else {
+ Log3 $name, 1, "Access Token: undef";
+ InternalTimer(gettimeofday()+$hash->{intervall}, "vitoconnect_GetUpdate", $hash);
+ }
+ } else {
+ Log3 $name, 1, "getAccessToken: An error happened: $err";
+ InternalTimer(gettimeofday()+$hash->{intervall}, "vitoconnect_GetUpdate", $hash);
+ }
+ return undef;
+}
+
+sub vitoconnect_getGw($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $access_token = $hash->{access_token};
+ my $param = {
+ url => "$apiURLBase$general",
+ hash => $hash,
+ header => "Authorization: Bearer $access_token",
+ sslargs => {SSL_verify_mode => 0},
+ callback => \&vitoconnect_getGwCallback
+ };
+ HttpUtils_NonblockingGet($param);
+ return undef;
+}
+
+sub vitoconnect_getGwCallback($) {
+ my ($param, $err, $response_body) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+
+ if ($err eq "") {
+ Log3 $name, 3, "$name - getGwCallback went ok";
+ Log3 $name, 5, "Received response: $response_body\n";
+ my $decode_json = eval{decode_json($response_body)};
+ if($@) {
+ Log3 $name, 1, "$name - JSON error while request: $@";
+ return;
+ }
+ my $installation = $decode_json->{entities}[0]->{properties}->{id};
+ Log3 $name, 5, "installation: $installation";
+ $hash->{installation} = $installation;
+ $decode_json = eval{decode_json($response_body)};
+ if($@) {
+ Log3 $name, 1, "$name - JSON error while request: $@";
+ return;
+ }
+ my $gw = $decode_json->{entities}[0]->{entities}[0]->{properties}->{serial};
+ Log3 $name, 5, "gw: $gw";
+ $hash->{gw} = $gw;
+ vitoconnect_getResource($hash);
+ } else {
+ Log3 $name, 1, "An error happened: $err";
+ InternalTimer(gettimeofday()+$hash->{intervall}, "vitoconnect_GetUpdate", $hash);
+ }
+ return undef;
+}
+
+sub vitoconnect_getResource($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $access_token = $hash->{access_token};
+ my $installation = $hash->{installation};
+ my $gw = $hash->{gw};
+ my $param = {
+ url => "https://api.viessmann-platform.io/operational-data/installations/$installation/gateways/$gw/devices/0/features/",
+ hash => $hash,
+ header => "Authorization: Bearer $access_token",
+ timeout => 10,
+ sslargs => {SSL_verify_mode => 0},
+ callback => \&vitoconnect_getResourceCallback
+ };
+ HttpUtils_NonblockingGet($param);
+ return undef;
+}
+
+sub vitoconnect_getResourceCallback($) {
+ my ($param, $err, $response_body) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+
+ readingsBeginUpdate($hash);
+ if ($err eq "") {
+ Log3 $name, 3, "$name - getResourceCallback went ok";
+ Log3 $name, 5, "Received response: $response_body\n";
+ my $decode_json = eval{decode_json($response_body)};
+ if($@) {
+ Log3 $name, 1, "$name - JSON error while request: $@";
+ return;
+ }
+ my $items = $decode_json;
+ for my $item( @{$items->{entities}} ) {
+ my $FieldName = $item->{class}[0];
+ Log3 $name, 5, "FieldName $FieldName";
+ my %Properties = %{$item->{properties}};
+ my @Keys = keys( %Properties );
+ for my $Key ( @Keys ) {
+ #readingsBulkUpdate($hash, $FieldName.".".$Key, $RequestList{$FieldName.".".$Key});
+ my $Reading = $RequestList{$FieldName.".".$Key};
+ if ( !defined($Reading) ) { $Reading = $FieldName.".".$Key; }
+ Log3 $name, 5, "Property: $FieldName $Key";
+ my $Type = $Properties{$Key}{type};
+ my $Value = $Properties{$Key}{value};
+ if ( $Type eq "string" ) {
+ readingsBulkUpdate($hash, $Reading, $Value);
+ Log3 $name, 5, "$FieldName".".$Key: $Value ($Type)";
+ } elsif ( $Type eq "number" ) {
+ readingsBulkUpdate($hash, $Reading, $Value);
+ Log3 $name, 5, "$FieldName".".$Key: $Value ($Type)";
+ } elsif ( $Type eq "array" ) {
+ my $Array = join(",", @$Value);
+ readingsBulkUpdate($hash, $Reading, $Array);
+ Log3 $name, 5, "$FieldName".".$Key: $Array ($Type)";
+ } elsif ( $Type eq "boolean" ) {
+ readingsBulkUpdate($hash, $Reading, $Value);
+ Log3 $name, 5, "$FieldName".".$Key: $Value ($Type)";
+ } elsif ( $Type eq "Schedule" ) {
+ my %Entries = %$Value;
+ my @Days = keys (%Entries);
+ my $Result = "";
+ for my $Day ( @Days ){
+ my $Entry = $Entries{$Day};
+ $Result = "$Result $Day";
+ for my $Element ( @$Entry ) {
+ #$Result = "$Result $Element";
+ while(my($k, $v) = each %$Element) {
+ $Result = "$Result $k:$v";
+ }
+ $Result = "$Result, ";
+ }
+ }
+ readingsBulkUpdate($hash, $Reading, $Result);
+ Log3 $name, 5, "$FieldName".".$Key: $Result ($Type)";
+ } elsif ( $Type eq "ErrorListChanges" ) {
+ # not implemented yet
+ readingsBulkUpdate($hash, $Reading, "ErrorListChanges");
+ Log3 $name, 5, "$FieldName".".$Key: $Value ($Type)";
+ } else {
+ readingsBulkUpdate($hash, $Reading, "Unknown: $Type");
+ Log3 $name, 5, "$FieldName".".$Key: $Value ($Type)";
+ }
+ };
+ };
+ readingsBulkUpdate($hash, "counter", $hash->{counter} );
+ $hash->{counter} = $hash->{counter} + 1;
+ readingsBulkUpdate($hash, "state", "ok");
+ } else {
+ # Error code, type of error, error message
+ readingsBulkUpdate($hash, "state", "An error happened: $err");
+ Log3 $name, 1, "An error happened: $err";
+ }
+ readingsEndUpdate($hash, 1);
+ # neuen Timer starten in einem konfigurierten Interval.
+ InternalTimer(gettimeofday()+$hash->{intervall}, "vitoconnect_GetUpdate", $hash);
+ return undef;
+}
+
+1;
+
+=pod
+=item device
+=item summary support for Vissmann API
+=item summary_DE Unterstützung für die Vissmann API
+=begin html
+
+
+vitoconnect
+
+ vitoconnect implements a device for Vissmann API Vitoconnect100.
+ Based on investigation of thetrueavatar
+
+ You need the user and password from the ViCare App account.
+
+ For details see: FHEM Wiki (german)
+
+
+
+ Define
+
+ define <name> vitoconnect <user> <password> <interval>
+
+ Example: define vitoconnect vitoconnect user@mail.xx geheim 60
+
+
+
+
+
+
+ Set
+
+
+
+
+ Get
+
+
+
+
+ Attributes
+
+ attr <name> <attribute> <value>
+
+ See commandref#attr for more info about
+ the attr command.
+
+ Attributes:
+
+ - not implemented yet
+ You can use al lot of standard attributes like verbose, userReadings, DBLogInclude ....
+
+
+
+
+
+ Readings
+
+ vitoconnect sets one reading for every value delivered by the API (depends on the type and the settings of your heater and the version of the API!).
+ Already known values will be mapped to clear names. Unknown values will added with their JSON path (e.g. "heating.burner.modulation.value").
+ Please report new readings to the module maintainer. A description of the known reading could be found here (german)
+
+
+
+=end html
+
+=cut
+=cut
diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index 50f75fb73..1fd972773 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -504,6 +504,7 @@ FHEM/98_MSwitch.pm Byte09 Automatisierung
FHEM/98_telnet.pm rudolfkoenig Automatisierung
FHEM/98_update.pm rudolfkoenig Sonstiges
FHEM/98_version.pm markusbloch Sonstiges
+FHEM/98_vitoconnect.pm andreas13 Heizungssteuerung/Raumklima
FHEM/98_weblink.pm rudolfkoenig Frontends/FHEMWEB
FHME/98_weekprofile.pm risiko Frontends
FHEM/98_STOCKQUOTES.pm vbs Unterstuetzende Dienste