mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-12 13:52:00 +00:00
3298 lines
115 KiB
Perl
3298 lines
115 KiB
Perl
# $Id$
|
|
########################################################################################################################
|
|
#
|
|
# 73_km200.pm
|
|
# Creates the possibility to access the Buderus central heating system via
|
|
# Buderus KM200, KM100 or KM50 communication module. It uses HttpUtils_NonblockingGet
|
|
# from Rudolf Koenig to avoid a full blockage of the fhem main system during the
|
|
# polling procedure.
|
|
#
|
|
# Author : Matthias Deeke
|
|
# Contributions : Olaf Droegehorn, Andreas Hahn, Rudolf Koenig, Markus Bloch,
|
|
# Contributions (continued) : Stefan M., Furban, KaiKr, grossi33, Morkin, DLindner
|
|
# e-mail : matthias.deeke(AT)deeke(PUNKT)eu
|
|
# Fhem Forum : http://forum.fhem.de/index.php/topic,25540.0.html
|
|
# Fhem Wiki : http://www.fhemwiki.de/wiki/Buderus_Web_Gateway
|
|
#
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# fhem.cfg: define <devicename> km200 <IPv4-address> <GatewayPassword> <PrivatePassword>
|
|
#
|
|
# Example 1 - Bare Passwords:
|
|
# define myKm200 km200 192.168.178.200 GatewayGeheim PrivateGeheim
|
|
#
|
|
# Example 2 - base64 encoded passwords: Both passwords may be pre-encode with base64
|
|
# define myKm200 km200 192.168.178.200 R2F0ZXdheUdlaGVpbQ== UHJpdmF0ZUdlaGVpbQ==
|
|
#
|
|
########################################################################################################################
|
|
|
|
########################################################################################################################
|
|
# List of open Problems:
|
|
#
|
|
#
|
|
########################################################################################################################
|
|
|
|
package main;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Blocking;
|
|
use Time::HiRes qw(gettimeofday sleep usleep);
|
|
use Digest::MD5 qw(md5 md5_hex md5_base64);
|
|
use base qw( Exporter );
|
|
use List::MoreUtils qw(first_index);
|
|
use MIME::Base64;
|
|
use LWP::UserAgent;
|
|
use JSON;
|
|
use Crypt::Rijndael;
|
|
use HttpUtils;
|
|
use Encode;
|
|
use constant false => 0;
|
|
use constant true => 1;
|
|
|
|
sub km200_Define($$);
|
|
sub km200_Undefine($$);
|
|
|
|
###START###### Initialize module ##############################################################################START####
|
|
sub km200_Initialize($)
|
|
{
|
|
my ($hash) = @_;
|
|
|
|
$hash->{STATE} = "Init";
|
|
$hash->{DefFn} = "km200_Define";
|
|
$hash->{UndefFn} = "km200_Undefine";
|
|
$hash->{SetFn} = "km200_Set";
|
|
$hash->{GetFn} = "km200_Get";
|
|
$hash->{AttrFn} = "km200_Attr";
|
|
$hash->{DbLog_splitFn} = "km200_DbLog_splitFn";
|
|
|
|
$hash->{AttrList} = "do_not_notify:1,0 " .
|
|
"header " .
|
|
"disable:1,0 " .
|
|
"loglevel:0,1,2,3,4,5,6 " .
|
|
"IntervalDynVal " .
|
|
"PollingTimeout " .
|
|
"DoNotPoll " .
|
|
"ReadBackDelay " .
|
|
$readingFnAttributes;
|
|
}
|
|
####END####### Initialize module ###############################################################################END#####
|
|
|
|
|
|
###START###### Activate module after module has been used via fhem command "define" ##########################START####
|
|
sub km200_Define($$)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my @a = split("[ \t][ \t]*", $def);
|
|
my $name = $a[0];
|
|
#$a[1] just contains the "km200" module name and we already know that! :-)
|
|
my $url = $a[2];
|
|
my $km200_gateway_password = $a[3];
|
|
my $km200_private_password = $a[4];
|
|
|
|
$hash->{NAME} = $name;
|
|
$hash->{STATE} = "define";
|
|
|
|
Log3 $name, 4, $name. " : km200 - Starting to define module";
|
|
|
|
### Stop the current timer if one exists errornous
|
|
RemoveInternalTimer($hash);
|
|
Log3 $name, 4, $name. " : km200 - InternalTimer has been removed.";
|
|
|
|
###START###### Define known services of gateway ###########################################################START####
|
|
my @KM200_AllServices = (
|
|
"/",
|
|
"/dhwCircuits",
|
|
"/gateway",
|
|
"/heatingCircuits",
|
|
"/heatSources",
|
|
"/notifications",
|
|
"/recordings",
|
|
"/solarCircuits",
|
|
"/system",
|
|
"/dhwCircuits",
|
|
);
|
|
####END####### Define known services of gateway ############################################################END#####
|
|
|
|
|
|
###START### Check whether all variables are available #####################################################START####
|
|
if (int(@a) == 5)
|
|
{
|
|
###START### Check whether IPv4 address is valid
|
|
if ($url =~ m/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)
|
|
{
|
|
Log3 $name, 4, $name. " : km200 - IPv4-address is valid : " . $url;
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - IPv4 address is not valid \n Please use \"define <devicename> km200 <IPv4-address> <interval/[s]> <GatewayPassword> <PrivatePassword>\" instead";
|
|
}
|
|
####END#### Check whether IPv4 address is valid
|
|
|
|
|
|
###START### Check whether gateway password is base64 encoded or bare, has the right length and delete "-" if required
|
|
my $PasswordEncrypted = false;
|
|
my $EvalPassWord = $km200_gateway_password;
|
|
$EvalPassWord =~ tr/-//d;
|
|
|
|
if ( length($EvalPassWord) == 16)
|
|
{
|
|
$km200_gateway_password = $EvalPassWord;
|
|
Log3 $name,4, $name. " : km200 - Provided GatewayPassword provided as bareword has the correct length at least.";
|
|
}
|
|
else # Check whether the password is eventually base64 encoded
|
|
{
|
|
# Remove additional encoding with base64
|
|
my $decryptData = decode_base64($km200_gateway_password);
|
|
$decryptData =~ tr/-//d;
|
|
$decryptData =~ s/\r|\n//g;
|
|
if ( length($decryptData) == 16)
|
|
{
|
|
$km200_gateway_password = $decryptData;
|
|
$PasswordEncrypted = true;
|
|
Log3 $name, 4, $name. " : km200 - Provided GatewayPassword encoded with base64 has the correct length at least.";
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - GatewayPassword does not have the correct length.\n".
|
|
" Please enter gateway password in the format of \"aaaabbbbccccdddd\" or \"aaaa-bbbb-cccc-dddd\"\n".
|
|
" You may encode your password with base64 first, in order to prevent bare passwords in fhem.cfg.\n".
|
|
" If you choose to encrypt your gateway password with base64, you also must encrypt your private password the same way\n";
|
|
|
|
Log3 $name, 3, $name. " : km200 - Provided Gateway Password does not follow the specifications";
|
|
}
|
|
}
|
|
####END#### Check whether gateway password has the right length and delete "-" if required
|
|
|
|
###START### Check whether private password is available and decode it with base64 if encoding is used
|
|
if ($PasswordEncrypted == true)
|
|
{
|
|
my $decryptData = decode_base64($km200_private_password);
|
|
$decryptData =~ s/\r|\n//g;
|
|
if (length($decryptData) > 0)
|
|
{
|
|
$km200_private_password = $decryptData;
|
|
Log3 $name, 4, $name. " : km200 - Provided PrivatePassword exists at least";
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - PrivatePassword does not have the minimum length.\n".
|
|
" You may encode your password with base64 first, in order to prevent bare passwords in fhem.cfg.\n".
|
|
" If you choose to encrypt your private password with base64, you also must encrypt your gateway password the same way\n";
|
|
}
|
|
}
|
|
else # If private password is provided as bare word
|
|
{
|
|
if (length($km200_private_password) > 0)
|
|
{
|
|
Log3 $name, 4, $name. " : km200 - Provided PrivatePassword exists at least";
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - PrivatePassword has not been provided.\n".
|
|
" You may encode your password with base64 first, in order to prevent bare passwords in fhem.cfg.\n".
|
|
" If you choose to encrypt your private password with base64, you also must encrypt your gateway password the same way\n";
|
|
Log3 $name, 3, $name. " : km200 - Provided Private Password does not follow the specifications";
|
|
}
|
|
}
|
|
####END#### Check whether private password is available and decode it with base64 if encoding is used
|
|
}
|
|
else
|
|
{
|
|
return $name .": km200 - Error - Not enough parameter provided." . "\n" . "Gateway IPv4 address, Gateway and Private Passwords must be provided" ."\n". "Please use \"define <devicename> km200 <IPv4-address> <GatewayPassword> <PrivatePassword>\" instead";
|
|
}
|
|
####END#### Check whether all variables are available ######################################################END#####
|
|
|
|
|
|
###START###### Create the secret SALT of the MD5-Hash for AES-encoding ####################################START####
|
|
my $Buderus_MD5Salt = pack(
|
|
'C*',
|
|
0x86, 0x78, 0x45, 0xe9, 0x7c, 0x4e, 0x29, 0xdc,
|
|
0xe5, 0x22, 0xb9, 0xa7, 0xd3, 0xa3, 0xe0, 0x7b,
|
|
0x15, 0x2b, 0xff, 0xad, 0xdd, 0xbe, 0xd7, 0xf5,
|
|
0xff, 0xd8, 0x42, 0xe9, 0x89, 0x5a, 0xd1, 0xe4
|
|
);
|
|
####END####### Create the secret SALT of the MD5-Hash for AES-encoding #####################################END#####
|
|
|
|
|
|
###START###### Create keys with MD5 #######################################################################START####
|
|
# Copy Salt
|
|
my $km200_crypt_md5_salt = $Buderus_MD5Salt;
|
|
|
|
# First half of the key: MD5 of (km200GatePassword . Salt)
|
|
my $key_1 = md5($km200_gateway_password . $km200_crypt_md5_salt);
|
|
|
|
# Second half of the key: - Initial: MD5 of ( Salt)
|
|
my $key_2_initial = md5($km200_crypt_md5_salt);
|
|
|
|
# Second half of the key: - private: MD5 of ( Salt . km200PrivatePassword)
|
|
my $key_2_private = md5($km200_crypt_md5_salt . $km200_private_password);
|
|
|
|
# Create keys
|
|
my $km200_crypt_key_initial = ($key_1 . $key_2_initial);
|
|
my $km200_crypt_key_private = ($key_1 . $key_2_private);
|
|
####END####### Create keys with MD5 #########################################################################END#####
|
|
|
|
|
|
###START###### Writing values to global hash ###############################################################START####
|
|
$hash->{NAME} = $name;
|
|
$hash->{URL} = $url;
|
|
$hash->{INTERVALDYNVAL} = 300;
|
|
$hash->{DELAYDYNVAL} = 60;
|
|
$hash->{POLLINGTIMEOUT} = 5;
|
|
$hash->{READBACKDELAY} = 100;
|
|
$hash->{temp}{ServiceCounterInit} = 0;
|
|
$hash->{temp}{ServiceCounterDyn} = 0;
|
|
$hash->{temp}{ServiceDbLogSplitHash} = ();
|
|
$hash->{status}{FlagInitRequest} = false;
|
|
$hash->{status}{FlagGetRequest} = false;
|
|
$hash->{status}{FlagSetRequest} = false;
|
|
$hash->{status}{FlagDynRequest} = false;
|
|
$hash->{status}{FlagStatRequest} = false;
|
|
$hash->{Secret}{CRYPTKEYPRIVATE} = $km200_crypt_key_private;
|
|
$hash->{Secret}{CRYPTKEYINITIAL} = $km200_crypt_key_initial;
|
|
@{$hash->{Secret}{KM200ALLSERVICES}} = sort @KM200_AllServices;
|
|
@{$hash->{Secret}{KM200ALLSERVICESBACKUP}} = sort @KM200_AllServices;
|
|
@{$hash->{Secret}{KM200RESPONDINGSERVICES}} = ();
|
|
@{$hash->{Secret}{KM200WRITEABLESERVICES}} = ();
|
|
@{$hash->{Secret}{KM200DONOTPOLL}} = ();
|
|
####END####### Writing values to global hash ################################################################END#####
|
|
|
|
###START###### Reset fullResponse error message ############################################################START####
|
|
readingsSingleUpdate( $hash, "fullResponse", "OK", 1);
|
|
####END####### Reset fullResponse error message #############################################################END#####
|
|
|
|
###START###### For Debugging purpose only ##################################################################START####
|
|
Log3 $name, 4, $name. " : km200 - Define H : " .$hash;
|
|
Log3 $name, 4, $name. " : km200 - Define D : " .$def;
|
|
Log3 $name, 4, $name. " : km200 - Define A : " .@a;
|
|
Log3 $name, 4, $name. " : km200 - Define Name : " .$name;
|
|
Log3 $name, 4, $name. " : km200 - Define Adr : " .$url;
|
|
####END####### For Debugging purpose only ###################################################################END#####
|
|
|
|
|
|
###START###### Check whether communication to the physical unit is possible ################################START####
|
|
my $Km200Info ="";
|
|
$hash->{temp}{service} = "/gateway/DateTime";
|
|
$Km200Info = km200_GetSingleService($hash);
|
|
|
|
if ($Km200Info eq "ERROR")
|
|
{
|
|
$Km200Info = $hash->{temp}{TransferValue};
|
|
$hash->{temp}{TransferValue} = "";
|
|
|
|
## Communication with Gateway WRONG !! ##
|
|
$hash->{STATE}="Error - No Communication";
|
|
return ($name .": km200 - ERROR - The communication between fhem and the Buderus KM200 failed! \n".
|
|
" Please check physical connection, IP-address and passwords! \n");
|
|
}
|
|
elsif ($Km200Info eq "SERVICE NOT AVAILABLE") ## Communication OK but service not available ##
|
|
{
|
|
Log3 $name, 4, $name. " : km200 - /gateway/DateTime : NOT AVAILABLE";
|
|
}
|
|
else ## Communication OK and service is available ##
|
|
{
|
|
Log3 $name, 4, $name. " : km200 - /gateway/DateTime : AVAILABLE";
|
|
}
|
|
####END####### Check whether communication to the physical unit is possible ################################END#####
|
|
|
|
###START###### Initiate the timer for first time polling of values from KM200 but wait 10s ###############START####
|
|
InternalTimer(gettimeofday()+10, "km200_GetInitService", $hash, 1);
|
|
Log3 $name, 4, $name. " : km200 - Internal timer for Initialisation of services started for the first time.";
|
|
####END####### Initiate the timer for first time polling of values from KM200 but wait 60s ################END#####
|
|
|
|
return undef;
|
|
}
|
|
####END####### Activate module after module has been used via fhem command "define" ############################END#####
|
|
|
|
|
|
###START###### To bind unit of value to DbLog entries #########################################################START####
|
|
sub km200_DbLog_splitFn($$)
|
|
{
|
|
my ($event, $name) = @_;
|
|
my ($reading, $value, $unit);
|
|
my $hash = $defs{$name};
|
|
my @argument = split("[ \t][ \t]*", $event);
|
|
|
|
### Delete ":" and everything behind in readings name
|
|
$argument[0] =~ s/:.*//;
|
|
|
|
### Log entries for debugging
|
|
Log3 $name, 5, $name. " : km200_DbLog_splitFn - Content of event : " . $event;
|
|
Log3 $name, 5, $name. " : km200_DbLog_splitFn - Content of argument[0] : " . $argument[0];
|
|
Log3 $name, 5, $name. " : km200_DbLog_splitFn - Content of argument[1] : " . $argument[1];
|
|
|
|
### If the service to be changed is identical to the one where the unit received from
|
|
if ($argument[0] = $hash->{temp}{ServiceDbLogSplitHash}{id})
|
|
{
|
|
### Get values being changed from hash
|
|
$reading = $argument[0];
|
|
$value = $argument[1];
|
|
$unit = $hash->{temp}{ServiceDbLogSplitHash}{unitOfMeasure};
|
|
}
|
|
|
|
### Delete temporary json-hash for DbLog-Split
|
|
$hash->{temp}{ServiceDbLogSplitHash} = ();
|
|
### Delete temporary json-hash for DbLog-Split
|
|
|
|
return ($reading, $value, $unit);
|
|
}
|
|
####END####### To bind unit of value to DbLog entries ##########################################################END#####
|
|
|
|
|
|
###START###### Deactivate module module after "undefine" command by fhem ######################################START####
|
|
sub km200_Undefine($$)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my $name = $hash->{NAME};
|
|
my $url = $hash->{URL};
|
|
|
|
### Stop the internal timer for this module
|
|
RemoveInternalTimer($hash);
|
|
|
|
Log3 $name, 3, $name. " - km200 has been undefined. The KM unit at $url will no longer polled.";
|
|
|
|
return undef;
|
|
}
|
|
####END####### Deactivate module module after "undefine" command by fhem #######################################END#####
|
|
|
|
|
|
###START###### Handle attributes after changes via fhem GUI ###################################################START####
|
|
sub km200_Attr(@)
|
|
{
|
|
my @a = @_;
|
|
my $name = $a[1];
|
|
my $hash = $defs{$name};
|
|
my $IntervalDynVal = $hash->{INTERVALDYNVAL};
|
|
my $DelayDynVal = $hash->{DELAYDYNVAL};
|
|
my $ReadBackDelay = $hash->{READBACKDELAY};
|
|
|
|
|
|
### Check whether disable attribute has been provided
|
|
if ($a[2] eq "disable")
|
|
{
|
|
###START### Check whether device shall be disabled
|
|
if ($a[3] == 1)
|
|
{
|
|
### Set new status
|
|
$hash->{STATE} = "Disabled";
|
|
|
|
### Stop the current timer
|
|
RemoveInternalTimer($hash);
|
|
Log3 $name, 4, $name. " : km200 - InternalTimer has been removed.";
|
|
|
|
### Delete all Readings
|
|
fhem( "deletereading $name .*" );
|
|
|
|
### Recover list of root services
|
|
@{$hash->{Secret}{KM200ALLSERVICES}}= @{$hash->{Secret}{KM200ALLSERVICESBACKUP}};
|
|
|
|
Log3 $name, 3, $name. " : km200 - Device disabled as per attribute.";
|
|
}
|
|
else
|
|
{
|
|
### Initiate the timer for first time polling of values from KM200 but wait 10s
|
|
InternalTimer(gettimeofday()+10, "km200_GetInitService", $hash, 1);
|
|
Log3 $name, 4, $name. " : km200 - Internal timer for Initialisation of services re-started.";
|
|
|
|
### Set new status
|
|
$hash->{STATE} = "Initiating Sounding...";
|
|
|
|
Log3 $name, 4, $name. " : km200 - Device enabled as per attribute.";
|
|
}
|
|
####END#### Check whether device shall be disabled
|
|
}
|
|
### Check whether dynamic interval attribute has been provided
|
|
elsif ($a[2] eq "IntervalDynVal")
|
|
{
|
|
|
|
$IntervalDynVal = $a[3];
|
|
###START### Check whether polling interval is not too short
|
|
if ($IntervalDynVal > 19)
|
|
{
|
|
$hash->{INTERVALDYNVAL} = $IntervalDynVal;
|
|
Log3 $name, 4, $name. " : km200 - IntervalDynVal set to attribute value:" . $IntervalDynVal ." s";
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - Gateway interval for IntervalDynVal too small - server response time longer than defined interval, please use something >=20, default is 90";
|
|
}
|
|
####END#### Check whether polling interval is not too short
|
|
}
|
|
### Check whether polling timeout attribute has been provided
|
|
elsif($a[2] eq "PollingTimeout")
|
|
{
|
|
###START### Check whether timeout is not too short
|
|
if ($a[3] >= 5)
|
|
{
|
|
$hash->{POLLINGTIMEOUT} = $a[3];
|
|
Log3 $name, 4, $name. " : km200 - Polling timeout set to attribute value:" . $a[3] ." s";
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 4, $name. " : km200 - Error - Gateway polling timeout attribute too small: " . $a[3] ." s";
|
|
return $name .": Error - Gateway polling timeout attribute is too small - server response time is 5s minimum, default is 5";
|
|
}
|
|
####END#### Check whether timeout is not too short
|
|
}
|
|
### Check whether DoNotPoll attribute have been provided
|
|
elsif($a[2] eq "DoNotPoll")
|
|
{
|
|
my @KM200_DONOTPOLL = ();
|
|
my @temp = @a;
|
|
|
|
### Stop the current timer
|
|
RemoveInternalTimer($hash);
|
|
Log3 $name, 4, $name. " : km200 - InternalTimer has been removed.";
|
|
|
|
### Delete the first 3 items of the array
|
|
splice @temp, 0, 3;
|
|
|
|
### Insert empty field as minimum entry
|
|
push @temp, "";
|
|
|
|
### Transform string entries seperated by blank into array
|
|
@KM200_DONOTPOLL = split(/\s+/, $temp[0]);
|
|
|
|
### Remove trailing slash of each item if available
|
|
|
|
### For each item found in this empty parent directory
|
|
foreach my $item (@KM200_DONOTPOLL)
|
|
{
|
|
### Delete trailing slash
|
|
$item =~ s/\/$//;
|
|
}
|
|
|
|
### Save list of services not to be polled into hash
|
|
@{$hash->{Secret}{KM200DONOTPOLL}} = @KM200_DONOTPOLL;
|
|
|
|
### Get original list of root services back
|
|
@{$hash->{Secret}{KM200ALLSERVICES}} = @{$hash->{Secret}{KM200ALLSERVICESBACKUP}};
|
|
|
|
### For every blacklisted service
|
|
foreach my $SearchWord(@KM200_DONOTPOLL)
|
|
{
|
|
### Filter all blocked root services out of services to be polled
|
|
my $FoundPosition = first_index{ $_ eq $SearchWord }@{$hash->{Secret}{KM200ALLSERVICES}};
|
|
if ($FoundPosition >= 0)
|
|
{
|
|
splice(@{$hash->{Secret}{KM200ALLSERVICES}}, $FoundPosition, 1);
|
|
}
|
|
}
|
|
### Message for debugging purposes
|
|
Log3 $name, 5, $name. "km200 module is only polling the following services! \n @{$hash->{Secret}{KM200ALLSERVICES}}";
|
|
Log3 $name, 5, $name. "km200 module is NOT polling the following services! \n @{$hash->{Secret}{KM200DONOTPOLL}}";
|
|
Log3 $name, 4, $name. " : km200 - The following services will not be polled: ". $a[3];
|
|
|
|
### Interrupting all currently running Polling
|
|
@{$hash->{Secret}{KM200DYNSERVICES}} = "";
|
|
$hash->{temp}{ServiceCounterDyn} = 0;
|
|
|
|
### Delete all Readings
|
|
fhem( "deletereading $name .*" );
|
|
|
|
### Re-start the sounding of values from KM200 but wait the period of $hash->{POLLINGTIMEOUT} + 10s
|
|
InternalTimer(gettimeofday()+$hash->{POLLINGTIMEOUT}+10, "km200_GetInitService", $hash, 1);
|
|
Log3 $name, 4, $name. " : km200 - Sounding of services re-started after change of DoNotPoll attribute";
|
|
}
|
|
### Check whether time-out for Read-Back has been provided
|
|
if($a[2] eq "ReadBackDelay")
|
|
{
|
|
$ReadBackDelay = $a[3];
|
|
###START### Check whether ReadBackDelay is valid
|
|
if ($ReadBackDelay >= 0)
|
|
{
|
|
$hash->{READBACKDELAY} = $ReadBackDelay;
|
|
Log3 $name, 4, $name. " : km200 - ReadBackDelay set to attribute value:" . $ReadBackDelay ." s";
|
|
}
|
|
else
|
|
{
|
|
return $name .": Error - Read-Back delay time must be positive. Default is 0us";
|
|
}
|
|
####END#### Check whether ReadBackDelay is valid
|
|
}
|
|
### If no attributes of the above known ones have been selected
|
|
else
|
|
{
|
|
# Do nothing
|
|
}
|
|
return undef;
|
|
}
|
|
####END####### Handle attributes after changes via fhem GUI ####################################################END#####
|
|
|
|
|
|
###START###### Obtain value after "get" command by fhem #######################################################START####
|
|
sub km200_Get($@)
|
|
{
|
|
my ( $hash, @a ) = @_;
|
|
|
|
### If not enough arguments have been provided
|
|
if ( @a < 2 )
|
|
{
|
|
return "\"get km200\" needs at least one argument";
|
|
}
|
|
|
|
my $name = shift @a;
|
|
my $service = shift @a;
|
|
my $option = shift @a;
|
|
my %km200_gets;
|
|
my $ReturnValue;
|
|
my $ReturnMessage;
|
|
|
|
### Get the list of possible services and create a hash out of it
|
|
my @GetServices = @{$hash->{Secret}{KM200ALLSERVICES}};
|
|
|
|
foreach my $item(@GetServices)
|
|
{
|
|
$km200_gets{$item} = ("1");
|
|
}
|
|
|
|
### Remove trailing slash if available
|
|
$service = $1 if($service=~/(.*)\/$/);
|
|
|
|
### If service chosen in GUI does not exist
|
|
if(!$km200_gets{$service})
|
|
{
|
|
my @cList = keys %km200_gets;
|
|
return "Unknown argument $service, choose one of " . join(" ", @cList);
|
|
}
|
|
|
|
### Check whether the initialisation process has been finished
|
|
if ($hash->{temp}{ServiceCounterInit} == false)
|
|
{
|
|
### Save chosen service into hash
|
|
$hash->{temp}{service} = $service;
|
|
|
|
### Read service-hash
|
|
$ReturnValue = km200_GetSingleService($hash);
|
|
|
|
### If the "get" - option has been set to "Json" for the return of the raw Json-string
|
|
if ($option =~ m/json/i)
|
|
{
|
|
$ReturnMessage = $hash->{temp}{JsonRaw};
|
|
}
|
|
### If no option has been chosen, just return the result of the value.
|
|
else
|
|
{
|
|
### If type is a floatvalue then format decimals
|
|
if ($ReturnValue->{type} eq "floatValue")
|
|
{
|
|
$ReturnMessage = sprintf("%.1f", $ReturnValue->{value});
|
|
}
|
|
### If type is something else just pass throught
|
|
else
|
|
{
|
|
$ReturnMessage = $ReturnValue->{value};
|
|
}
|
|
}
|
|
}
|
|
### If the initialisation process has NOT been finished
|
|
else
|
|
{
|
|
$ReturnMessage = "The initialisation process is still ongoing. Please wait for the STATE changing to \"Standby\"";
|
|
}
|
|
|
|
|
|
### Delete temporary values
|
|
$hash->{temp}{service} = "";
|
|
$hash->{temp}{JsonRaw} = "";
|
|
|
|
|
|
### Return value
|
|
return($ReturnMessage);
|
|
}
|
|
####END####### Obtain value after "get" command by fhem ########################################################END#####
|
|
|
|
|
|
###START###### Manipulate service after "set" command by fhem #################################################START####
|
|
sub km200_Set($@)
|
|
{
|
|
my ( $hash, @a ) = @_;
|
|
|
|
### If not enough arguments have been provided
|
|
if ( @a < 2 )
|
|
{
|
|
return "\"set km200\" needs at least one argument";
|
|
}
|
|
|
|
my $name = shift @a;
|
|
my $service = shift @a;
|
|
my $value = join(" ", @a);
|
|
my %km200_sets;
|
|
my $ReturnMessage;
|
|
|
|
### Get the list of possible services and create a hash out of it
|
|
my @WriteableServices = @{$hash->{Secret}{KM200WRITEABLESERVICES}};
|
|
|
|
foreach my $item(@WriteableServices)
|
|
{
|
|
$km200_sets{$item} = ("1");
|
|
}
|
|
|
|
### If service chosen in GUI does not exist
|
|
if(!$km200_sets{$service})
|
|
{
|
|
my @cList = keys %km200_sets;
|
|
return "Unknown argument $service, choose one of " . join(" ", @cList);
|
|
}
|
|
|
|
### Check whether the initialisation process has been finished
|
|
if ($hash->{temp}{ServiceCounterInit} == false)
|
|
{
|
|
### Save chosen service into hash
|
|
$hash->{temp}{service} = $service;
|
|
$hash->{temp}{postdata} = $value;
|
|
|
|
### Call set sub
|
|
$ReturnMessage = km200_PostSingleService($hash);
|
|
}
|
|
### If the initialisation process has NOT been finished
|
|
else
|
|
{
|
|
$ReturnMessage = "The initialisation process is still ongoing. Please wait for the STATE changing to \"Standby\"";
|
|
}
|
|
|
|
### Delete temporary hash values
|
|
$hash->{temp}{postdata} = "";
|
|
$hash->{temp}{service} = "";
|
|
|
|
return($ReturnMessage);
|
|
}
|
|
####END####### Manipulate service after "Set" command by fhem ##################################################END#####
|
|
|
|
|
|
###START####### Repeats "string" for "count" times ############################################################START####
|
|
sub str_repeat($$)
|
|
{
|
|
my $string = $_[0];
|
|
my $count = $_[1];
|
|
return(${string}x${count});
|
|
}
|
|
####END######## Repeats "string" for "count" times #############################################################END#####
|
|
|
|
|
|
###START###### Subroutine Encrypt Data ########################################################################START####
|
|
sub km200_Encrypt($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
|
|
my $km200_crypt_key_private = $hash->{Secret}{CRYPTKEYPRIVATE};
|
|
my $name = $hash->{NAME};
|
|
my $encryptData = $hash->{temp}{jsoncontent};
|
|
|
|
# Create Rijndael encryption object
|
|
my $cipher = Crypt::Rijndael->new($km200_crypt_key_private, Crypt::Rijndael::MODE_ECB() );
|
|
|
|
# Get blocksize and add PKCS #7 padding
|
|
my $blocksize = $cipher->blocksize();
|
|
my $encrypt_padchar = $blocksize - ( length( $encryptData ) % $blocksize );
|
|
$encryptData .= str_repeat( chr( $encrypt_padchar ), $encrypt_padchar );
|
|
|
|
# Do the encryption
|
|
my $ciphertext = $cipher->encrypt( $encryptData );
|
|
|
|
# Do additional encoding with base64
|
|
$ciphertext = encode_base64($ciphertext);
|
|
|
|
# Return the encoded text
|
|
return($ciphertext);
|
|
}
|
|
####END####### Subroutine Encrypt Data #########################################################################END#####
|
|
|
|
|
|
###START###### Subroutine Decrypt Data ########################################################################START####
|
|
sub km200_Decrypt($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
|
|
my $km200_crypt_key_private = $hash->{Secret}{CRYPTKEYPRIVATE};
|
|
my $name = $hash->{NAME};
|
|
my $decryptData = $hash->{temp}{decodedcontent};
|
|
|
|
### Log entries for debugging purposes
|
|
#Log3 $name, 5, $name. " : km200 - decryptData2 - decryptData : " .$decryptData;
|
|
|
|
# Remove additional encoding with base64
|
|
$decryptData = decode_base64($decryptData);
|
|
|
|
### Log entries for debugging purposes
|
|
#Log3 $name, 5, $name. " : km200 - decryptData2 - base64decode : " .$decryptData;
|
|
|
|
# Check whether the length of the decryptData is NOT multiplies of 16
|
|
if ((length($decryptData)&0xF) != 0)
|
|
{
|
|
# Return nothing which will end this subroutine
|
|
return "";
|
|
}
|
|
|
|
# Create Rijndael decryption object and do the decryption
|
|
my $cipher = Crypt::Rijndael->new($km200_crypt_key_private, Crypt::Rijndael::MODE_ECB() );
|
|
my $deciphertext = $cipher->decrypt( $decryptData );
|
|
|
|
# Remove zero padding
|
|
$deciphertext =~ s/\x00+$//;
|
|
|
|
# Remove PKCS #7 padding
|
|
my $decipher_len = length($deciphertext);
|
|
my $decipher_padchar = ord(substr($deciphertext,($decipher_len - 1),1));
|
|
|
|
my $i = 0;
|
|
|
|
for ( $i = 0; $i < $decipher_padchar ; $i++ )
|
|
{
|
|
if ( $decipher_padchar != ord( substr($deciphertext,($decipher_len - $i - 1),1)))
|
|
{
|
|
last;
|
|
}
|
|
}
|
|
|
|
# Return decrypted text
|
|
if ( $i != $decipher_padchar )
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200 - decryptData1 - decipher_len : " .$decipher_len;
|
|
$deciphertext =~ s/\x00+$//;
|
|
Log3 $name, 5, $name. " : km200 - decryptData1 - deciphertext : " .$deciphertext;
|
|
### Log entries for debugging purposes
|
|
return $deciphertext;
|
|
}
|
|
|
|
else
|
|
{
|
|
$deciphertext = substr($deciphertext,0,$decipher_len - $decipher_padchar);
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200 - decryptData2 - decipher_len : " .$decipher_len;
|
|
$deciphertext =~ s/\x00+$//;
|
|
Log3 $name, 5, $name. " : km200 - decryptData2 - deciphertext : " .$deciphertext;
|
|
### Log entries for debugging purposes
|
|
return $deciphertext;
|
|
}
|
|
}
|
|
####END####### Subroutine Decrypt Data #########################################################################END#####
|
|
|
|
###START###### Subroutine set individual data value ###########################################################START####
|
|
sub km200_PostSingleService($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my $Service = $hash->{temp}{service};
|
|
my $km200_gateway_host = $hash->{URL} ;
|
|
my $name = $hash->{NAME} ;
|
|
my $PollingTimeout = $hash->{POLLINGTIMEOUT};
|
|
my $err;
|
|
my $data;
|
|
my $jsonSend;
|
|
my $jsonRead;
|
|
my $JsonContent;
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. ("km200_Set - Writing value: " . $hash->{temp}{postdata} . " to the service : ". $Service . "\n");
|
|
|
|
### Read the current json string
|
|
$jsonRead = km200_GetSingleService($hash);
|
|
|
|
#### If the get-command returns an error due to an unknown Service requested
|
|
if ($jsonRead -> {type} eq "ERROR")
|
|
{
|
|
### Rescue original Service request
|
|
my $WriteService = $Service;
|
|
|
|
### Try to replace the Post-String with nothing
|
|
$Service =~ s/\/1-Mo//i;
|
|
$Service =~ s/\/2-Tu//i;
|
|
$Service =~ s/\/3-We//i;
|
|
$Service =~ s/\/4-Th//i;
|
|
$Service =~ s/\/5-Fr//i;
|
|
$Service =~ s/\/6-Sa//i;
|
|
$Service =~ s/\/7-Su//i;
|
|
|
|
### Save corrected string in hash
|
|
$hash->{temp}{service} = $Service;
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - Trying to re-read Service - Assuming its a switchProgram list";
|
|
|
|
### Try again to read the current json string again with the corrected service
|
|
$jsonRead = km200_GetSingleService($hash);
|
|
|
|
### Check whether the type is an switchProgram.
|
|
### If true, the requested service was a particular week of the switchProgram
|
|
if ($jsonRead -> {type} eq "switchProgram")
|
|
{
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - It is a switchProgram list!";
|
|
|
|
### For each weekday, get current readings, delete all unnecessary blanks and transform to array
|
|
my $TempReadingVal;
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/1-Mo"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingMo = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/2-Tu"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingTu = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/3-We"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingWe = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/4-Th"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingTh = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/5-Fr"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingFr = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/6-Sa"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingSa = split(/\s+/, $TempReadingVal,0);
|
|
|
|
$TempReadingVal = ReadingsVal($name,($Service . "/7-Su"),"");
|
|
$TempReadingVal =~ s/\s+/ /g;
|
|
$TempReadingVal =~ s/\s+$//g;
|
|
my @TempReadingSu = split(/\s+/, $TempReadingVal,0);
|
|
|
|
|
|
### For value to be written, delete all unnecessary blanks and transform to array and get length of array
|
|
my $ReturnString = $hash->{temp}{postdata};
|
|
$ReturnString =~ s/\s+/ /g;
|
|
$ReturnString =~ s/\s+$//g;
|
|
my @TempReading = split(/\s+/, $ReturnString);
|
|
my $TempReadingLength = @TempReading;
|
|
|
|
|
|
### Obtain the allowed terminology for setpoints
|
|
$hash->{temp}{service} = $jsonRead -> {setpointProperty}{id};
|
|
my $TempSetpointsJson = km200_GetSingleService($hash);
|
|
|
|
|
|
my @TempSetpointNames =();
|
|
### For each item found in this empty parent directory
|
|
foreach my $item (@{ $TempSetpointsJson->{references} })
|
|
{
|
|
my $TempSetPoint = substr($item->{id}, (rindex($item->{id}, "/") - length($item->{id}) +1));
|
|
|
|
### Add service, which is one of the allowed terminologies at the same time, to the list of all known services
|
|
push (@TempSetpointNames, $TempSetPoint);
|
|
}
|
|
|
|
### Restore the original service
|
|
$hash->{temp}{service} = $Service;
|
|
|
|
### If number of switchpoints exceeds maximum allowed
|
|
if (($TempReadingLength / 2) > $jsonRead -> {maxNbOfSwitchPointsPerDay})
|
|
{
|
|
return ("ERROR - Too much Switchpoints for weeklist inserted. \n Do not add more than " . $jsonRead -> {maxNbOfSwitchPointsPerDay} . " SwitchPoints per day!\n");
|
|
}
|
|
|
|
### If content of array is not even
|
|
if (($TempReadingLength % 2) != 0)
|
|
{
|
|
return "ERROR - At least one Switchtime or Switchpoint is missing. \n Make sure you always have couples of Switchtime and Switchpoint!\n";
|
|
}
|
|
|
|
### Check whether description of setpoints is the same as referenced and the data is in the right order
|
|
for (my $i=0;$i<$TempReadingLength;$i+=2)
|
|
{
|
|
### If the even element behind the uneven index [1, 3, 5, ...] is not one of the pre-defined setpoints
|
|
if (! grep /($TempReading[$i+1])/,@TempSetpointNames)
|
|
{
|
|
return "ERROR - At least for one Switchpoint the wrong terminology has been used. Only use one of the following items: " . join(' , ',@TempSetpointNames) ."\n";;
|
|
}
|
|
|
|
### If the uneven element behind the even index [0, 2, 4, ...]is not a number, hand back an error message
|
|
if ($TempReading[$i] !~ /^[0-9.-]+$/)
|
|
{
|
|
return "ERROR - At least for one Switchtime a number is expected at that position. \n Ensure the correct syntax of time and switchpoint. (E.g. 0600 eco)\n";
|
|
}
|
|
|
|
### Convert timepoint into raster of defined switchPointTimeRaster
|
|
my $TempHours = substr($TempReading[$i], 0, length($TempReading[$i])-2);
|
|
if ($TempHours > 23)
|
|
{
|
|
$TempHours = 23;
|
|
}
|
|
my $TempMinutes = substr($TempReading[$i], -2);
|
|
if ($TempMinutes > 59)
|
|
{
|
|
$TempMinutes = 59;
|
|
}
|
|
$TempMinutes = $TempMinutes / ($jsonRead -> {switchPointTimeRaster});
|
|
$TempMinutes =~ s/^(.*?)\..*$/$1/;
|
|
$TempMinutes = $TempMinutes * ($jsonRead -> {switchPointTimeRaster});
|
|
$TempMinutes = sprintf ('%02d', $TempMinutes);
|
|
$TempReading[$i] = ($TempHours . $TempMinutes);
|
|
|
|
}
|
|
|
|
$hash->{temp}{postdata} = join(" ", @TempReading);
|
|
|
|
### For the requested day to be changed, save new value
|
|
if ($WriteService =~ m/1-Mo/i)
|
|
{
|
|
@TempReadingMo = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/2-Tu/i)
|
|
{
|
|
@TempReadingTu = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/3-We/i)
|
|
{
|
|
@TempReadingWe = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/4-Th/i)
|
|
{
|
|
@TempReadingTh = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/5-Fr/i)
|
|
{
|
|
@TempReadingFr = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/6-Sa/i)
|
|
{
|
|
@TempReadingSa = @TempReading;
|
|
}
|
|
elsif ($WriteService =~ m/7-Su/i)
|
|
{
|
|
@TempReadingSu = @TempReading;
|
|
}
|
|
|
|
|
|
### For every weekday create setpoint hash and push it to array of hashes of switchpoints to be send
|
|
my @SwitchPointsSend =();
|
|
|
|
for (my $i=0;$i<$#TempReadingMo;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Mo";
|
|
my $TempHours = substr($TempReadingMo[$i], 0, length($TempReadingMo[$i])-2);
|
|
my $TempMinutes = substr($TempReadingMo[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingMo[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingTu;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Tu";
|
|
my $TempHours = substr($TempReadingTu[$i], 0, length($TempReadingTu[$i])-2);
|
|
my $TempMinutes = substr($TempReadingTu[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingTu[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingWe;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "We";
|
|
my $TempHours = substr($TempReadingWe[$i], 0, length($TempReadingWe[$i])-2);
|
|
my $TempMinutes = substr($TempReadingWe[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingWe[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingTh;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Th";
|
|
my $TempHours = substr($TempReadingTh[$i], 0, length($TempReadingTh[$i])-2);
|
|
my $TempMinutes = substr($TempReadingTh[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingTh[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingFr;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Fr";
|
|
my $TempHours = substr($TempReadingFr[$i], 0, length($TempReadingFr[$i])-2);
|
|
my $TempMinutes = substr($TempReadingFr[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingFr[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingSa;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Sa";
|
|
my $TempHours = substr($TempReadingSa[$i], 0, length($TempReadingSa[$i])-2);
|
|
my $TempMinutes = substr($TempReadingSa[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingSa[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
for (my $i=0;$i<$#TempReadingSu;$i+=2)
|
|
{
|
|
my $TempHashSend;
|
|
$TempHashSend->{"dayOfWeek"} = "Su";
|
|
my $TempHours = substr($TempReadingSu[$i], 0, length($TempReadingSu[$i])-2);
|
|
my $TempMinutes = substr($TempReadingSu[$i], -2);
|
|
$TempHashSend->{"time"} = ($TempHours * 60 ) + $TempMinutes;
|
|
$TempHashSend->{"setpoint"} = $TempReadingSu[$i+1];
|
|
push @SwitchPointsSend, $TempHashSend;
|
|
}
|
|
|
|
### Save array of hashes of switchpoints into json hash to be send
|
|
@{$jsonSend->{switchPoints}} = @SwitchPointsSend;
|
|
|
|
### Create full URL of the current Service to be written
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
### Encode as json
|
|
$JsonContent = encode_json($jsonSend);
|
|
|
|
### Delete the name of hash, "{" and "}" out of json String. No idea why but result of Try-and-Error method
|
|
$JsonContent =~ s/{"switchPoints"://;
|
|
$JsonContent =~ s/]}/]/g;
|
|
|
|
### Encrypt
|
|
$hash->{temp}{jsoncontent} = $JsonContent;
|
|
$data = km200_Encrypt($hash);
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - Trying to push switchPoint list to KM-Unit";
|
|
|
|
### Create parameter set for HttpUtils_BlockingGet
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout * 5,
|
|
data => $data,
|
|
method => "POST",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
};
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagSetRequest} = true;
|
|
|
|
### Write value with HttpUtils_BlockingGet
|
|
($err, $data) = HttpUtils_BlockingGet($param);
|
|
|
|
### Reset flag
|
|
$hash->{status}{FlagSetRequest} = false;
|
|
|
|
### If error message has been returned
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " - ERROR: $err";
|
|
return $err;
|
|
}
|
|
|
|
Log3 $name, 5, $name. ("Waiting for processing time (READBACKDELAY / [ms]) : " . $hash->{READBACKDELAY} . " \n");
|
|
|
|
### Make a pause before ReadBack
|
|
usleep ($hash->{READBACKDELAY}*1000);
|
|
|
|
### Read service-hash and format it so it is compareable to the sent content
|
|
my $ReReadContent = km200_GetSingleService($hash);
|
|
$ReReadContent = $ReReadContent->{switchPoints};
|
|
$ReReadContent = encode_json($ReReadContent);
|
|
$ReReadContent =~ s/{"switchPoints"://;
|
|
$ReReadContent =~ s/]}/]/g;
|
|
|
|
### Transform back into array of hashes
|
|
eval
|
|
{
|
|
$ReReadContent = decode_json(encode_utf8($ReReadContent));
|
|
$JsonContent = decode_json(encode_utf8($JsonContent));
|
|
1;
|
|
}
|
|
or do
|
|
{
|
|
};
|
|
|
|
### Set Counter for found items in SwitchPrograms
|
|
my $FoundJsonItem = 0;
|
|
|
|
|
|
### For every item of the array of SwitchPrograms to be send
|
|
foreach my $ReReadItem (@{$ReReadContent})
|
|
{
|
|
### Set Counter for found items of ReRead values
|
|
my $FoundReReadItem = 0;
|
|
|
|
### For every item of the array of SwitchPrograms after Re-Reading
|
|
foreach my $JsonItem (@{$JsonContent})
|
|
{
|
|
### If the current Switchprogram - hash does not have the same amount of keys
|
|
if (%$ReReadItem ne %$JsonItem)
|
|
{
|
|
### Do nothing
|
|
#print "they don't have the same number of keys\n";
|
|
}
|
|
### If the current Switchprogram - hash do have the same amount of keys
|
|
else
|
|
{
|
|
### Compare key names and values
|
|
my %cmp = map { $_ => 1 } keys %$ReReadItem;
|
|
for my $key (keys %$JsonItem)
|
|
{
|
|
last unless exists $cmp{$key};
|
|
last unless $$ReReadItem{$key} eq $$JsonItem{$key};
|
|
delete $cmp{$key};
|
|
}
|
|
if (%cmp)
|
|
{
|
|
### Do nothing
|
|
#print "they don't have the same keys or values\n";
|
|
}
|
|
else
|
|
{
|
|
### Inkrement Counter
|
|
$FoundReReadItem = 1;
|
|
#print "they have the same keys and values\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
### If item has been found
|
|
if ($FoundReReadItem == 1)
|
|
{
|
|
### Inkrement Counter for found identical SwitchPoints
|
|
$FoundJsonItem++;
|
|
}
|
|
}
|
|
|
|
my $ReturnValue;
|
|
|
|
if ($FoundJsonItem == @{$ReReadContent})
|
|
{
|
|
$ReturnValue = "The service " . $Service . " has been changed succesfully!";
|
|
Log3 $name, 5, $name. "Writing $Service succesfully \n";
|
|
}
|
|
else
|
|
{
|
|
$ReturnValue = "ERROR - The service " . $Service . " could not changed! \n";
|
|
}
|
|
|
|
### Return the status message
|
|
return $ReturnValue;
|
|
}
|
|
}
|
|
### Check whether the type is an switchProgram.
|
|
### If true, the requested service is referring to the entire week but not a particular week.
|
|
if ($jsonRead -> {type} eq "switchProgram")
|
|
{
|
|
### Create full URL of the current Service to be written
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
### Get the string to be send
|
|
$JsonContent = $hash->{temp}{postdata};
|
|
|
|
### Encrypt
|
|
$hash->{temp}{jsoncontent} = $JsonContent;
|
|
$data = km200_Encrypt($hash);
|
|
|
|
### Create parameter set for HttpUtils_BlockingGet
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout * 5,
|
|
data => $data,
|
|
method => "POST",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
};
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagSetRequest} = true;
|
|
|
|
### Write value with HttpUtils_BlockingGet
|
|
($err, $data) = HttpUtils_BlockingGet($param);
|
|
|
|
### Reset flag
|
|
$hash->{status}{FlagSetRequest} = false;
|
|
|
|
### If error message has been returned
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " - ERROR: $err";
|
|
return $err;
|
|
}
|
|
|
|
Log3 $name, 5, $name. ("Waiting for processing time (READBACKDELAY / [ms]) : " . $hash->{READBACKDELAY} . " \n");
|
|
### Make a pause before ReadBack
|
|
usleep ($hash->{READBACKDELAY}*1000);
|
|
|
|
### Read service-hash and format it so it is compareable to the sent content
|
|
my $ReReadContent = km200_GetSingleService($hash);
|
|
$ReReadContent = $ReReadContent->{switchPoints};
|
|
$ReReadContent = encode_json($ReReadContent);
|
|
$ReReadContent =~ s/{"switchPoints"://;
|
|
$ReReadContent =~ s/]}/]/g;
|
|
|
|
### Transform back into array of hashes
|
|
eval
|
|
{
|
|
$ReReadContent = decode_json(encode_utf8($ReReadContent));
|
|
$JsonContent = decode_json(encode_utf8($JsonContent));
|
|
1;
|
|
}
|
|
or do
|
|
{
|
|
};
|
|
|
|
### Set Counter for found items in SwitchPrograms
|
|
my $FoundJsonItem = 0;
|
|
|
|
|
|
### For every item of the array of SwitchPrograms to be send
|
|
foreach my $ReReadItem (@{$ReReadContent})
|
|
{
|
|
### Set Counter for found items of ReRead values
|
|
my $FoundReReadItem = 0;
|
|
|
|
### For every item of the array of SwitchPrograms after Re-Reading
|
|
foreach my $JsonItem (@{$JsonContent})
|
|
{
|
|
|
|
### If the current Switchprogram - hash does not have the same amount of keys
|
|
if (%$ReReadItem != %$JsonItem)
|
|
{
|
|
### Do nothing
|
|
#print "they don't have the same number of keys\n";
|
|
}
|
|
### If the current Switchprogram - hash do have the same amount of keys
|
|
else
|
|
{
|
|
### Compare key names and values
|
|
my %cmp = map { $_ => 1 } keys %$ReReadItem;
|
|
for my $key (keys %$JsonItem)
|
|
{
|
|
last unless exists $cmp{$key};
|
|
last unless $$ReReadItem{$key} eq $$JsonItem{$key};
|
|
delete $cmp{$key};
|
|
}
|
|
if (%cmp)
|
|
{
|
|
### Do nothing
|
|
#print "they don't have the same keys or values\n";
|
|
}
|
|
else
|
|
{
|
|
### Inkrement Counter
|
|
$FoundReReadItem = 1;
|
|
#print "they have the same keys and values\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
### If item has been found
|
|
if ($FoundReReadItem == 1)
|
|
{
|
|
### Inkrement Counter for found identical SwitchPoints
|
|
$FoundJsonItem++;
|
|
}
|
|
}
|
|
|
|
my $ReturnValue;
|
|
|
|
if ($FoundJsonItem == @{$ReReadContent})
|
|
{
|
|
$ReturnValue = "The service " . $Service . " has been changed succesfully!";
|
|
Log3 $name, 5, $name. "The service $Service has been changed succesfully!";
|
|
}
|
|
else
|
|
{
|
|
$ReturnValue = "ERROR - The service " . $Service . " could not changed! \n";
|
|
Log3 $name, 5, $name. "Writing $Service was NOT succesfully";
|
|
}
|
|
|
|
### Return the status message
|
|
return $ReturnValue;
|
|
}
|
|
## Check whether the type is a single value containing a string
|
|
elsif($jsonRead->{type} eq "stringValue")
|
|
{
|
|
### Save chosen value into hash to be send
|
|
$jsonSend->{value} = $hash->{temp}{postdata};
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - String value";
|
|
|
|
### Create full URL of the current Service to be written
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
### Encode as json
|
|
$JsonContent = encode_json($jsonSend);
|
|
|
|
### Encrypt
|
|
$hash->{temp}{jsoncontent} = $JsonContent;
|
|
$data = km200_Encrypt($hash);
|
|
|
|
|
|
### Create parameter set for HttpUtils_BlockingGet
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout,
|
|
data => $data,
|
|
method => "POST",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
};
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagSetRequest} = true;
|
|
|
|
### Write value with HttpUtils_BlockingGet
|
|
($err, $data) = HttpUtils_BlockingGet($param);
|
|
|
|
### Reset flag
|
|
$hash->{status}{FlagSetRequest} = false;
|
|
|
|
### If error message has been returned
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " - ERROR: $err";
|
|
return $err;
|
|
}
|
|
|
|
### Make a pause before ReadBack
|
|
usleep ($hash->{READBACKDELAY}*1000);
|
|
|
|
### Read service-hash
|
|
my $ReadValue = km200_GetSingleService($hash);
|
|
|
|
### Return value
|
|
my $ReturnValue = "";
|
|
if ($ReadValue->{value} eq $hash->{temp}{postdata})
|
|
{
|
|
$ReturnValue = "The service " . $Service . " has been changed to: " . $ReadValue->{value};
|
|
Log3 $name, 5, $name. "km200_Set - Writing " . $Service . " succesfully with value: " . $hash->{temp}{postdata};
|
|
}
|
|
else
|
|
{
|
|
$ReturnValue = "ERROR - The service " . $Service . " could not changed.";
|
|
Log3 $name, 5, $name. "km200_Set - Writing " . $Service . " was NOT successful";
|
|
}
|
|
|
|
### Return the status message
|
|
return $ReturnValue;
|
|
}
|
|
## Check whether the type is a single value containing a float value
|
|
elsif($jsonRead -> {type} eq "floatValue")
|
|
{
|
|
### Check whether value to be sent is numeric
|
|
if ($hash->{temp}{postdata} =~ /^[0-9.-]+$/)
|
|
{
|
|
### Save chosen value into hash to be send
|
|
$jsonSend->{value} = ($hash->{temp}{postdata}) * 1;
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - Numeric value";
|
|
|
|
### Create full URL of the current Service to be written
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
### Encode as json
|
|
$JsonContent = encode_json($jsonSend);
|
|
|
|
### Encrypt
|
|
$hash->{temp}{jsoncontent} = $JsonContent;
|
|
$data = km200_Encrypt($hash);
|
|
|
|
|
|
### Create parameter set for HttpUtils_BlockingGet
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout,
|
|
data => $data,
|
|
method => "POST",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
};
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagSetRequest} = true;
|
|
|
|
### Write value with HttpUtils_BlockingGet
|
|
($err, $data) = HttpUtils_BlockingGet($param);
|
|
|
|
### Reset flag
|
|
$hash->{status}{FlagSetRequest} = false;
|
|
|
|
### If error messsage has been returned
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " - ERROR: $err";
|
|
return $err;
|
|
}
|
|
|
|
### Make a pause before ReadBack
|
|
usleep ($hash->{READBACKDELAY}*1000);
|
|
|
|
### Read service-hash
|
|
my $ReadValue = km200_GetSingleService($hash);
|
|
|
|
|
|
### Return value
|
|
my $ReturnValue = "";
|
|
if ($ReadValue->{value} eq $hash->{temp}{postdata})
|
|
{
|
|
$ReturnValue = "The service " . $Service . " has been changed to: " . $ReadValue->{value} . "\n";
|
|
Log3 $name, 5, $name. "km200_Set - Writing " . $Service . " succesfully with value: " . $hash->{temp}{postdata};
|
|
}
|
|
elsif ($jsonRead -> {value} == $ReadValue->{value})
|
|
{
|
|
$ReturnValue = "ERROR - The service " . $Service . " could not changed to: " . $hash->{temp}{postdata} . "\n The value is: " . $ReadValue->{value} . "\n";
|
|
Log3 $name, 5, $name. "km200_Set - Writing " . $Service . " was NOT successful";
|
|
}
|
|
else
|
|
{
|
|
$ReturnValue = "The service " . $Service . " has been rounded to: " . $ReadValue->{value} . "\n";
|
|
Log3 $name, 5, $name. "km200_Set - Writing " . $Service . " was rounded and changed successful";
|
|
}
|
|
|
|
### Return the status message
|
|
return $ReturnValue;
|
|
}
|
|
### If the value to be sent is NOT numeric
|
|
else
|
|
{
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "km200_Set - ERROR - Float value expected!";
|
|
return ("km200_Set - ERROR - Float value expected!\n");
|
|
}
|
|
}
|
|
## If the type is unknown
|
|
else
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_SetSingleService - type unknown for : " .$Service;
|
|
}
|
|
}
|
|
####END####### Subroutine set individual data value ############################################################END#####
|
|
|
|
|
|
###START###### Subroutine get individual data value ###########################################################START####
|
|
sub km200_GetSingleService($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my $Service = $hash->{temp}{service};
|
|
my $km200_gateway_host = $hash->{URL};
|
|
my $name = $hash->{NAME};
|
|
my $PollingTimeout = $hash->{POLLINGTIMEOUT};
|
|
my $json -> {type} = "";
|
|
$json -> {value} = "";
|
|
my $err;
|
|
my $data;
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200 - GetSingleService - service : " .$Service;
|
|
|
|
### Create full URL of the current Service to be read
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200 - GetSingleService - url : " .$url;
|
|
|
|
### Create parameter set for HttpUtils_BlockingGet
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout,
|
|
method => "GET",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
};
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagGetRequest} = true;
|
|
|
|
### Retrieve data from km200
|
|
($err, $data) = HttpUtils_BlockingGet($param);
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200 - GetSingleService - err : " .$err;
|
|
Log3 $name, 5, $name. " : km200 - GetSingleService - data : " .$data;
|
|
|
|
### Block other scheduled and unscheduled routines
|
|
$hash->{status}{FlagGetRequest} = false;
|
|
|
|
### If error message has been reported
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " : ERROR: Service: ".$Service. ": No proper Communication with Gateway: " .$err;
|
|
my $ReturnMessage ="ERROR";
|
|
$json -> {type} = $ReturnMessage;
|
|
$json -> {value} = $ReturnMessage;
|
|
return $json;
|
|
}
|
|
### If NO error message has been reported
|
|
else
|
|
{
|
|
$hash->{temp}{decodedcontent} = $data;
|
|
my $decodedContent = km200_Decrypt($hash);
|
|
|
|
if ($decodedContent ne "")
|
|
{
|
|
eval
|
|
{
|
|
$json = decode_json(encode_utf8($decodedContent));
|
|
1;
|
|
}
|
|
or do
|
|
{
|
|
Log3 $name, 5, $name. " : km200_GetSingleService - Data cannot be parsed by JSON on km200 for http://" . $param->{url};
|
|
};
|
|
|
|
### Check whether the type is a single value containing a string or float value
|
|
if(($json -> {type} eq "stringValue") || ($json -> {type} eq "floatValue"))
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my $JsonValue = $json->{value};
|
|
|
|
### Save json-hash for DbLog-Split
|
|
$hash->{temp}{ServiceDbLogSplitHash} = $json;
|
|
$hash->{temp}{JsonRaw} = $decodedContent;
|
|
|
|
|
|
### Write reading for fhem
|
|
readingsSingleUpdate( $hash, $JsonId, $JsonValue, 1);
|
|
|
|
return $json
|
|
}
|
|
|
|
### Check whether the type is an switchProgram
|
|
elsif ($json -> {type} eq "switchProgram")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_GetSingleService - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_GetSingleService - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_GetSingleService - type : " .$JsonType;
|
|
|
|
### Set up variables
|
|
my $TempReturnVal = "";
|
|
my $TempReadingMo = "";
|
|
my $TempReadingTu = "";
|
|
my $TempReadingWe = "";
|
|
my $TempReadingTh = "";
|
|
my $TempReadingFr = "";
|
|
my $TempReadingSa = "";
|
|
my $TempReadingSu = "";
|
|
|
|
foreach my $item (@{ $json->{switchPoints} })
|
|
{
|
|
### Create string for time and switchpoint in fixed format and write part of Reading String
|
|
|
|
my $time = $item->{time};
|
|
my $temptime = $time / 60;
|
|
my $temptimeHH = int($temptime);
|
|
my $temptimeMM = ($time - ($temptimeHH * 60));
|
|
|
|
$temptimeHH = sprintf ('%02d', $temptimeHH);
|
|
$temptimeMM = sprintf ('%02d', $temptimeMM);
|
|
$temptime = $temptimeHH . $temptimeMM;
|
|
|
|
my $tempsetpoint = $item->{setpoint};
|
|
$tempsetpoint =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(8-length($1)))/e;
|
|
my $TempReading = $temptime . " " . $tempsetpoint;
|
|
|
|
### Create ValueString for this day
|
|
if ($item->{dayOfWeek} eq "Mo")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingMo eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingMo = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingMo = $TempReadingMo . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Tu")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTu = $TempReadingTu . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "We")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingWe eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingWe = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingWe = $TempReadingWe . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Th")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTh eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTh = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTh = $TempReadingTh . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Fr")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingFr eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingFr = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingFr = $TempReadingFr . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Sa")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSa eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSa = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSa = $TempReadingSa . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Su")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSu = $TempReadingSu . " " . $TempReading;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 5, $name. "dayOfWeek of unknow day: " . $item->{dayOfWeek};
|
|
}
|
|
}
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempReturnVal = "1-Mo: " . $TempReadingMo . "\n";
|
|
$TempReturnVal = $TempReturnVal . "2-Tu: " . $TempReadingTu . "\n";
|
|
$TempReturnVal = $TempReturnVal . "3-We: " . $TempReadingWe . "\n";
|
|
$TempReturnVal = $TempReturnVal . "4-Th: " . $TempReadingTh . "\n";
|
|
$TempReturnVal = $TempReturnVal . "5-Fr: " . $TempReadingFr . "\n";
|
|
$TempReturnVal = $TempReturnVal . "6-Sa: " . $TempReadingSa . "\n";
|
|
$TempReturnVal = $TempReturnVal . "7-Su: " . $TempReadingSu . "\n";
|
|
|
|
### Save weeklist in "value"
|
|
$json->{value} = $TempReturnVal;
|
|
|
|
### Save raw Json string
|
|
$hash->{temp}{JsonRaw} = $decodedContent;
|
|
|
|
|
|
my $TempJsonId;
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "1-Mo";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingMo, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "2-Tu";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTu, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "3-We";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingWe, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "4-Th";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTh, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "5-Fr";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingFr, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "6-Sa";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSa, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "7-Su";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSu, 1);
|
|
|
|
return $json
|
|
}
|
|
### Check whether the type is an errorlist
|
|
elsif ($json -> {type} eq "errorList")
|
|
{
|
|
my $TempErrorList = "";
|
|
|
|
### Sort list by timestamps descending
|
|
my @TempSortedErrorList = sort { $b->{t} <=> $a->{t} } @{ $json->{values} };
|
|
|
|
### For every notification do
|
|
foreach my $item (@TempSortedErrorList)
|
|
{
|
|
### Create message string with fixed blocksize
|
|
my $TempTime = $item->{t};
|
|
$TempTime =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(20-length($1)))/e;
|
|
my $TempErrorCode = $item->{dcd};
|
|
$TempErrorCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(3 -length($1)))/e;
|
|
my $TempAddCode = $item->{ccd};
|
|
$TempAddCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(4 -length($1)))/e;
|
|
my $TempClassCode = $item->{cat};
|
|
$TempClassCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(2- length($1)))/e;
|
|
my $TempErrorMessage = "Time: " . $TempTime . "-ErrorCode: " . $TempErrorCode . " -AddCode: " . $TempAddCode . " -Category: " . $TempClassCode;
|
|
|
|
### Create List
|
|
$TempErrorList = $TempErrorList . $TempErrorMessage . "\n";
|
|
}
|
|
### Save raw Json string
|
|
$hash->{temp}{JsonRaw} = $decodedContent;
|
|
|
|
### Save errorList
|
|
$json->{value} = $TempErrorList;
|
|
|
|
return $json;
|
|
}
|
|
### Check whether the type is an refEnum which is indicating an empty parent directory
|
|
elsif ($json -> {type} eq "refEnum")
|
|
{
|
|
### Initialise Return Message
|
|
my $ReturnMessage = "";
|
|
|
|
### For each item found in this empty parent directory
|
|
foreach my $item (@{ $json->{references} })
|
|
{
|
|
### If it is the first item in the list
|
|
if ($ReturnMessage eq "")
|
|
{
|
|
$ReturnMessage = $item->{id};
|
|
}
|
|
### If it is not the first item in the list
|
|
else
|
|
{
|
|
$ReturnMessage = $ReturnMessage . "\n" . $item->{id};
|
|
}
|
|
}
|
|
### Return list of available directories
|
|
$json->{value} = $ReturnMessage;
|
|
|
|
### Save raw Json string
|
|
$hash->{temp}{JsonRaw} = $decodedContent;
|
|
|
|
return $json;
|
|
}
|
|
### Check whether the type is a systeminfo
|
|
elsif ($json -> {type} eq "systeminfo")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my @JsonValues = $json->{values};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_GetSingleService - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_GetSingleService - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_GetSingleService - type : " .$JsonType;
|
|
|
|
### Initialise Return Message
|
|
my $ReturnMessage = "";
|
|
|
|
### Initialise ArrayCounter
|
|
my $ArrayCounter = 0;
|
|
|
|
foreach my $ArrayItem (@{ $json->{values} })
|
|
{
|
|
### Incrementation of ArrayCounter
|
|
$ArrayCounter++;
|
|
|
|
### Get array from scalar
|
|
my %ArrayHash = %{$ArrayItem};
|
|
|
|
while( my( $SystemInfoHashKey, $SystemInfoHashValue ) = each %ArrayHash )
|
|
{
|
|
### Create new Service and write reading for fhem
|
|
my $TempJsonId = $JsonId . "/" . sprintf ('%02d', $ArrayCounter) . "/" . $SystemInfoHashKey;
|
|
readingsSingleUpdate( $hash, $TempJsonId, $SystemInfoHashValue, 1);
|
|
|
|
### If it is the first item in the list
|
|
if ($ReturnMessage eq "")
|
|
{
|
|
$ReturnMessage = $TempJsonId . " = " . $SystemInfoHashValue;
|
|
}
|
|
### If it is not the first item in the list
|
|
else
|
|
{
|
|
$ReturnMessage = $ReturnMessage . "\n" . $TempJsonId . " = " . $SystemInfoHashValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
### Return list of available directories
|
|
$json->{value} = $ReturnMessage;
|
|
|
|
### Save raw Json string
|
|
$hash->{temp}{JsonRaw} = $decodedContent;
|
|
|
|
return $json;
|
|
}
|
|
### If the type is unknown
|
|
else
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_GetSingleService - type unknown for : " .$Service;
|
|
### Log entries for debugging purposes
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 4, $name. " : km200_GetSingleService: ". $Service . " NOT available";
|
|
|
|
my $ReturnMessage = "ERROR";
|
|
$json -> {type} = $ReturnMessage;
|
|
$json -> {value} = $ReturnMessage;
|
|
return $json;
|
|
}
|
|
}
|
|
}
|
|
####END####### Subroutine get individual data value ############################################################END#####
|
|
|
|
|
|
###START###### Subroutine initial contact of services via HttpUtils ###########################################START####
|
|
sub km200_GetInitService($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my $km200_gateway_host = $hash->{URL} ;
|
|
my $name = $hash->{NAME} ;
|
|
$hash->{status}{FlagInitRequest} = true;
|
|
my @KM200_InitServices = @{$hash->{Secret}{KM200ALLSERVICES}};
|
|
my $ServiceCounterInit = $hash->{temp}{ServiceCounterInit};
|
|
my $PollingTimeout = $hash->{POLLINGTIMEOUT};
|
|
my $Service = $KM200_InitServices[$ServiceCounterInit];
|
|
|
|
### Stop the current timer
|
|
RemoveInternalTimer($hash);
|
|
|
|
### If this this loop is accessed for the first time, stop the timer and set status
|
|
if ($ServiceCounterInit == 0)
|
|
{
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "Sounding and importing of services started";
|
|
|
|
### Set status of km200 fhem module
|
|
$hash->{STATE} = "Sounding...";
|
|
}
|
|
|
|
|
|
### Get the values
|
|
my $url ="http://" . $km200_gateway_host . $Service;
|
|
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout,
|
|
hash => $hash,
|
|
method => "GET",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
callback => \&km200_ParseHttpResponseInit
|
|
};
|
|
|
|
### Set flag for initialisation
|
|
$hash->{status}{FlagInitRequest} = true;
|
|
|
|
### Get the value
|
|
HttpUtils_NonblockingGet($param);
|
|
}
|
|
####END####### Subroutine initial contact of services via HttpUtils ############################################END#####
|
|
|
|
|
|
###START###### Subroutine to download complete initial data set from gateway ##################################START####
|
|
# For all known, but not excluded services by attribute "DoNotPoll", try reading the respective values from gateway
|
|
sub km200_ParseHttpResponseInit($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash ->{NAME};
|
|
my $ServiceCounterInit = $hash ->{temp}{ServiceCounterInit};
|
|
my @KM200_RespondingServices = @{$hash ->{Secret}{KM200RESPONDINGSERVICES}};
|
|
my @KM200_WriteableServices = @{$hash ->{Secret}{KM200WRITEABLESERVICES}};
|
|
my @KM200_InitServices = @{$hash ->{Secret}{KM200ALLSERVICES}};
|
|
my $NumberInitServices = "";
|
|
my $Service = $KM200_InitServices[$ServiceCounterInit];
|
|
my $type;
|
|
my $json ->{type} = "";
|
|
|
|
if($err ne "")
|
|
{
|
|
### Create Log entry
|
|
Log3 $name, 2, $name . " : km200_ParseHttpResponseInit - ERROR : ".$Service. ": No proper Communication with Gateway: " .$err;
|
|
|
|
### Set status of km200 fhem module
|
|
$hash->{STATE} = "ERROR - Initial Connection failed... Try to re-connect in 10s";
|
|
|
|
### Start the timer for polling again but wait 10s
|
|
InternalTimer(gettimeofday()+10, "km200_GetInitService", $hash, 1);
|
|
|
|
### Create Log entry
|
|
Log3 $name, 2, $name . " : km200_ParseHttpResponseInit - ERROR : Timer restarted to try again in 10s";
|
|
Log3 $name, 5, $name . "______________________________________________________________________________________________________________________";
|
|
return "ERROR";
|
|
}
|
|
|
|
$hash->{temp}{decodedcontent} = $data;
|
|
my $decodedContent = km200_Decrypt($hash);
|
|
|
|
### Check whether the decoded content is not empty and therefore available
|
|
if ($decodedContent ne "")
|
|
{
|
|
eval
|
|
{
|
|
$json = decode_json(encode_utf8($decodedContent));
|
|
1;
|
|
}
|
|
or do
|
|
{
|
|
Log3 $name, 4, $name. " : km200_ParseHttpResponseInit - CANNOT be parsed : ". $Service;
|
|
};
|
|
|
|
### Check whether the type is a single value containing a string or float value
|
|
if(($json -> {type} eq "stringValue") || ($json -> {type} eq "floatValue"))
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my $JsonValue = $json->{value};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - type : " .$JsonType;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value : " .$JsonValue;
|
|
|
|
### Add service to the list of responding services
|
|
push (@KM200_RespondingServices, $Service);
|
|
|
|
### Delete double entries in the list of responding services and sort in alphabetical order
|
|
my %FilteredKM200RespondingServices;
|
|
$FilteredKM200RespondingServices{$_}=0 for @KM200_RespondingServices;
|
|
@KM200_RespondingServices = (keys %FilteredKM200RespondingServices);
|
|
@KM200_RespondingServices = sort @KM200_RespondingServices;
|
|
|
|
### Save json-hash for DbLog-Split
|
|
$hash->{temp}{ServiceDbLogSplitHash} = $json;
|
|
|
|
### Write reading for fhem
|
|
readingsSingleUpdate( $hash, $JsonId, $JsonValue, 1);
|
|
|
|
### Log file entry for debugging
|
|
my $LogMessage = " : The following Service can be read";
|
|
|
|
|
|
### Check whether service is writeable and write name of service in array
|
|
if ($json->{writeable} == 1)
|
|
{
|
|
$LogMessage = $LogMessage . " and is writeable";
|
|
push (@KM200_WriteableServices, $Service);
|
|
}
|
|
else
|
|
{
|
|
# Do nothing
|
|
$LogMessage = $LogMessage . " ";
|
|
}
|
|
|
|
### Log file entry for debugging
|
|
$LogMessage = $LogMessage . " : " . $JsonId;
|
|
Log3 $name, 4, $name. $LogMessage;
|
|
}
|
|
### Check whether the type is an switchProgram
|
|
elsif ($json -> {type} eq "switchProgram")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my @JsonValues = $json->{switchPoints};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - type : " .$JsonType;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value : " .@JsonValues;
|
|
|
|
### Add service to the list of responding services
|
|
push (@KM200_RespondingServices, $Service);
|
|
|
|
### Delete double entries in the list of responding services and sort in alphabetical order
|
|
my %FilteredKM200RespondingServices;
|
|
$FilteredKM200RespondingServices{$_}=0 for @KM200_RespondingServices;
|
|
@KM200_RespondingServices = (keys %FilteredKM200RespondingServices);
|
|
@KM200_RespondingServices = sort @KM200_RespondingServices;
|
|
|
|
### Log file entry for debugging
|
|
my $LogMessage = " : The following Service can be read";
|
|
|
|
### Check whether service is writeable and write name of service in array
|
|
if ($json->{writeable} == 1)
|
|
{
|
|
$LogMessage = $LogMessage . " and is writeable";
|
|
push (@KM200_WriteableServices, $Service);
|
|
}
|
|
else
|
|
{
|
|
# Do nothing
|
|
$LogMessage = $LogMessage . " ";
|
|
}
|
|
$LogMessage = $LogMessage . " : " .$JsonId;
|
|
Log3 $name, 4, $name . $LogMessage;
|
|
|
|
### Set up variables
|
|
my $TempJsonId = "";
|
|
my $TempReadingMo = "";
|
|
my $TempReadingTu = "";
|
|
my $TempReadingWe = "";
|
|
my $TempReadingTh = "";
|
|
my $TempReadingFr = "";
|
|
my $TempReadingSa = "";
|
|
my $TempReadingSu = "";
|
|
|
|
foreach my $item (@{ $json->{switchPoints} })
|
|
{
|
|
### Create string for time and switchpoint in fixed format and write part of Reading String
|
|
my $time = $item->{time};
|
|
my $temptime = $time / 60;
|
|
my $temptimeHH = int($temptime);
|
|
my $temptimeMM = ($time - ($temptimeHH * 60));
|
|
|
|
$temptimeHH = sprintf ('%02d', $temptimeHH);
|
|
$temptimeMM = sprintf ('%02d', $temptimeMM);
|
|
$temptime = $temptimeHH . $temptimeMM;
|
|
|
|
my $tempsetpoint = $item->{setpoint};
|
|
$tempsetpoint =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(8-length($1)))/e;
|
|
my $TempReading = $temptime . " " . $tempsetpoint;
|
|
|
|
### Create ValueString for this day
|
|
if ($item->{dayOfWeek} eq "Mo")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingMo eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingMo = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingMo = $TempReadingMo . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Tu")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTu = $TempReadingTu . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "We")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingWe eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingWe = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingWe = $TempReadingWe . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Th")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTh eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTh = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTh = $TempReadingTh . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Fr")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingFr eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingFr = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingFr = $TempReadingFr . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Sa")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSa eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSa = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSa = $TempReadingSa . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Su")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSu = $TempReadingSu . " " . $TempReading;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 5, $name. "dayOfWeek of unknow day: " . $item->{dayOfWeek};
|
|
}
|
|
}
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "1-Mo";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingMo, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "2-Tu";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTu, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "3-We";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingWe, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "4-Th";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTh, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "5-Fr";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingFr, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "6-Sa";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSa, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "7-Su";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSu, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read and is writeable : " . $TempJsonId;
|
|
### Add service to the list of writeable services
|
|
push (@KM200_WriteableServices, $TempJsonId);
|
|
|
|
}
|
|
### Check whether the type is an errorlist
|
|
elsif ($json -> {type} eq "errorList")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - type : " .$JsonType;
|
|
|
|
### Add service to the list of responding services
|
|
push (@KM200_RespondingServices, $Service);
|
|
|
|
### Delete double entries in the list of responding services and sort in alphabetical order
|
|
my %FilteredKM200RespondingServices;
|
|
$FilteredKM200RespondingServices{$_}=0 for @KM200_RespondingServices;
|
|
@KM200_RespondingServices = (keys %FilteredKM200RespondingServices);
|
|
@KM200_RespondingServices = sort @KM200_RespondingServices;
|
|
|
|
### Log file entry for debugging
|
|
my $LogMessage = " : The following Service can be read";
|
|
|
|
### Check whether service is writeable and write name of service in array
|
|
if ($json->{writeable} == 1)
|
|
{
|
|
$LogMessage = $LogMessage . " and is writeable ";
|
|
push (@KM200_WriteableServices, $Service);
|
|
}
|
|
else
|
|
{
|
|
# Do nothing
|
|
$LogMessage = $LogMessage . " ";
|
|
}
|
|
### Log file entry for debugging
|
|
$LogMessage = $LogMessage . " : " . $JsonId;
|
|
Log3 $name, 4, $name . $LogMessage;
|
|
|
|
### Sort list by timestamps descending
|
|
my $TempServiceIndex = 0;
|
|
# my @TempSortedErrorList = sort { $b->{t} <=> $a->{t} } @{ $json->{values} };
|
|
my @TempSortedErrorList = sort ( @{ $json->{values} } );
|
|
|
|
|
|
foreach my $item (@TempSortedErrorList)
|
|
{
|
|
### Increment Service-Index
|
|
$TempServiceIndex++;
|
|
|
|
### Create message string with fixed blocksize
|
|
my $TempTime = $item->{t};
|
|
$TempTime =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(20-length($1)))/e;
|
|
my $TempErrorCode = $item->{dcd};
|
|
$TempErrorCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(3 -length($1)))/e;
|
|
my $TempAddCode = $item->{ccd};
|
|
$TempAddCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(4 -length($1)))/e;
|
|
my $TempClassCode = $item->{cat};
|
|
$TempClassCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(2- length($1)))/e;
|
|
my $TempErrorMessage = "Time: " . $TempTime . "-ErrorCode: " . $TempErrorCode . " -AddCode: " . $TempAddCode . " -Category: " . $TempClassCode;
|
|
|
|
### Create Service with Increment
|
|
my $TempServiceString = $Service . "/Error-" . (sprintf("%02d", $TempServiceIndex));
|
|
|
|
### Write Reading
|
|
readingsSingleUpdate( $hash, $TempServiceString, $TempErrorMessage, 1);
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service can be read : " .$TempServiceString;
|
|
}
|
|
}
|
|
### Check whether the type is an refEnum which is indicating an empty parent directory
|
|
elsif ($json -> {type} eq "refEnum")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my @JsonReferences = $json->{references};
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. " : The following Service is an empty parent directory : " . $JsonId;
|
|
|
|
### For each item found in this empty parent directory
|
|
foreach my $item (@{ $json->{references} })
|
|
{
|
|
my $SearchWord = $item->{id};
|
|
|
|
### If the Service found is listed as blocked service
|
|
if ((grep {$_ eq $SearchWord} @{$hash->{Secret}{KM200DONOTPOLL}}) == 1)
|
|
{
|
|
### Do nothing
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "The following Service has been found but is blacklisted: " . $item->{id};
|
|
}
|
|
### If the Service found is NOT listed as blocked service
|
|
else
|
|
{
|
|
### Add service to the list of all known services
|
|
push (@{$hash ->{Secret}{KM200ALLSERVICES}}, $item->{id});
|
|
}
|
|
}
|
|
### Sort the list of all services alphabetically
|
|
@{$hash ->{Secret}{KM200ALLSERVICES}} = sort @{$hash ->{Secret}{KM200ALLSERVICES}};
|
|
}
|
|
### Check whether the type is a systeminfo
|
|
elsif ($json -> {type} eq "systeminfo")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my @JsonValues = $json->{values};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - type : " .$JsonType;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value : " .@JsonValues;
|
|
|
|
### Add service to the list of responding services
|
|
push (@KM200_RespondingServices, $Service);
|
|
|
|
### Delete double entries in the list of responding services and sort in alphabetical order
|
|
my %FilteredKM200RespondingServices;
|
|
$FilteredKM200RespondingServices{$_}=0 for @KM200_RespondingServices;
|
|
@KM200_RespondingServices = (keys %FilteredKM200RespondingServices);
|
|
@KM200_RespondingServices = sort @KM200_RespondingServices;
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 4, $name . " : The following Service can be read : " .$JsonId;
|
|
|
|
### Initialise ArrayCounter
|
|
my $ArrayCounter = 0;
|
|
|
|
foreach my $ArrayItem (@{ $json->{values} })
|
|
{
|
|
### Incrementation of ArrayCounter
|
|
$ArrayCounter++;
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name . " : The ArrayItem is : " . $ArrayItem ;
|
|
Log3 $name, 5, $name . " : The keys ArrayItem is : " . (keys %{$ArrayItem}) ;
|
|
|
|
### Get array from scalar
|
|
my %ArrayHash = %{$ArrayItem};
|
|
|
|
while( my( $SystemInfoHashKey, $SystemInfoHashValue ) = each %ArrayHash )
|
|
{
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name . " : The ArrayHashKey is : " . $SystemInfoHashKey;
|
|
Log3 $name, 5, $name . " : The ArrayHashValue is : " . $SystemInfoHashValue;
|
|
|
|
### Create new Service and write reading for fhem
|
|
my $TempJsonId = $JsonId . "/" . sprintf ('%02d', $ArrayCounter) . "/" . $SystemInfoHashKey;
|
|
readingsSingleUpdate( $hash, $TempJsonId, $SystemInfoHashValue, 1);
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name . " : The following Service can be read : " . $TempJsonId;
|
|
|
|
}
|
|
}
|
|
}
|
|
### Check whether the type is unknown
|
|
else
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_ParseHttpResponseInit - type unknown for : " .$Service;
|
|
}
|
|
}
|
|
### Check whether the decoded content is empty and therefore NOT available
|
|
else
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_ParseHttpResponseInit - NOT available : ". $Service;
|
|
}
|
|
|
|
### Log entries for debugging purposes
|
|
#Log3 $name, 5, $name. " : km200_ParseHttpResponseInit : response : " .$data;
|
|
Log3 $name, 5, $name . "______________________________________________________________________________________________________________________";
|
|
### Get the size of the array
|
|
@KM200_InitServices = @{$hash ->{Secret}{KM200ALLSERVICES}};
|
|
$NumberInitServices = @KM200_InitServices;
|
|
|
|
### If the list of KM200ALLSERVICES has not been finished yet
|
|
if ($ServiceCounterInit < ($NumberInitServices-1))
|
|
{
|
|
++$ServiceCounterInit;
|
|
$hash->{temp}{ServiceCounterInit} = $ServiceCounterInit;
|
|
@{$hash->{Secret}{KM200RESPONDINGSERVICES}} = @KM200_RespondingServices;
|
|
@{$hash->{Secret}{KM200WRITEABLESERVICES}} = @KM200_WriteableServices;
|
|
km200_GetInitService($hash);
|
|
}
|
|
### If the list of KM200ALLSERVICES is finished
|
|
else
|
|
{
|
|
my @KM200_DynServices = @KM200_RespondingServices;
|
|
|
|
### Save arrays of services in hash
|
|
@{$hash->{Secret}{KM200RESPONDINGSERVICES}} = @KM200_RespondingServices;
|
|
@{$hash->{Secret}{KM200WRITEABLESERVICES}} = @KM200_WriteableServices;
|
|
@{$hash->{Secret}{KM200DYNSERVICES}} = @KM200_DynServices;
|
|
|
|
|
|
### Reset flag for initialisation
|
|
$hash->{status}{FlagInitRequest} = false;
|
|
|
|
|
|
###START###### Initiate the timer for continuous polling of dynamical values from KM200 ###################START####
|
|
InternalTimer(gettimeofday()+($hash->{INTERVALDYNVAL}), "km200_GetDynService", $hash, 1);
|
|
Log3 $name, 4, $name. " : km200 - Define: InternalTimer for dynamic values started with interval of: ".($hash->{INTERVALDYNVAL});
|
|
####END####### Initiate the timer for continuous polling of dynamical values from KM200 ####################END#####
|
|
|
|
### Reset fullResponse error message
|
|
readingsSingleUpdate( $hash, "fullResponse", "OK", 1);
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name. "Sounding and importing of services is completed";
|
|
|
|
### Set status of km200 fhem module
|
|
$hash->{STATE} = "Standby";
|
|
|
|
### Disable flag
|
|
$hash->{temp}{ServiceCounterInit} = false;
|
|
}
|
|
### If the Initialisation process has been interuppted with an error message
|
|
if (ReadingsVal($name,"fullResponse",0) eq "ERROR")
|
|
{
|
|
### Reset fullResponse error message
|
|
readingsSingleUpdate( $hash, "fullResponse", "Restarted after ERROR", 1);
|
|
|
|
### Reset timer for init procedure and start over again until it works
|
|
InternalTimer(gettimeofday()+5, "km200_GetInitService", $hash, 1);
|
|
Log3 $name, 5, $name. " : km200 - Internal timer for Initialisation of services restarted after fullResponse - error.";
|
|
}
|
|
|
|
### Clear up temporary variables
|
|
$hash->{temp}{decodedcontent} = "";
|
|
|
|
return;
|
|
}
|
|
####END####### Subroutine to download complete initial data set from gateway ###################################END#####
|
|
|
|
|
|
###START###### Subroutine obtaining dynamic services via HttpUtils ############################################START####
|
|
sub km200_GetDynService($)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my $km200_gateway_host = $hash->{URL};
|
|
my $name = $hash->{NAME};
|
|
$hash->{STATE} = "Polling";
|
|
my @KM200_DynServices = @{$hash->{Secret}{KM200DYNSERVICES}};
|
|
my $ServiceCounterDyn = $hash->{temp}{ServiceCounterDyn};
|
|
my $PollingTimeout = $hash->{POLLINGTIMEOUT};
|
|
|
|
### Stop the current timer
|
|
RemoveInternalTimer($hash);
|
|
|
|
### If at least one service to be polled is available
|
|
if (@KM200_DynServices != 0)
|
|
{
|
|
my $Service = $KM200_DynServices[$ServiceCounterDyn];
|
|
|
|
### Log file entry for debugging
|
|
if ($ServiceCounterDyn == 0)
|
|
{
|
|
Log3 $name, 5, $name. "Starting download of dynamic services";
|
|
}
|
|
|
|
### Log file entry for debugging
|
|
Log3 $name, 5, $name . " - km200_GetDynService - Polling : " . $Service;
|
|
|
|
my $url = "http://" . $km200_gateway_host . $Service;
|
|
my $param = {
|
|
url => $url,
|
|
timeout => $PollingTimeout,
|
|
hash => $hash,
|
|
method => "GET",
|
|
header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json",
|
|
callback => \&km200_ParseHttpResponseDyn
|
|
};
|
|
|
|
### Set Status Flag in order state running dynamic request
|
|
$hash->{status}{FlagDynRequest} = true;
|
|
|
|
### Get data
|
|
HttpUtils_NonblockingGet($param);
|
|
}
|
|
### If no service to be polled is available
|
|
else
|
|
{
|
|
Log3 $name, 5, $name . " : No dynamic values available to be read. Skipping download.";
|
|
}
|
|
}
|
|
####END####### Subroutine get dynamic data value ###############################################################END#####
|
|
|
|
###START###### Subroutine to download complete dynamic data set from gateway ##################################START####
|
|
# For all responding dynamic services read the respective values from gateway
|
|
sub km200_ParseHttpResponseDyn($)
|
|
{
|
|
my ($param, $err, $data) = @_;
|
|
my $hash = $param->{hash};
|
|
my $name = $hash ->{NAME};
|
|
my $ServiceCounterDyn = $hash ->{temp}{ServiceCounterDyn};
|
|
my @KM200_DynServices = @{$hash ->{Secret}{KM200DYNSERVICES}};
|
|
my $NumberDynServices = @KM200_DynServices;
|
|
my $Service = $KM200_DynServices[$ServiceCounterDyn];
|
|
my $type;
|
|
my $json ->{type} = "";
|
|
|
|
Log3 $name, 5, $name. " : Parsing response of dynamic service received for : " . $Service;
|
|
|
|
### Reset Status Flag
|
|
$hash->{status}{FlagDynRequest} = false;
|
|
|
|
if($err ne "")
|
|
{
|
|
Log3 $name, 2, $name . " : ERROR: Service: ".$Service. ": No proper Communication with Gateway: " .$err;
|
|
readingsSingleUpdate($hash, "fullResponse", "ERROR", 1);
|
|
}
|
|
|
|
$hash->{temp}{decodedcontent} = $data;
|
|
my $decodedContent = km200_Decrypt($hash);
|
|
|
|
if ($decodedContent ne "")
|
|
{
|
|
eval
|
|
{
|
|
$json = decode_json(encode_utf8($decodedContent));
|
|
1;
|
|
}
|
|
or do
|
|
{
|
|
Log3 $name, 5, $name. " - km200_parseHttpResponseDyn : Data cannot be parsed by JSON on km200 for http://" . $param->{url};
|
|
};
|
|
|
|
### Check whether the type is a single value containing a string or float value
|
|
if(($json -> {type} eq "stringValue") || ($json -> {type} eq "floatValue"))
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my $JsonValue = $json->{value};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_parseHttpResponseDyn - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - type : " .$JsonType;
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - value : " .$JsonValue;
|
|
### Log entries for debugging purposes
|
|
|
|
### Save json-hash for DbLog-Split
|
|
$hash->{temp}{ServiceDbLogSplitHash} = $json;
|
|
### Save json-hash for DbLog-Split
|
|
|
|
### Write reading
|
|
readingsSingleUpdate( $hash, $JsonId, $JsonValue, 1);
|
|
### Write reading
|
|
}
|
|
### Check whether the type is an switchProgram
|
|
elsif ($json -> {type} eq "switchProgram")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_parseHttpResponseDyn - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - type : " .$JsonType;
|
|
|
|
### Set up variables
|
|
my $TempJsonId = "";
|
|
my $TempReadingMo = "";
|
|
my $TempReadingTu = "";
|
|
my $TempReadingWe = "";
|
|
my $TempReadingTh = "";
|
|
my $TempReadingFr = "";
|
|
my $TempReadingSa = "";
|
|
my $TempReadingSu = "";
|
|
|
|
foreach my $item (@{ $json->{switchPoints} })
|
|
{
|
|
### Create string for time and switchpoint in fixed format and write part of Reading String
|
|
my $time = $item->{time};
|
|
my $temptime = $time / 60;
|
|
my $temptimeHH = int($temptime);
|
|
my $temptimeMM = ($time - ($temptimeHH * 60));
|
|
|
|
$temptimeHH = sprintf ('%02d', $temptimeHH);
|
|
$temptimeMM = sprintf ('%02d', $temptimeMM);
|
|
$temptime = $temptimeHH . $temptimeMM;
|
|
|
|
my $tempsetpoint = $item->{setpoint};
|
|
$tempsetpoint =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(8-length($1)))/e;
|
|
my $TempReading = $temptime . " " . $tempsetpoint;
|
|
|
|
### Create ValueString for this day
|
|
if ($item->{dayOfWeek} eq "Mo")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingMo eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingMo = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingMo = $TempReadingMo . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Tu")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTu = $TempReadingTu . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "We")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingWe eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingWe = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingWe = $TempReadingWe . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Th")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingTh eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingTh = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingTh = $TempReadingTh . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Fr")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingFr eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingFr = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingFr = $TempReadingFr . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Sa")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSa eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSa = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSa = $TempReadingSa . " " . $TempReading;
|
|
}
|
|
}
|
|
elsif ($item->{dayOfWeek} eq "Su")
|
|
{
|
|
### If it is the first entry for this day
|
|
if ($TempReadingSu eq "")
|
|
{
|
|
### Write the first entry
|
|
$TempReadingSu = $TempReading;
|
|
}
|
|
### If it is NOT the first entry for this day
|
|
else
|
|
{
|
|
### Add the next entry
|
|
$TempReadingSu = $TempReadingSu . " " . $TempReading;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 5, $name. "dayOfWeek of unknow day: " . $item->{dayOfWeek};
|
|
}
|
|
}
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "1-Mo";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingMo, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "2-Tu";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTu, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "3-We";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingWe, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "4-Th";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingTh, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "5-Fr";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingFr, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "6-Sa";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSa, 1);
|
|
|
|
### Create new Service and write reading for fhem
|
|
$TempJsonId = $JsonId . "/" . "7-Su";
|
|
readingsSingleUpdate( $hash, $TempJsonId, $TempReadingSu, 1);
|
|
}
|
|
### Check whether the type is an errorlist
|
|
elsif ($json -> {type} eq "errorList")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my $TempServiceIndex = 0;
|
|
|
|
### Sort list by timestamps descending
|
|
# my @TempSortedErrorList = sort { $b->{t} <=> $a->{t} } @{ $json->{values} };
|
|
my @TempSortedErrorList = sort ( @{ $json->{values} } );
|
|
|
|
|
|
### For every notification do
|
|
foreach my $item (@TempSortedErrorList)
|
|
{
|
|
### Increment Service-Index
|
|
$TempServiceIndex++;
|
|
|
|
### Create message string with fixed blocksize
|
|
my $TempTime = $item->{t};
|
|
$TempTime =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(20-length($1)))/e;
|
|
my $TempErrorCode = $item->{dcd};
|
|
$TempErrorCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(3 -length($1)))/e;
|
|
my $TempAddCode = $item->{ccd};
|
|
$TempAddCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(4 -length($1)))/e;
|
|
my $TempClassCode = $item->{cat};
|
|
$TempClassCode =~ s/^(.+)$/sprintf("%s%s", $1, ' 'x(2- length($1)))/e;
|
|
my $TempErrorMessage = "Time: " . $TempTime . "-ErrorCode: " . $TempErrorCode . " -AddCode: " . $TempAddCode . " -Category: " . $TempClassCode;
|
|
|
|
### Create Service with Increment and leading 0
|
|
my $TempServiceString = $Service . "/Error-" . (sprintf("%02d", $TempServiceIndex));
|
|
|
|
### Write Reading
|
|
readingsSingleUpdate( $hash, $TempServiceString, $TempErrorMessage, 1);
|
|
}
|
|
}
|
|
### Check whether the type is a systeminfo
|
|
elsif ($json -> {type} eq "systeminfo")
|
|
{
|
|
my $JsonId = $json->{id};
|
|
my $JsonType = $json->{type};
|
|
my @JsonValues = $json->{values};
|
|
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - value found for : " .$Service;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - id : " .$JsonId;
|
|
Log3 $name, 5, $name. " : km200_ParseHttpResponseInit - type : " .$JsonType;
|
|
|
|
### Initialise ArrayCounter
|
|
my $ArrayCounter = 0;
|
|
|
|
foreach my $ArrayItem (@{ $json->{values} })
|
|
{
|
|
### Incrementation of ArrayCounter
|
|
$ArrayCounter++;
|
|
|
|
|
|
### Get array from scalar
|
|
my %ArrayHash = %{$ArrayItem};
|
|
|
|
while( my( $SystemInfoHashKey, $SystemInfoHashValue ) = each %ArrayHash )
|
|
{
|
|
### Create new Service and write reading for fhem
|
|
my $TempJsonId = $JsonId . "/" . sprintf ('%02d', $ArrayCounter) . "/" . $SystemInfoHashKey;
|
|
readingsSingleUpdate( $hash, $TempJsonId, $SystemInfoHashValue, 1);
|
|
}
|
|
}
|
|
}
|
|
### Check whether the type is unknown
|
|
else
|
|
{
|
|
### Log entries for debugging purposes
|
|
Log3 $name, 4, $name. " : km200_parseHttpResponseDyn - type unknown for : " .$Service;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log3 $name, 5, $name. " : km200_parseHttpResponseDyn - Data not available on km200 for http://" . $param->{url};
|
|
}
|
|
|
|
Log3 $name, 5, $name . "______________________________________________________________________________________________________________________";
|
|
### Clear up temporary variables
|
|
$hash->{temp}{decodedcontent} = "";
|
|
$hash->{temp}{service} = "";
|
|
### Clear up temporary variables
|
|
|
|
### If list is not complete yet
|
|
if ($ServiceCounterDyn < ($NumberDynServices-1))
|
|
{
|
|
++$ServiceCounterDyn;
|
|
$hash->{temp}{ServiceCounterDyn} = $ServiceCounterDyn;
|
|
km200_GetDynService($hash);
|
|
}
|
|
### If list is complete
|
|
else
|
|
{
|
|
$hash->{STATE} = "Standby";
|
|
$hash->{temp}{ServiceCounterDyn} = 0;
|
|
|
|
###START###### Re-Start the timer #####################################START####
|
|
InternalTimer(gettimeofday()+$hash->{INTERVALDYNVAL}, "km200_GetDynService", $hash, 1);
|
|
####END####### Re-Start the timer ######################################END#####
|
|
|
|
### Update fullResponse Reading
|
|
readingsSingleUpdate( $hash, "fullResponse", "OK", 1);
|
|
|
|
$hash->{status}{FlagDynRequest} = false;
|
|
|
|
Log3 $name, 5, $name . "______________________________________________________________________________________________________________________";
|
|
}
|
|
return undef;
|
|
}
|
|
####END####### Subroutine to download complete dynamic data set from gateway ###################################END#####
|
|
|
|
1;
|
|
|
|
###START###### Description for fhem commandref ################################################################START####
|
|
=pod
|
|
=item device
|
|
=item summary Connects fhem to Buderus KM300, KM200, KM100, KM50
|
|
=item summary_DE Verbindet fhem mit Buderus KM300, KM200, KM100, KM50
|
|
|
|
|
|
=begin html
|
|
|
|
<a name="km200"></a>
|
|
<h3>KM200</h3>
|
|
<ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
The Buderus <a href="https://www.buderus.de/de/produkte/catalogue/alle-produkte/7719_Gateway-Logamatic-web-KM200-KM100-KM50">KM200, KM100 or KM50 (hereafter described as KMxxx)</a> is a communication device to establish a connection between the Buderus central heating control unit and the internet.<BR>
|
|
It has been designed in order to allow the inhabitants accessing their heating system via his Buderus App <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbrauch
|
|
er/EasyControl-4848514.html"> EasyControl</a>.<BR>
|
|
Furthermore it allows the maintenance companies to access the central heating control system to read and change settings.<BR>
|
|
The km200 fhem-module enables read/write access to these parameters.<BR>
|
|
<BR>
|
|
In order to use the KMxxx with fhem, you must define the private password with the Buderus App <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbraucher/EasyControl-4848514.html"> EasyControl</a> first.<BR>
|
|
<BR>
|
|
<b><u>Remark:</u></b><BR>
|
|
Despite the instruction of the Buderus KMxxx Installation guide, the ports 5222 and 5223 should not be opened and allow access to the KMxxx module from outside.<BR>
|
|
You should configure (or leave) your internet router with the respective settings.<BR>
|
|
If you want to read or change settings on the heating system, you should access the central heating control system via your fhem system only.<BR>
|
|
<BR>
|
|
As soon the module has been defined within the fhem.cfg, the module is trying to obtain all known/possible services. <BR>
|
|
After this initial contact, the module differs between a set of continuous (dynamically) changing values (e.g.: temperatures) and not changing static values (e.g.: Firmware version).<BR>
|
|
This two different set of values can be bound to an individual polling interval. Refer to <a href="#KM200Attr">Attributes</a><BR>
|
|
<BR>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200define"></a><b>Define</b></td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>define <name> km200 <IPv4-address> <GatewayPassword> <PrivatePassword></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr><td><code><name></code> : </td><td>The name of the device. Recommendation: "myKm200".</td></tr>
|
|
<tr><td><code><IPv4-address></code> : </td><td>A valid IPv4 address of the KMxxx. You might look into your router which DHCP address has been given to the KMxxx.</td></tr>
|
|
<tr><td><code><GatewayPassword></code> : </td><td>The gateway password which is provided on the type sign of the KMxxx.</td></tr>
|
|
<tr><td><code><PrivatePassword></code> : </td><td>The private password which has been defined by the user via <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbraucher/EasyControl-4848514.html"> EasyControl</a>.</td></tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Set"></a><b>Set</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
The set function is able to change a value of a service which has the "writeable" - tag within the KMxxx service structure.<BR>
|
|
Most of those values have an additional list of allowed values which are the only ones to be set.<BR>
|
|
Other floatable type values can be changed only within their range of minimum and maximum value.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>set <service> <value></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr><td><code><service></code> : </td><td>The name of the service which value shall be set. E.g.: "<code>/heatingCircuits/hc1/operationMode</code>"<BR></td></tr>
|
|
<tr><td><code><value></code> : </td><td>A valid value for this service.<BR></td></tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Get"></a><b>Get</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
The get function is able to obtain a value of a service within the KMxxx service structure.<BR>
|
|
The additional list of allowed values or their range of minimum and maximum value will not be handed back.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>get <service> <option></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td><code><service></code> : </td><td>The name of the service which value shall be obtained. E.g.: "<code>/heatingCircuits/hc1/operationMode</code>"<BR>
|
|
It returns only the value but not the unit or the range or list of allowed values possible.<BR>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td><code><option></code> : </td><td>The optional Argument for the result of the get-command e.g.: "<code>json</code>"<BR>
|
|
The following options are available:<BR>
|
|
json - Returns the raw json-answer from the KMxxx as string.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Attr"></a><b>Attributes</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
The following user attributes can be used with the km200 module in addition to the global ones e.g. <a href="#room">room</a>.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>IntervalDynVal</code> : </li></td><td>A valid polling interval for the dynamically changing values of the KMxxx. The value must be >=20s to allow the km200 module to perform a full polling procedure. <BR>
|
|
The default value is 300s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>PollingTimeout</code> : </li></td><td>A valid time in order to allow the module to wait for a response of the KMxxx. Usually this value does not need to be changed but might in case of slow network or slow response.<BR>
|
|
The default and minimum value is 5s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>DoNotPoll</code> : </li></td><td>A list of services separated by blanks which shall not be downloaded due to repeatable crashes or irrelevant values.<BR>
|
|
The list can be filled with the name of the top - hierarchy service, which means everything below that service will also be ignored.<BR>
|
|
The default value (empty) therefore nothing will be ignored.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>ReadBackDelay</code> : </li></td><td>A valid time in milliseconds [ms] for the delay between writing and re-reading of values after using the "set" - command. The value must be >=0ms.<BR>
|
|
The default value is 100 = 100ms = 0,1s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>disable</code> : </li></td><td>Stops the device from further pollings and deletes the existing readings.<BR>
|
|
The default value is 0 = activated<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
</ul>
|
|
=end html
|
|
|
|
|
|
=begin html_DE
|
|
|
|
<a name="km200"></a>
|
|
<h3>KM200</h3>
|
|
<ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
Das Buderus <a href="https://www.buderus.de/de/produkte/catalogue/alle-produkte/7719_Gateway-Logamatic-web-KM200-KM100-KM50">KM200, KM100 or KM50 (ab hier als KMxxx beschrieben)</a> ist eine Schnittstelle zwischen der Buderus Zentralheizungssteuerung un dem Internet.<BR>
|
|
Es wurde entwickelt um den Bewohnern den Zugang zu Ihrem Heizungssystem durch die Buderus App <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbraucher/EasyControl-4848514.html"> EasyControl zu erlauben.</a>.<BR>
|
|
Darüber hinaus erlaubt es nach vorheriger Freigabe dem Heizungs- bzw. Wartungsbetrieb die Heizungsanlage von aussen zu warten und Werte zu verändern.<BR>
|
|
Das km200 fhem-Modul erlaubt den Lese-/Schreibzugriff dieser Parameter durch fhem.<BR>
|
|
<BR>
|
|
Um das KMxxx Gerät mit fhem nutzen zu können, muß zunächst ein privates Passwort mit der Buderus Buderus App <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbraucher/EasyControl-4848514.html"> EasyControl</a> - App gesetzt werden.<BR>
|
|
<BR>
|
|
<b><u>Anmerkung:</u></b><BR>
|
|
Unabhängig der Installationsanleitung des Buderus KMxxx Geräts, sollten die Ports 5222 und 5223 am Router geschlossen bleiben um keinen Zugriff von außen auf das Gerät zu erlauben.<BR>
|
|
Der Router sollte entsprechend Konfiguriert bzw. so belassen werden.<BR>
|
|
Wenn der Lese-/Schreibzugriff von aussen gewünscht ist, so sollte man ausschließlich über das fhem-System auf die Zentralheizung zugreifen.<BR>
|
|
<BR>
|
|
Sobald das Modul in der fhem.cfg definiert ist, wird das Modul versuchen alle bekannten Services abzuklopfen ob diese in der angeschlossenen Konstellation überhaupt vorhanden sind.<BR>
|
|
Nach diesem Initial-Kontakt unterscheidet das Modul zwisachen einem Satz an Services die sich ständig (dynamisch) ändern (z.B.: Vorlauftemperatur) sowie sich nicht ständig (statisch) ändernden Werten (z.B.: Firmware Version).<BR>
|
|
Diese beiden Sätze an Services können mir einem individuellen Abfrageintervall versehen werden. Siehe <a href="#KM200Attr">Attributes</a><BR>
|
|
<BR>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200define"></a><b>Define</b></td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>define <name> km200 <IPv4-address> <GatewayPassword> <PrivatePassword></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr><td><code><name></code> : </td><td>Der Name des Gerätes. Empfehlung: "myKm200".</td></tr>
|
|
<tr><td><code><IPv4-address></code> : </td><td>Eine gültige IPv4 Adresse des KM200. Eventuell im Router nachschauen welche DHCP - Addresse dem KM200/KM50 vergeben wurde.</td></tr>
|
|
<tr><td><code><GatewayPassword></code> : </td><td>Das gateway Passwort, welches auf dem Typenschild des KM200/KM50 zu finden ist.</td></tr>
|
|
<tr><td><code><PrivatePassword></code> : </td><td>Das private Passwort, welches durch den User mit Hilfe der <a href="http://www.buderus.de/Online_Anwendungen/Apps/fuer_den_Endverbraucher/EasyControl-4848514.html"> EasyControl</a> - App vergeben wurde.</td></tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Set"></a><b>Set</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
Die set Funktion ändert die Werte der Services welche das Flag "schreibbar" innerhalb der KMxxx Service Struktur besitzen.<BR>
|
|
Die meisten dieser beschreibbaren Werte haben eine exklusive Liste von möglichen Werten innerhalb dessen sich der neue Wert bewegen muss.<BR>
|
|
Andere Fließkomma Werte haben einen maximum und minumum Wert, in dessen sich der neue Wert bewegen muß.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>set <service> <value></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr><td><code><service></code> : </td><td>Der Name des Service welcher gesetzt werden soll. Z.B.: "<code>/heatingCircuits/hc1/operationMode</code>"<BR></td></tr>
|
|
<tr><td><code><value></code> : </td><td>Ein gültiger Wert für diesen Service.<BR></td></tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Get"></a><b>Get</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
Die get-Funktion ist in der Lage einen Wert eines Service innerhalb der KMxxx Service Struktur auszulesen.<BR>
|
|
Die zusätzliche Liste von erlaubten Werten oder der Wertebereich zwischen Minimum und Maximum wird nicht zurück gegeben.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<table><tr><td><ul><code>get <service> <option></code></ul></td></tr></table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td><code><service></code> : </td><td>Der Name des Service welcher ausgelesen werden soll. Z.B.: "<code>/heatingCircuits/hc1/operationMode</code>"<BR>
|
|
Es gibt nur den Wert, aber nicht die Werteliste oder den möglichen Wertebereich zurück.<BR>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td><code><option></code> : </td><td>Das optionelle Argument für Ausgabe des get-Befehls Z.B.: "<code>json</code>"<BR>
|
|
Folgende Optionen sind verfügbar:<BR>
|
|
json - Gibt anstelle des Wertes, die gesamte Json Antwort des KMxxx als String zurück
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<BR>
|
|
|
|
<table>
|
|
<tr><td><a name="KM200Attr"></a><b>Attributes</b></td></tr>
|
|
<tr><td>
|
|
<ul>
|
|
Die folgenden Modul-spezifischen Attribute können neben den bekannten globalen Attributen gesetzt werden wie z.B.: <a href="#room">room</a>.<BR>
|
|
</ul>
|
|
</td></tr>
|
|
</table>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>IntervalDynVal</code> : </li></td><td>Ein gültiges Abfrageintervall für die sich ständig verändernden - dynamischen Werte der KMxxx Services. Der Wert muss größer gleich >=20s sein um dem Modul genügend Zeit einzuräumen eine volle Abfrage auszuführen bevor die nächste Abfrage startet.<BR>
|
|
Der Default-Wert ist 300s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>PollingTimeout</code> : </li></td><td>Ein gültiger Zeitwert um dem KMxxx genügend Zeit zur Antwort einzelner Werte einzuräumen. Normalerweise braucht dieser Wert nicht verändert werden, muss jedoch im Falle eines langsamen Netzwerks erhöht werden<BR>
|
|
Der Default-Wert ist 5s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>DoNotPoll</code> : </li></td><td>Eine durch Leerzeichen (Blank) getrennte Liste von Services welche von der Abfrage aufgrund irrelevanter Werte oder fhem - Abstürzen ausgenommen werden sollen.<BR>
|
|
Die Liste kann auch Hierarchien von services enthalten. Dies bedeutet, das alle Services unterhalb dieses Services ebenfalls gelöscht werden.<BR>
|
|
Der Default Wert ist (empty) somit werden alle bekannten Services abgefragt.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>ReadBackDelay</code> : </li></td><td>Ein gültiger Zeitwert in Mllisekunden [ms] für die Pause zwischen schreiben und zurücklesen des Wertes durch den "set" - Befehl. Der Wert muss >=0ms sein.<BR>
|
|
Der Default-Wert ist 100 = 100ms = 0,1s.<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
<ul><ul>
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
<tr><td><li><code>disable</code> : </li></td><td>Deaktiviert das Device und löscht alle bestehenden Readings.<BR>
|
|
Der Default-Wert ist 0 = aktiviert<BR>
|
|
</td></tr>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</ul></ul>
|
|
|
|
</ul>
|
|
=end html_DE |