############################################################################### # # Developed with Kate # # (c) 2015-2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com) # All rights reserved # # Special thanks goes to comitters: # - Jens Wohlgemuth Thanks for Commandref # - Schlimbo Tasker integration and Tasker Commandref # # # This script 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 # any later version. # # The GNU General Public License can be found at # http://www.gnu.org/copyleft/gpl.html. # A copy is found in the textfile GPL.txt and important notices to the license # from the author is found in LICENSE.txt distributed with these scripts. # # This script 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. # # # $Id$ # ############################################################################### ## ## ## Das JSON Modul immer in einem eval aufrufen # $data = eval{decode_json($data)}; # # if($@){ # Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); # # readingsSingleUpdate($hash, "state", "error", 1); # # return; # } # # ###### Möglicher Aufbau eines JSON Strings für die AMADCommBridge # # first initial String # {"amad": {"amad_id": "1495827100156","fhemcmd": "setreading"},"firstrun": {"fhemdevice": "TabletWohnzimmer","fhemserverip": "fhem02.tuxnet.local","amaddevice_ip": "10.6.9.35"}} # {"amad": {"amad_id": "1495827100156","fhemcmd": "setreading"},"firstrun": {"fhemdevice": "TabletWohnzimmer","fhemserverip": "fhem02.tuxnet.local","amaddevice_ip": "10.6.9.35"}} # # default String # {"amad": {"amad_id": "37836534","fhemcmd": "setreading"},"payload": {"reading0": "value0","reading1": "value1","readingX": "valueX"}} # Aufruf zum testens # curl --data '{"amad": {"amad_id": "37836534","fhemcmd": "setreading"},"payload": {"reading0": "value0","reading1": "value1","readingX": "valueX"}}' localhost:8090 # # ## ## package FHEM::AMADCommBridge; use strict; use warnings; use POSIX; use FHEM::Meta; use GPUtils qw(GP_Import GP_Export); use HttpUtils; use TcpServerUtils; my $missingModul = ''; eval "use Encode qw(encode encode_utf8);1" or $missingModul .= 'Encode '; # try to use JSON::MaybeXS wrapper # for chance of better performance + open code eval { require JSON::MaybeXS; import JSON::MaybeXS qw( decode_json encode_json ); 1; }; if ($@) { $@ = undef; # try to use JSON wrapper # for chance of better performance eval { # JSON preference order local $ENV{PERL_JSON_BACKEND} = 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); require JSON; import JSON qw( decode_json encode_json ); 1; }; if ($@) { $@ = undef; # In rare cases, Cpanel::JSON::XS may # be installed but JSON|JSON::MaybeXS not ... eval { require Cpanel::JSON::XS; import Cpanel::JSON::XS qw(decode_json encode_json); 1; }; if ($@) { $@ = undef; # In rare cases, JSON::XS may # be installed but JSON not ... eval { require JSON::XS; import JSON::XS qw(decode_json encode_json); 1; }; if ($@) { $@ = undef; # Fallback to built-in JSON which SHOULD # be available since 5.014 ... eval { require JSON::PP; import JSON::PP qw(decode_json encode_json); 1; }; if ($@) { $@ = undef; # Fallback to JSON::backportPP in really rare cases require JSON::backportPP; import JSON::backportPP qw(decode_json encode_json); 1; } } } } } ## Import der FHEM Funktionen #-- Run before package compilation BEGIN { # Import from main context GP_Import( qw(readingsSingleUpdate readingsBulkUpdate readingsBeginUpdate readingsEndUpdate defs modules Log3 CommandAttr CommandDelete CommandSet readingFnAttributes attr AttrVal ReadingsVal init_done urlEncode Dispatch HttpUtils_NonblockingGet TcpServer_Open TcpServer_Close TcpServer_Accept AnalyzeCommandChain AnalyzePerlCommand) ); } #-- Export to main context with different name GP_Export( qw( Initialize ) ); sub Initialize($) { my ($hash) = @_; # Provider $hash->{ReadFn} = 'FHEM::AMADCommBridge::Read'; $hash->{WriteFn} = 'FHEM::AMADCommBridge::Write'; $hash->{Clients} = ':AMADDevice:'; $hash->{MatchList} = { "1:AMADDevice" => '{"amad": \{"amad_id":.+}}' }; # Consumer $hash->{SetFn} = 'FHEM::AMADCommBridge::Set'; $hash->{DefFn} = 'FHEM::AMADCommBridge::Define'; $hash->{UndefFn} = 'FHEM::AMADCommBridge::Undef'; $hash->{AttrFn} = 'FHEM::AMADCommBridge::Attr'; $hash->{AttrList} = 'fhemControlMode:trigger,setControl,thirdPartControl ' . 'debugJSON:0,1 ' . 'enableSubCalls:0,1 ' . 'disable:1 ' . 'allowfrom ' . 'fhemServerIP ' . $readingFnAttributes; return FHEM::Meta::InitMod( __FILE__, $hash ); } sub Define($$) { my ( $hash, $def ) = @_; my @a = split( '[ \t][ \t]*', $def ); return $@ unless ( FHEM::Meta::SetInternals($hash) ); use version 0.44; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); return 'too few parameters: define AMADCommBridge ' if ( @a < 2 and @a > 3 ); return 'Cannot define a AMADCommBridge device. Perl modul ' . $missingModul . ' is missing.' if ($missingModul); my $name = $a[0]; my $port; $port = $a[2] if ( $a[2] ); $port = 8090 if ( not defined($port) and ( !$port ) ); $hash->{BRIDGE} = 1; $hash->{PORT} = $port; $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{VERSIONFLOWSET} = FHEM::Meta::Get( $hash, 'x_flowsetversion' ); CommandAttr( undef, $name . ' room AMAD' ) if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); Log3( $name, 3, "AMADCommBridge ($name) - defined AMADCommBridge with Socketport $port" ); Open($hash); $modules{AMADCommBridge}{defptr}{BRIDGE} = $hash; return undef; } sub Undef($$) { my ( $hash, $arg ) = @_; TcpServer_Close($hash); delete $modules{AMADCommBridge}{defptr}{BRIDGE} if ( defined( $modules{AMADCommBridge}{defptr}{BRIDGE} ) and $hash->{BRIDGE} ); return undef; } sub Attr(@) { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; my $orig = $attrVal; if ( $attrName eq 'disable' ) { if ( $cmd eq 'set' ) { if ( $attrVal == 0 ) { readingsSingleUpdate( $hash, 'state', 'enabled', 1 ); Open($hash); Log3( $name, 3, "AMADCommBridge ($name) - enabled" ); } else { Close($hash); readingsSingleUpdate( $hash, 'state', 'disabled', 1 ) if ( not defined( $hash->{FD} ) ); Log3( $name, 3, "AMADCommBridge ($name) - disabled" ); } } else { readingsSingleUpdate( $hash, 'state', 'enabled', 1 ); Open($hash); Log3( $name, 3, "AMADCommBridge ($name) - enabled" ); } } elsif ( $attrName eq 'fhemControlMode' ) { if ( $cmd eq 'set' ) { CommandSet( undef, 'set TYPE=AMADDevice:FILTER=deviceState=online statusRequest' ); Log3( $name, 3, "AMADCommBridge ($name) - set fhemControlMode global Variable at Device" ); } else { CommandSet( undef, 'set TYPE=AMADDevice:FILTER=deviceState=online statusRequest' ); Log3( $name, 3, "AMADCommBridge ($name) - set fhemControlMode global Variable NONE at Device" ); } } return undef; } sub Set($@) { my ( $hash, $name, $cmd, @args ) = @_; my ( $arg, @params ) = @args; if ( $cmd eq 'open' ) { Open($hash); } elsif ( $cmd eq 'close' ) { Close($hash); } else { my $list = 'open:noArg close:noArg'; return "Unknown argument $cmd, choose one of $list"; } } sub Write($@) { my ( $hash, $amad_id, $uri, $path, $header, $method ) = @_; my $name = $hash->{NAME}; my $dhash = $modules{AMADDevice}{defptr}{$amad_id}; my $param; my $remoteServer = AttrVal( $dhash->{NAME}, 'remoteServer', 'Automagic' ); Log3( $name, 4, "AMADCommBridge ($name) - Write Path: $path" ); if ( $remoteServer ne 'Automagic' and $path =~ /\?/ ) { $path .= '&amad_id=' . $amad_id; } elsif ( $remoteServer ne 'Automagic' ) { $path .= '?amad_id=' . $amad_id; } return readingsSingleUpdate( $dhash, 'lastSetCommand', $path, 1 ) if ( $remoteServer eq 'other' ); $param = { url => 'http://' . $uri . $path, timeout => 15, hash => $hash, amad_id => $amad_id, method => $method, header => $header . "\r\namadid: $amad_id", doTrigger => 1, callback => \&ErrorHandling } if ( $remoteServer eq 'Automagic' ); $param = { url => 'http://' . $uri . '/', data => "{\"message\":\"AMAD=:=$path\", \"sender\":\"AMAD\", \"ttl\":60, \"communication_base_params\":{\"type\":\"Message\", \"fallback\":false, \"via\":\"Wifi\"},\"version\":\"1.62\"}", timeout => 15, hash => $hash, amad_id => $amad_id, method => $method, header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json", doTrigger => 1, callback => \&ErrorHandling } if ( $remoteServer eq 'Autoremote' ); $param = { url => 'http://' . $uri . '/', data => 'device=AMAD&cmd=' . urlEncode($path), timeout => 15, hash => $hash, amad_id => $amad_id, method => $method, header => "agent: TeleHeater/2.2.3\r\nUser-Agent: TeleHeater/2.2.3\r\nAccept: application/json", doTrigger => 1, callback => \&ErrorHandling } if ( $remoteServer eq 'TNES' ); my $logtext = "AMADCommBridge ($name) - Send with remoteServer: $remoteServer URL: $param->{url}, HEADER: $param->{header}, METHOD: $method"; $logtext .= ", DATA: $param->{data}" if ( $remoteServer ne 'Automagic' ); Log3( $name, 5, "$logtext" ); HttpUtils_NonblockingGet($param) if ( defined($param) ); } sub ErrorHandling($$$) { my ( $param, $err, $data ) = @_; my $hash = $param->{hash}; my $dhash = $modules{AMADDevice}{defptr}{ $param->{'amad_id'} }; my $dname = $dhash->{NAME}; if ( $param->{method} eq 'GET' ) { ### Begin Error Handling if ( $dhash->{helper}{infoErrorCounter} > 0 ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'lastStatusRequestState', 'statusRequest_error', 1 ); if ( ReadingsVal( $dname, 'flow_Informations', 'active' ) eq 'inactive' && ReadingsVal( $dname, 'flow_SetCommands', 'active' ) eq 'inactive' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: CHECK THE LAST ERROR READINGS FOR MORE INFO, DEVICE IS SET OFFLINE" ); readingsBulkUpdate( $dhash, 'deviceState', 'offline', 1 ); readingsBulkUpdate( $dhash, 'state', 'AMAD Flows inactive, device set offline', 1 ); } elsif ($dhash->{helper}{infoErrorCounter} > 7 && $dhash->{helper}{setCmdErrorCounter} > 4 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: UNKNOWN ERROR, PLEASE CONTACT THE DEVELOPER, DEVICE DISABLED" ); $attr{$dname}{disable} = 1; readingsBulkUpdate( $dhash, 'state', 'Unknown Error, device disabled', 1 ); $dhash->{helper}{infoErrorCounter} = 0; $dhash->{helper}{setCmdErrorCounter} = 0; return; } elsif ( ReadingsVal( $dname, 'flow_Informations', 'active' ) eq 'inactive' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: Informations Flow on your Device is inactive, will try to reactivate" ); } elsif ( $dhash->{helper}{infoErrorCounter} > 7 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: To many Errors please check your Network or Device Configuration, DEVICE IS SET OFFLINE" ); readingsBulkUpdate( $dhash, 'deviceState', 'offline', 1 ); readingsBulkUpdate( $dhash, 'state', 'To many Errors, device set offline', 1 ); $dhash->{helper}{infoErrorCounter} = 0; } elsif ( $dhash->{helper}{infoErrorCounter} > 2 && ReadingsVal( $dname, 'flow_Informations', 'active' ) eq 'active' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: Please check the AutomagicAPP on your Device" ); } readingsEndUpdate( $dhash, 1 ); } if ( defined($err) ) { if ( $err ne '' ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $err ) if ( ReadingsVal( $dname, 'state', 1 ) ne 'initialized' ); $dhash->{helper}{infoErrorCounter} = ( $dhash->{helper}{infoErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastStatusRequestState', 'statusRequest_error', 1 ); if ( $err =~ /timed out/ ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: connect to your device is timed out. check network" ); } elsif ( ( $err =~ /Keine Route zum Zielrechner/ ) && $dhash->{helper}{infoErrorCounter} > 1 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: no route to target. bad network configuration or network is down" ); } else { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: $err" ); } readingsEndUpdate( $dhash, 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: statusRequestErrorHandling: error while requesting AutomagicInfo: $err" ); return; } } if ( $data eq '' and exists( $param->{code} ) && $param->{code} ne 200 ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $param->{code}, 1 ) if ( ReadingsVal( $dname, 'state', 1 ) ne 'initialized' ); $dhash->{helper}{infoErrorCounter} = ( $dhash->{helper}{infoErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastStatusRequestState', 'statusRequest_error', 1 ); if ( $param->{code} ne 200 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: " . $param->{code} ); } readingsEndUpdate( $dhash, 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: received http code " . $param->{code} . " without any data after requesting AMAD AutomagicInfo" ); return; } if ( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $param->{code}, 1 ) if ( ReadingsVal( $dname, 'state', 0 ) ne 'initialized' ); $dhash->{helper}{infoErrorCounter} = ( $dhash->{helper}{infoErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastStatusRequestState', 'statusRequest_error', 1 ); if ( $param->{code} eq 404 && ReadingsVal( $dname, 'flow_Informations', 'inactive' ) eq 'inactive' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: check the informations flow on your device" ); } elsif ( $param->{code} eq 404 && ReadingsVal( $dname, 'flow_Informations', 'active' ) eq 'active' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: check the automagicApp on your device" ); } else { Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: http error " . $param->{code} ); } readingsEndUpdate( $dhash, 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - statusRequestERROR: received http code " . $param->{code} . " receive Error after requesting AMAD AutomagicInfo" ); return; } ### End Error Handling readingsSingleUpdate( $dhash, 'lastStatusRequestState', 'statusRequest_done', 1 ); $dhash->{helper}{infoErrorCounter} = 0; } elsif ( $param->{method} eq 'POST' ) { ### Begin Error Handling if ( $dhash->{helper}{setCmdErrorCounter} > 2 ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'lastSetCommandState', 'statusRequest_error', 1 ); if ( ReadingsVal( $dname, 'flow_Informations', 'active' ) eq 'inactive' && ReadingsVal( $dname, 'flow_SetCommands', 'active' ) eq 'inactive' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: CHECK THE LAST ERROR READINGS FOR MORE INFO, DEVICE IS SET OFFLINE" ); readingsBulkUpdate( $dhash, 'deviceState', 'offline', 1 ); readingsBulkUpdate( $dhash, 'state', 'AMAD Flows inactive, device set offline', 1 ); } elsif ($dhash->{helper}{infoErrorCounter} > 7 && $dhash->{helper}{setCmdErrorCounter} > 4 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: UNKNOWN ERROR, PLEASE CONTACT THE DEVELOPER, DEVICE DISABLED" ); $attr{$dname}{disable} = 1; readingsBulkUpdate( $dhash, 'state', 'Unknown Error, device disabled', 1 ); $dhash->{helper}{infoErrorCounter} = 0; $dhash->{helper}{setCmdErrorCounter} = 0; return; } elsif ( ReadingsVal( $dname, 'flow_SetCommands', 'active' ) eq 'inactive' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: Flow SetCommands on your Device is inactive, will try to reactivate" ); } elsif ( $dhash->{helper}{setCmdErrorCounter} > 9 ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: To many Errors please check your Network or Device Configuration, DEVICE IS SET OFFLINE" ); readingsBulkUpdate( $dhash, 'deviceState', 'offline', 1 ); readingsBulkUpdate( $dhash, 'state', 'To many Errors, device set offline', 1 ); $dhash->{helper}{setCmdErrorCounter} = 0; } elsif ( $dhash->{helper}{setCmdErrorCounter} > 4 && ReadingsVal( $dname, 'flow_SetCommands', 'active' ) eq 'active' ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: Please check the AutomagicAPP on your Device" ); } readingsEndUpdate( $dhash, 1 ); } if ( defined($err) ) { if ( $err ne '' ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $err, 1 ) if ( ReadingsVal( $dname, 'state', 0 ) ne 'initialized' ); $dhash->{helper}{setCmdErrorCounter} = ( $dhash->{helper}{setCmdErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastSetCommandState', 'setCmd_error', 1 ); if ( $err =~ /timed out/ ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: connect to your device is timed out. check network" ); } elsif ( $err =~ /Keine Route zum Zielrechner/ ) { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: no route to target. bad network configuration or network is down" ); } else { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: $err" ); } readingsEndUpdate( $dhash, 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: error while POST Command: $err" ); return; } } if ( $data eq '' and exists( $param->{code} ) && $param->{code} ne 200 ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $param->{code}, 1 ) if ( ReadingsVal( $dhash, 'state', 0 ) ne 'initialized' ); $dhash->{helper}{setCmdErrorCounter} = ( $dhash->{helper}{setCmdErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastSetCommandState', 'setCmd_error', 1 ); readingsEndUpdate( $dhash, 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: received http code " . $param->{code} ); return; } if ( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, 'state', $param->{code}, 1 ) if ( ReadingsVal( $dname, 'state', 0 ) ne 'initialized' ); $dhash->{helper}{setCmdErrorCounter} = ( $dhash->{helper}{setCmdErrorCounter} + 1 ); readingsBulkUpdate( $dhash, 'lastSetCommandState', 'setCmd_error', 1 ); if ( $param->{code} eq 404 ) { readingsBulkUpdate( $dhash, 'lastSetCommandError', '', 1 ); Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: setCommands flow is inactive on your device!" ); } else { Log3( $dname, 5, "AMADCommBridge ($dname) - setCommandERROR: http error " . $param->{code} ); } return; } ### End Error Handling readingsSingleUpdate( $dhash, 'lastSetCommandState', 'setCmd_done', 1 ); $dhash->{helper}{setCmdErrorCounter} = 0; return undef; } } sub Open($) { my $hash = shift; my $name = $hash->{NAME}; my $port = $hash->{PORT}; if ( not defined( $hash->{FD} ) and ( !$hash->{FD} ) ) { # Oeffnen des TCP Sockets my $ret = TcpServer_Open( $hash, $port, 'global' ); if ( $ret && !$init_done ) { Log3( $name, 3, "AMADCommBridge ($name) - $ret. Exiting." ); exit(1); } readingsSingleUpdate( $hash, 'state', 'opened', 1 ) if ( defined( $hash->{FD} ) ); Log3( $name, 3, "AMADCommBridge ($name) - Socket opened." ); return $ret; } else { Log3( $name, 3, "AMADCommBridge ($name) - Socket already opened" ); } return; } sub Close($) { my $hash = shift; my $name = $hash->{NAME}; TcpServer_Close($hash); if ( not defined( $hash->{FD} ) ) { readingsSingleUpdate( $hash, 'state', 'closed', 1 ); Log3( $name, 3, "AMADCommBridge ($name) - Socket closed." ); } else { Log3( $name, 3, "AMADCommBridge ($name) - can't close Socket." ); } return; } sub Read($) { my $hash = shift; my $name = $hash->{NAME}; if ( $hash->{SERVERSOCKET} ) { # Accept and create a child TcpServer_Accept( $hash, 'AMADCommBridge' ); return; } # Read 1024 byte of data my $buf; my $ret = sysread( $hash->{CD}, $buf, 2048 ); # When there is an error in connection return if ( !defined($ret) or $ret <= 0 ) { CommandDelete( undef, $name ); Log3( $name, 5, "AMADCommBridge ($name) - Connection closed for $name" ); return; } ProcessRead( $hash, $buf ); } sub ProcessRead($$) { my ( $hash, $buf ) = @_; my $name = $hash->{NAME}; my $flowsetversion = $modules{AMADCommBridge}{defptr}{BRIDGE}->{VERSIONFLOWSET}; my @data = split( '\R\R', $buf ); my $data = $data[0]; my $json = $data[1]; my $buffer = ''; Log3( $name, 4, "AMADCommBridge ($name) - process read" ); my $response; my $c; my $fhempath = $attr{global}{modpath}; if ( $data =~ /currentFlowsetUpdate.xml/ ) { $response = qx(cat $fhempath/FHEM/lib/74_AMADautomagicFlowset_$flowsetversion.xml); $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $data =~ /currentTaskersetUpdate.prj.xml/ ) { $response = qx(cat $fhempath/FHEM/lib/74_AMADtaskerset_$flowsetversion.prj.xml); $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $data =~ /installFlow_([^.]*.xml)/ ) { if ( defined($1) ) { $response = qx(cat /tmp/$1); $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } } if ( defined( $hash->{PARTIAL} ) and $hash->{PARTIAL} ) { Log3( $name, 5, "AMADCommBridge ($name) - PARTIAL: " . $hash->{PARTIAL} ); $buffer = $hash->{PARTIAL}; } else { Log3( $name, 4, "AMADCommBridge ($name) - No PARTIAL buffer" ); } return if ( !defined($json) ); Log3( $name, 5, "AMADCommBridge ($name) - Incoming data: " . $json ); $buffer = $buffer . $json; Log3( $name, 4, "AMADCommBridge ($name) - Current processing buffer (PARTIAL + incoming data): " . $buffer ); my ( $correct_json, $tail ) = ParseMsg( $hash, $buffer ); while ($correct_json) { $hash->{LAST_RECV} = time(); Log3( $name, 5, "AMADCommBridge ($name) - Decoding JSON message. Length: " . length($correct_json) . " Content: " . $correct_json ); Log3( $name, 5, "AMADCommBridge ($name) - Vor Sub: Laenge JSON: " . length($correct_json) . " Content: " . $correct_json . " Tail: " . $tail ); ResponseProcessing( $hash, $correct_json ) unless ( not defined($tail) and not($tail) ); ( $correct_json, $tail ) = ParseMsg( $hash, $tail ); Log3( $name, 5, "AMADCommBridge ($name) - Nach Sub: Laenge JSON: " . length($correct_json) . " Content: " . $correct_json . " Tail: " . $tail ); } $hash->{PARTIAL} = $tail; Log3( $name, 4, "AMADCommBridge ($name) - PARTIAL lenght: " . length($tail) ); Log3( $name, 5, "AMADCommBridge ($name) - Tail: " . $tail ); Log3( $name, 5, "AMADCommBridge ($name) - PARTIAL: " . $hash->{PARTIAL} ); } sub ResponseProcessing($$) { my ( $hash, $json ) = @_; my $name = $hash->{NAME}; my $bhash = $modules{AMADCommBridge}{defptr}{BRIDGE}; my $bname = $bhash->{NAME}; #### Verarbeitung der Daten welche über die AMADCommBridge kommen #### Log3( $bname, 4, "AMADCommBridge ($name) - Receive RAW Message in Debugging Mode: $json" ); my $response; my $c; my $decode_json = eval { decode_json($json) }; if ($@) { Log3( $bname, 4, "AMADCommBridge ($name) - JSON error while request: $@" ); if ( AttrVal( $bname, 'debugJSON', 0 ) == 1 ) { readingsBeginUpdate($bhash); readingsBulkUpdate( $bhash, 'JSON_ERROR', $@, 1 ); readingsBulkUpdate( $bhash, 'JSON_ERROR_STRING', $json, 1 ); readingsEndUpdate( $bhash, 1 ); } $response = "header lines: \r\n AMADCommBridge receive a JSON error\r\n AMADCommBridge to do nothing\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } my $amad_id = $decode_json->{amad}{amad_id}; my $fhemcmd = $decode_json->{amad}{fhemcmd}; my $fhemDevice; if ( defined( $decode_json->{firstrun} ) and ( $decode_json->{firstrun} ) ) { $fhemDevice = $decode_json->{firstrun}{fhemdevice} if ( defined( $decode_json->{firstrun}{fhemdevice} ) ); } else { $fhemDevice = $modules{AMADDevice}{defptr}{$amad_id}->{NAME}; } if ( !defined($amad_id) or !defined($fhemDevice) ) { readingsSingleUpdate( $bhash, 'transmitterERROR', $hash->{NAME} . ' has no correct amad_id', 1 ); Log3( $bname, 4, "AMADCommBridge ($name) - ERROR - no device name given. please check your global variable amad_id in automagic" ); $response = "header lines: \r\n AMADCommBridge receive no device name. please check your global variable amad_id in automagic\r\n FHEM to do nothing\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } if ( defined($fhemcmd) and ($fhemcmd) ) { if ( $fhemcmd eq 'setreading' ) { return Log3( $bname, 3, "AMADCommBridge ($name) - AMADCommBridge: processing receive no reading values from Device: $fhemDevice" ) unless ( ( defined( $decode_json->{payload} ) and ( $decode_json->{payload} ) ) or ( defined( $decode_json->{firstrun} ) and ( $decode_json->{firstrun} ) ) ); Log3( $bname, 4, "AMADCommBridge ($bname) - AMADCommBridge: processing receive reading values - Device: $fhemDevice Data: $decode_json->{payload}" ) if ( defined( $decode_json->{payload} ) and ( $decode_json->{payload} ) ); Dispatch( $bhash, $json, undef ); Log3( $bname, 4, "AMADCommBridge ($bname) - call Dispatcher" ); CommandAttr( undef, $bname . ' fhemServerIP ' . $decode_json->{firstrun}{'fhemserverip'} ) if ( defined( $decode_json->{firstrun}{'fhemserverip'} ) ); $response = "header lines: \r\n AMADCommBridge receive Data complete\r\n FHEM was processes\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $fhemcmd eq 'set' ) { my $fhemCmd = encode_utf8( $decode_json->{payload}{setcmd} ); AnalyzeCommandChain( $bhash, 'set ' . $fhemCmd ) if ( AttrVal( $bname, 'fhemControlMode', 'trigger' ) eq 'setControl' ); readingsSingleUpdate( $bhash, 'receiveFhemCommand', 'set ' . $fhemCmd, 1 ) if ( AttrVal( $bname, 'fhemControlMode', 'trigger' ) eq 'trigger' ); Log3( $bname, 4, "AMADCommBridge ($name) - CommBridge: set reading receive fhem command" ); $response = "header lines: \r\n AMADCommBridge receive Data complete\r\n FHEM response\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $fhemcmd eq 'voiceinputvalue' ) { my $fhemCmd = lc( encode_utf8( $decode_json->{payload}{voiceinputdata} ) ); readingsBeginUpdate($bhash); readingsBulkUpdate( $bhash, 'receiveVoiceCommand', $fhemCmd, 1 ); readingsBulkUpdate( $bhash, 'receiveVoiceDevice', $fhemDevice, 1 ); readingsEndUpdate( $bhash, 1 ); Log3( $bname, 4, "AMADCommBridge ($name) - CommBridge: set reading receive voice command: $fhemCmd from Device $fhemDevice" ); $response = "header lines: \r\n AMADCommBridge receive Data complete\r\n FHEM was processes\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $fhemcmd eq 'readingsval' ) { my $fhemCmd = $decode_json->{payload}{readingsvalcmd}; my @datavalue = split( ' ', $fhemCmd ); $response = ReadingsVal( $datavalue[0], $datavalue[1], $datavalue[2] ); $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } elsif ( $fhemcmd eq 'fhemfunc' ) { my $fhemCmd = $decode_json->{payload}{fhemsub}; Log3( $bname, 4, "AMADCommBridge ($name) - CommBridge: receive fhem-function command" ); if ( AttrVal( $bname, 'enableSubCalls', 0 ) == 1 ) { $response = AnalyzePerlCommand( $bhash, '{' . $fhemCmd . '}' ) ; # AnalyzePerlCommand is new https://forum.fhem.de/index.php/topic,89619.msg820964.html#msg820964 } else { $response = "header lines: \r\n Attribut enableSubCalls is not set or value is 0\r\n FHEM to do nothing\r\n"; Log3 $bname, 3, "AMADCommBridge ($name) - Attribut enableSubCalls is not set or value is 0, FHEM to do nothing"; } $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; return; } } $response = "header lines: \r\n AMADCommBridge receive incomplete or corrupt Data\r\n FHEM to do nothing\r\n"; $c = $hash->{CD}; print $c "HTTP/1.1 200 OK\r\n", "Content-Type: text/plain\r\n", "Connection: close\r\n", "Content-Length: " . length($response) . "\r\n\r\n", $response; } ################## ### my little helper ################## sub ParseMsg($$) { my ( $hash, $buffer ) = @_; my $name = $hash->{NAME}; my $open = 0; my $close = 0; my $msg = ''; my $tail = ''; if ($buffer) { foreach my $c ( split //, $buffer ) { if ( $open == $close && $open > 0 ) { $tail .= $c; Log3( $name, 5, "AMADCommBridge ($name) - $open == $close && $open > 0" ); } elsif ( ( $open == $close ) && ( $c ne '{' ) ) { Log3( $name, 5, "AMADCommBridge ($name) - Garbage character before message: " . $c ); } else { if ( $c eq '{' ) { $open++; } elsif ( $c eq '}' ) { $close++; } $msg .= $c; } } if ( $open != $close ) { $tail = $msg; $msg = ''; } } Log3( $name, 5, "AMADCommBridge ($name) - return msg: $msg and tail: $tail" ); return ( $msg, $tail ); } ##### bleibt zu Anschauungszwecken erhalten #sub Header2Hash($) { # # my $string = shift; # my %hash = (); # # foreach my $line (split("\r\n", $string)) { # my ($key,$value) = split( ": ", $line ); # next if( !$value ); # # $value =~ s/^ //; # $hash{$key} = $value; # } # # return \%hash; #} 1; =pod =item device =item summary Integrates Android devices into FHEM and displays several settings. =item summary_DE Integriert Android-Geräte in FHEM und zeigt verschiedene Einstellungen an. =begin html

AMADCommBridge

=end html =begin html_DE

AMADCommBridge

=end html_DE =for :application/json;q=META.json 73_AMADCommBridge.pm { "abstract": "Integrates Android devices into FHEM and displays several settings", "x_lang": { "de": { "abstract": "Integriert Android-Geräte in FHEM und zeigt verschiedene Einstellungen an" } }, "keywords": [ "fhem-mod-device", "fhem-core", "Android", "Tablet", "Handy", "AMAD" ], "release_status": "stable", "license": "GPL_2", "version": "v4.4.4", "x_flowsetversion": "4.4.3", "author": [ "Marko Oldenburg " ], "x_fhem_maintainer": [ "CoolTux" ], "x_fhem_maintainer_github": [ "LeonGaultier" ], "prereqs": { "runtime": { "requires": { "FHEM": 5.00918799, "perl": 5.016, "Meta": 0, "Encode": 0, "JSON": 0, "HttpUtils": 0, "TcpServerUtils": 0 }, "recommends": { }, "suggests": { } } } } =end :application/json;q=META.json =cut