50_MOBILEALERTSGW.pm 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. ##############################################
  2. # $Id: 50_MOBILEALERTSGW.pm 16104 2018-02-06 19:11:13Z MarkusF $
  3. # Written by Markus Feist, 2017
  4. package main;
  5. use strict;
  6. use warnings;
  7. use TcpServerUtils;
  8. use HttpUtils;
  9. use IO::Socket;
  10. use constant MA_PACKAGE_LENGTH => 64;
  11. my $MA_wname;
  12. my $MA_chash;
  13. my $MA_cname;
  14. my @MA_httpheader;
  15. my %MA_httpheader;
  16. sub MOBILEALERTSGW_Initialize($) {
  17. my ($hash) = @_;
  18. $hash->{ReadFn} = "MOBILEALERTSGW_Read";
  19. $hash->{GetFn} = "MOBILEALERTSGW_Get";
  20. $hash->{SetFn} = "MOBILEALERTSGW_Set";
  21. $hash->{AttrFn} = "MOBILEALERTSGW_Attr";
  22. $hash->{DefFn} = "MOBILEALERTSGW_Define";
  23. $hash->{UndefFn} = "MOBILEALERTSGW_Undef";
  24. $hash->{Clients} = "MOBILEALERTS";
  25. $hash->{MatchList} = { "1:MOBILEALERTS" => "^.*" };
  26. $hash->{Write} = "MOBILEALERTSGW_Write";
  27. $hash->{FingerprintFn} = "MOBILEALERTSGW_Fingerprint";
  28. #$hash->{NotifyFn}= ($init_done ? "FW_Notify" : "FW_SecurityCheck");
  29. #$hash->{AsyncOutputFn} = "MOBILEALERTSGW_AsyncOutput";
  30. #$hash->{ActivateInformFn} = "MOBILEALERTSGW_ActivateInform";
  31. $hash->{AttrList} = "forward:0,1 " . $readingFnAttributes;
  32. Log3 "MOBILEALERTSGW", 5, "MOBILEALERTSGW_Initialize finished.";
  33. }
  34. sub MOBILEALERTSGW_Define($$) {
  35. my ( $hash, $def ) = @_;
  36. my ( $name, $type, $port ) = split( "[ \t]+", $def );
  37. return "Usage: define <name> MOBILEALERTSGW <tcp-portnr>"
  38. if ( $port !~ m/^\d+$/ );
  39. my $ret = TcpServer_Open( $hash, $port, "global" );
  40. return $ret;
  41. }
  42. sub MOBILEALERTSGW_GetUDPSocket($$) {
  43. my ( $hash, $name ) = @_;
  44. my $socket;
  45. if ( defined( $hash->{UDPHASH} ) ) {
  46. $socket = $hash->{UDPHASH}->{UDPSOCKET};
  47. }
  48. else {
  49. #IO::Socket::INET geht leider nicht
  50. Log3 $name, 3, "$name MOBILEALERTSGW: Create UDP Socket.";
  51. unless ( socket( $socket, AF_INET, SOCK_DGRAM, getprotobyname('udp') ) )
  52. {
  53. Log3 $name, 1, "$name MOBILEALERTSGW: Could not create socket: $!";
  54. return undef;
  55. }
  56. unless ( setsockopt( $socket, SOL_SOCKET, SO_BROADCAST, 1 ) ) {
  57. Log3 $name, 1, "$name MOBILEALERTSGW: Could not setsockopt: $!";
  58. return undef;
  59. }
  60. my $cname = "${name}_UDPPORT";
  61. my %nhash;
  62. $nhash{NR} = $devcount++;
  63. $nhash{NAME} = $cname;
  64. $nhash{FD} = fileno($socket);
  65. $nhash{UDPSOCKET} = $socket;
  66. $nhash{TYPE} = $hash->{TYPE};
  67. $nhash{STATE} = "Connected";
  68. $nhash{SNAME} = $name;
  69. $nhash{TEMPORARY} = 1; # Don't want to save it
  70. $nhash{HASH} = $hash;
  71. $attr{$cname}{room} = "hidden";
  72. $defs{$cname} = \%nhash;
  73. $selectlist{ $nhash{NAME} } = \%nhash;
  74. $hash->{UDPHASH} = \%nhash;
  75. }
  76. return $socket;
  77. }
  78. sub MOBILEALERTSGW_Get ($$@) {
  79. my ( $hash, $name, $cmd, @args ) = @_;
  80. return "\"get $name\" needs at least one argument" unless ( defined($cmd) );
  81. if ( $cmd eq "config" ) {
  82. my @gateways = split( ",", ReadingsVal( $name, "Gateways", "" ) );
  83. my $gateway = $args[0];
  84. my $destpaddr;
  85. my $command;
  86. if ( $gateway =~ /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/ ) {
  87. $destpaddr = sockaddr_in( 8003, inet_aton($gateway) );
  88. $gateway = "000000000000";
  89. $command = 1;
  90. }
  91. elsif ( $gateway =~ /([0-9A-F]{12})/ ) {
  92. if ( @gateways == 0 ) {
  93. $destpaddr = sockaddr_in( 8003, INADDR_BROADCAST );
  94. $command = 2;
  95. }
  96. elsif ( !grep( /^$gateway$/, @gateways ) ) {
  97. $destpaddr = sockaddr_in( 8003, INADDR_BROADCAST );
  98. $command = 2;
  99. }
  100. else {
  101. my $ip = ReadingsVal( $name, "GW_" . $gateway . "_ip", "" );
  102. if ( length($ip) == 0 ) {
  103. $destpaddr = sockaddr_in( 8003, INADDR_BROADCAST );
  104. }
  105. else {
  106. $destpaddr = sockaddr_in( 8003, inet_aton($ip) );
  107. }
  108. $command = 2;
  109. }
  110. }
  111. else {
  112. $gateway = "000000000000";
  113. $destpaddr = sockaddr_in( 8003, INADDR_BROADCAST );
  114. $command = 1;
  115. }
  116. my $socket = MOBILEALERTSGW_GetUDPSocket( $hash, $name );
  117. if ( !defined($socket) ) {
  118. return "Could not create socket.";
  119. }
  120. my $data = pack( "nH[12]n", $command, $gateway, 10 );
  121. Log3 $name, 5,
  122. "$name MOBILEALERTSGW: Send GetConfig " . unpack( "H*", $data );
  123. send( $socket, $data, 0, $destpaddr );
  124. return undef;
  125. }
  126. else {
  127. return "Unknown argument $cmd, choose one of config";
  128. }
  129. }
  130. sub MOBILEALERTSGW_Set ($$@) {
  131. my ( $hash, $name, $cmd, @args ) = @_;
  132. return "\"set $name\" needs at least one argument" unless ( defined($cmd) );
  133. if ( $cmd eq "clear" ) {
  134. if ( $args[0] eq "readings" ) {
  135. for ( keys %{ $hash->{READINGS} } ) {
  136. readingsDelete($hash, $_) if ( $_ ne 'state' );
  137. }
  138. return undef;
  139. }
  140. else {
  141. return "Unknown value $args[0] for $cmd, choose one of readings";
  142. }
  143. }
  144. elsif ( $cmd eq "initgateway" ) {
  145. my @gateways = split( ",", ReadingsVal( $name, "Gateways", "" ) );
  146. my $gateway = $args[0];
  147. if ( @gateways == 0 ) {
  148. return
  149. "No gateway known. Find with 'get $name findgateways' first.";
  150. }
  151. if ( !grep( /^$gateway$/, @gateways ) ) {
  152. return "Unknown $gateway for $cmd, choose one of "
  153. . join( ",", @gateways );
  154. }
  155. my $ip = ReadingsVal( $name, "GW_" . $gateway . "_ip", "" );
  156. my $config = ReadingsVal( $name, "GW_" . $gateway . "_config", "" );
  157. if ( length($ip) == 0 ) {
  158. return
  159. "IP of gateway unknown. Find with 'get $name findgateways' first.";
  160. }
  161. if ( length($config) == 0 ) {
  162. return
  163. "Config of gateway unknown. Find with 'get $name findgateways' first.";
  164. }
  165. my $sock = IO::Socket::INET->new(
  166. Proto => 'udp',
  167. PeerPort => 8003,
  168. PeerAddr => $ip
  169. ) or return "Could not create socket: $!\n";
  170. my $myip = $sock->sockhost;
  171. my $myport = $hash->{PORT};
  172. Log3 $name, 4,
  173. "$name MOBILEALERTSGW: Config gateway $gateway $ip Proxy auf $myip:$myport";
  174. $config = pack( "H*", $config );
  175. $config =
  176. "\0\x04"
  177. . substr( $config, 2, 6 )
  178. . "\0\xB5"
  179. . substr( $config, 15, 1 + 4 + 4 + 4 + 21 + 65 ) . "\x01"
  180. . pack( "a65n", $myip, $myport )
  181. . substr( $config, 182, 4 );
  182. Log3 $name, 5, "$name MOBILEALERTSGW: Send " . unpack( "H*", $config );
  183. $sock->send($config) or return "Could not send $!";
  184. $sock->close();
  185. return undef;
  186. }
  187. elsif ( $cmd eq "rebootgateway" ) {
  188. my @gateways = split( ",", ReadingsVal( $name, "Gateways", "" ) );
  189. my $gateway = $args[0];
  190. if ( @gateways == 0 ) {
  191. return
  192. "No gateway known. Find with 'get $name findgateways' first.";
  193. }
  194. if ( !grep( /^$gateway$/, @gateways ) ) {
  195. return "Unknown $gateway for $cmd, choose one of "
  196. . join( ",", @gateways );
  197. }
  198. my $ip = ReadingsVal( $name, "GW_" . $gateway . "_ip", "" );
  199. if ( length($ip) == 0 ) {
  200. return
  201. "IP of gateway unknown. Find with 'get $name findgateways' first.";
  202. }
  203. my $sock = IO::Socket::INET->new(
  204. Proto => 'udp',
  205. PeerPort => 8003,
  206. PeerAddr => $ip
  207. ) or return "Could not create socket: $!\n";
  208. Log3 $name, 4,
  209. "$name MOBILEALERTSGW: Reboot gateway $gateway auf $ip:8003";
  210. my $data = pack( "nH[12]n", 5, $gateway, 10 );
  211. Log3 $name, 5, "$name MOBILEALERTSGW: Send " . unpack( "H*", $data );
  212. $sock->send($data) or return "Could not send $!";
  213. $sock->close();
  214. return undef;
  215. }
  216. elsif ( $cmd eq "debuginsert" ) {
  217. my $data = pack( "H*", $args[0] );
  218. my ( $packageHeader, $timeStamp, $packageLength, $deviceID ) =
  219. unpack( "CNCH12", $data );
  220. Log3 $name, 4,
  221. "$name MOBILEALERTSGW: Debuginsert PackageHeader: "
  222. . $packageHeader
  223. . " Timestamp: "
  224. . scalar( FmtDateTimeRFC1123($timeStamp) )
  225. . " PackageLength: "
  226. . $packageLength
  227. . " DeviceID: "
  228. . $deviceID;
  229. Log3 $name, 5, "$name MOBILEALERTSGW: Debuginsert for $deviceID: "
  230. . unpack( "H*", $data );
  231. Dispatch( $hash, $data, undef );
  232. return undef;
  233. }
  234. else {
  235. my $gateways = ReadingsVal( $name, "Gateways", "" );
  236. return
  237. "Unknown argument $cmd, choose one of clear:readings rebootgateway:"
  238. . $gateways
  239. . " initgateway:"
  240. . $gateways;
  241. }
  242. }
  243. sub MOBILEALERTSGW_Undef($$) {
  244. my ( $hash, $name ) = @_;
  245. if ( defined( $hash->{UDPHASH} ) ) {
  246. my $cname = "${name}_UDPPORT";
  247. delete( $selectlist{$cname} );
  248. delete $attr{$cname};
  249. delete $defs{$cname};
  250. close( $hash->{UDPHASH}->{UDPSOCKET} );
  251. delete $hash->{UDPHASH};
  252. }
  253. my $ret = TcpServer_Close($hash);
  254. return $ret;
  255. }
  256. sub MOBILEALERTSGW_Attr($$$$) {
  257. my ( $cmd, $name, $attrName, $attrValue ) = @_;
  258. if ( $cmd eq "set" ) {
  259. if ( $attrName eq "forward" ) {
  260. if ( $attrValue !~ /^[01]$/ ) {
  261. Log3 $name, 3,
  262. "$name MOBILEALERTSGW: Invalid parameter attr $name $attrName $attrValue";
  263. return "Invalid value $attrValue allowed 0,1";
  264. }
  265. }
  266. }
  267. return undef;
  268. }
  269. sub MOBILEALERTSGW_Fingerprint($$$) {
  270. my ( $io_name, $message ) = @_;
  271. #PackageHeader + UTC Timestamp + Package Length + Device ID + tx counter (3 bytes)
  272. my $fingerprint = unpack( "H30", $message );
  273. return ( $io_name, $fingerprint );
  274. }
  275. sub MOBILEALERTSGW_Write ($$) {
  276. #Dummy, because it is not possible to send to device.
  277. my ( $hash, @arguments ) = @_;
  278. return undef;
  279. }
  280. sub MOBILEALERTSGW_Read($$);
  281. sub MOBILEALERTSGW_Read($$) {
  282. my ( $hash, $reread ) = @_;
  283. my $name = $hash->{NAME};
  284. my $verbose = GetVerbose($name);
  285. if ( exists $hash->{UDPSOCKET} ) {
  286. my $phash = $hash->{HASH};
  287. $name = $phash->{NAME};
  288. Log3 $name, 5, "$name MOBILEALERTSGW: Data from UDP received";
  289. my $srcpaddr = recv( $hash->{UDPSOCKET}, my $udpdata, 186, 0 );
  290. MOBILEALERTSGW_DecodeUDP( $phash, $udpdata, $srcpaddr );
  291. return;
  292. }
  293. if ( $hash->{SERVERSOCKET} ) { # Accept and create a child
  294. my $nhash = TcpServer_Accept( $hash, "MOBILEALERTSGW" );
  295. return if ( !$nhash );
  296. my $wt = AttrVal( $name, "alarmTimeout", undef );
  297. $nhash->{ALARMTIMEOUT} = $wt if ($wt);
  298. $nhash->{CD}->blocking(0);
  299. return;
  300. }
  301. $MA_chash = $hash;
  302. $MA_wname = $hash->{SNAME};
  303. $MA_cname = $name;
  304. $verbose = GetVerbose($MA_wname);
  305. #$FW_subdir = "";
  306. my $c = $hash->{CD};
  307. if ( !$reread ) {
  308. # Data from HTTP Client
  309. my $buf;
  310. my $ret = sysread( $c, $buf, 1024 );
  311. if ( !defined($ret) && $! == EWOULDBLOCK ) {
  312. $hash->{wantWrite} = 1
  313. if ( TcpServer_WantWrite($hash) );
  314. return;
  315. }
  316. elsif ( !$ret ) { # 0==EOF, undef=error
  317. CommandDelete( undef, $name );
  318. Log3 $MA_wname, 4,
  319. "$MA_wname MOBILEALERTSGW: Connection closed for $name: "
  320. . ( defined($ret) ? 'EOF' : $! );
  321. return;
  322. }
  323. $hash->{BUF} .= $buf;
  324. }
  325. if ( !$hash->{HDR} ) {
  326. return if ( $hash->{BUF} !~ m/^(.*?)(\n\n|\r\n\r\n)(.*)$/s );
  327. $hash->{HDR} = $1;
  328. $hash->{BUF} = $3;
  329. if ( $hash->{HDR} =~ m/Content-Length:\s*([^\r\n]*)/si ) {
  330. $hash->{CONTENT_LENGTH} = $1;
  331. }
  332. }
  333. @MA_httpheader = split( /[\r\n]+/, $hash->{HDR} );
  334. %MA_httpheader = map {
  335. my ( $k, $v ) = split( /: */, $_, 2 );
  336. $k =~ s/(\w+)/\u$1/g; # Forum #39203
  337. $k => ( defined($v) ? $v : 1 );
  338. } @MA_httpheader;
  339. my $POSTdata = "";
  340. if ( $hash->{CONTENT_LENGTH} ) {
  341. return if ( length( $hash->{BUF} ) < $hash->{CONTENT_LENGTH} );
  342. $POSTdata = substr( $hash->{BUF}, 0, $hash->{CONTENT_LENGTH} );
  343. $hash->{BUF} = substr( $hash->{BUF}, $hash->{CONTENT_LENGTH} );
  344. }
  345. delete( $hash->{HDR} );
  346. if ( $verbose >= 5 ) {
  347. Log3 $MA_wname, 5,
  348. "$MA_wname MOBILEALERTSGW: Headers: " . join( ", ", @MA_httpheader );
  349. Log3 $MA_wname, 5, "$MA_wname MOBILEALERTSGW: Receivebuffer: "
  350. . unpack( "H*", $POSTdata );
  351. }
  352. my ( $method, $url, $httpvers ) = split( " ", $MA_httpheader[0], 3 )
  353. if ( $MA_httpheader[0] );
  354. $method = "" if ( !$method );
  355. #if($method !~ m/^(GET|POST)$/i){
  356. if ( $method !~ m/^(PUT|POST)$/i ) {
  357. TcpServer_WriteBlocking( $MA_chash,
  358. "HTTP/1.1 405 Method Not Allowed\r\n"
  359. . "Content-Length: 0\r\n\r\n" );
  360. delete $hash->{CONTENT_LENGTH};
  361. MOBILEALERTSGW_Read( $hash, 1 ) if ( $hash->{BUF} );
  362. Log3 $MA_wname, 3,
  363. "$MA_wname MOBILEALERTSGW: $MA_cname: unsupported HTTP method $method, rejecting it.";
  364. MOBILEALERTSGW_closeConn($hash);
  365. return;
  366. }
  367. if ( $url !~ m/.*\/gateway\/put$/i ) {
  368. TcpServer_WriteBlocking( $MA_chash,
  369. "HTTP/1.1 400 Bad Request\r\n" . "Content-Length: 0\r\n\r\n" );
  370. delete $hash->{CONTENT_LENGTH};
  371. MOBILEALERTSGW_Read( $hash, 1 ) if ( $hash->{BUF} );
  372. Log3 $MA_wname, 3,
  373. "$MA_wname MOBILEALERTSGW: $MA_cname: unsupported URL $url, rejecting it.";
  374. MOBILEALERTSGW_closeConn($hash);
  375. return;
  376. }
  377. if ( !exists $MA_httpheader{"HTTP_IDENTIFY"} ) {
  378. TcpServer_WriteBlocking( $MA_chash,
  379. "HTTP/1.1 400 Bad Request\r\n" . "Content-Length: 0\r\n\r\n" );
  380. delete $hash->{CONTENT_LENGTH};
  381. MOBILEALERTSGW_Read( $hash, 1 ) if ( $hash->{BUF} );
  382. Log3 $MA_wname, 3,
  383. "$MA_wname MOBILEALERTSGW: $MA_cname: not Header http_identify, rejecting it.";
  384. MOBILEALERTSGW_closeConn($hash);
  385. return;
  386. }
  387. Log3 $MA_wname, 5, "Header HTTP_IDENTIFY" . $MA_httpheader{"HTTP_IDENTIFY"};
  388. my ( $gwserial, $gwmac, $actioncode ) =
  389. split( /:/, $MA_httpheader{"HTTP_IDENTIFY"} );
  390. my @gateways = split( ",", ReadingsVal( $MA_wname, "Gateways", "" ) );
  391. readingsBeginUpdate( $defs{$MA_wname} );
  392. if ( !grep( /^$gwmac$/, @gateways ) ) {
  393. push( @gateways, $gwmac );
  394. readingsBulkUpdate( $defs{$MA_wname}, "Gateways",
  395. join( ",", @gateways ) );
  396. }
  397. readingsBulkUpdate( $defs{$MA_wname}, "GW_" . $gwmac . "_lastSeen",
  398. TimeNow() );
  399. readingsBulkUpdateIfChanged( $defs{$MA_wname}, "GW_" . $gwmac . "_ip",
  400. $hash->{PEER} );
  401. readingsBulkUpdateIfChanged( $defs{$MA_wname}, "GW_" . $gwmac . "_serial",
  402. $gwserial );
  403. readingsEndUpdate( $defs{$MA_wname}, 1 );
  404. if ( $actioncode eq "00" ) {
  405. Log3 $MA_wname, 4,
  406. "$MA_wname MOBILEALERTSGW: $MA_cname: Initrequest from $gwserial $gwmac";
  407. MOBILEALERTSGW_DecodeInit( $hash, $POSTdata );
  408. MOBILEALERTSGW_DefaultAnswer($hash);
  409. }
  410. elsif ( $actioncode eq "C0" ) {
  411. Log3 $MA_wname, 4,
  412. "$MA_wname MOBILEALERTSGW: $MA_cname: Data from $gwserial $gwmac";
  413. MOBILEALERTSGW_DecodeData( $hash, $POSTdata );
  414. MOBILEALERTSGW_DefaultAnswer($hash);
  415. }
  416. else {
  417. TcpServer_WriteBlocking( $MA_chash,
  418. "HTTP/1.1 400 Bad Request\r\n" . "Content-Length: 0\r\n\r\n" );
  419. delete $hash->{CONTENT_LENGTH};
  420. MOBILEALERTSGW_Read( $hash, 1 ) if ( $hash->{BUF} );
  421. Log3 $MA_wname, 3,
  422. "$MA_wname MOBILEALERTSGW: $MA_cname: unknown Actioncode $actioncode";
  423. Log3 $MA_wname, 4,
  424. "$MA_wname MOBILEALERTSGW: $MA_cname: unknown Actioncode $actioncode Postdata: "
  425. . unpack( "H*", $POSTdata );
  426. MOBILEALERTSGW_closeConn($hash);
  427. return;
  428. }
  429. MOBILEALERTSGW_closeConn($hash); #No Keep-Alive
  430. #Send to Server
  431. if ( AttrVal( $MA_wname, "forward", 0 ) == 1 ) {
  432. my $httpparam = {
  433. url => "http://www.data199.com/gateway/put",
  434. timeout => 20,
  435. httpversion => "1.1",
  436. hash => $hash,
  437. method => "PUT",
  438. header => "HTTP_IDENTIFY: "
  439. . $MA_httpheader{"HTTP_IDENTIFY"}
  440. . "\r\nContent-Type: application/octet-stream",
  441. data => $POSTdata,
  442. callback => \&MOBILEALERTSGW_NonblockingGet_Callback
  443. };
  444. HttpUtils_NonblockingGet($httpparam);
  445. }
  446. return;
  447. }
  448. sub MOBILEALERTSGW_NonblockingGet_Callback($$$) {
  449. my ( $param, $err, $data ) = @_;
  450. my $hash = $param->{hash};
  451. my $name = $hash->{NAME};
  452. my $code = $param->{code};
  453. Log3 $name, 4, "$name MOBILEALERTSGW: Callback";
  454. if ( $err ne "" ) {
  455. Log3 $name, 3,
  456. "$name MOBILEALERTSGW: error while forward request to "
  457. . $param->{url}
  458. . " - $err";
  459. }
  460. elsif ( $code != 200 ) {
  461. Log3 $name, 3,
  462. "$name MOBILEALERTSGW: http-error while forward request to "
  463. . $param->{url} . " - "
  464. . $param->{code};
  465. Log3 $name, 5,
  466. "$name MOBILEALERTSGW: http-header: " . $param->{httpheader};
  467. Log3 $name, 5, "$name MOBILEALERTSGW: http-data: " . $data;
  468. }
  469. else {
  470. Log3 $name, 5, "$name MOBILEALERTSGW: forward successfull";
  471. Log3 $name, 5,
  472. "$name MOBILEALERTSGW: http-header: " . $param->{httpheader};
  473. Log3 $name, 5,
  474. "$name MOBILEALERTSGW: http-data: " . unpack( "H*", $data );
  475. }
  476. HttpUtils_Close($param);
  477. }
  478. sub MOBILEALERTSGW_closeConn($) {
  479. my ($hash) = @_;
  480. # Kein Keep-Alive noetig
  481. TcpServer_Close( $hash, 1 );
  482. }
  483. sub MOBILEALERTSGW_DefaultAnswer($) {
  484. my ($hash) = @_;
  485. my $buf;
  486. $buf = pack( "NxxxxNxxxxNN", 420, time, 0x1761D480, 15 );
  487. TcpServer_WriteBlocking( $MA_chash,
  488. "HTTP/1.1 200 OK\r\n"
  489. . "Content-Type: application/octet-stream\r\n"
  490. . "Content-Length: 24\r\n\r\n"
  491. . $buf );
  492. }
  493. sub MOBILEALERTSGW_DecodeInit($$) {
  494. my ( $hash, $POSTdata ) = @_;
  495. my ( $packageLength, $upTime, $ID, $unknown1, $unknown50 ) =
  496. unpack( "CNH12nn", $POSTdata );
  497. Log3 $MA_wname, 4,
  498. "$MA_wname MOBILEALERTSGW: Uptime (s): " . $upTime . " ID: " . $ID;
  499. }
  500. sub MOBILEALERTSGW_DecodeData($$) {
  501. my ( $hash, $POSTdata ) = @_;
  502. my $verbose = GetVerbose($MA_wname);
  503. for ( my $pos = 0 ; $pos < length($POSTdata) ; $pos += MA_PACKAGE_LENGTH ) {
  504. my $data = substr $POSTdata, $pos, MA_PACKAGE_LENGTH;
  505. my ( $packageHeader, $timeStamp, $packageLength, $deviceID ) =
  506. unpack( "CNCH12", $data );
  507. Log3 $MA_wname, 4,
  508. "$MA_wname MOBILEALERTSGW: PackageHeader: "
  509. . $packageHeader
  510. . " Timestamp: "
  511. . scalar( FmtDateTimeRFC1123($timeStamp) )
  512. . " PackageLength: "
  513. . $packageLength
  514. . " DeviceID: "
  515. . $deviceID
  516. if ( $verbose >= 4 );
  517. Log3 $MA_wname, 5,
  518. "$MA_wname MOBILEALERTSGW: Data for $deviceID: "
  519. . unpack( "H*", $data )
  520. if ( $verbose >= 5 );
  521. my $found = Dispatch( $defs{$MA_wname}, $data, undef );
  522. }
  523. }
  524. sub MOBILEALERTSGW_DecodeUDP($$$) {
  525. my ( $hash, $udpdata, $srcpaddr ) = @_;
  526. my ( $port, $ipaddr ) = sockaddr_in($srcpaddr);
  527. my $name = $hash->{NAME};
  528. Log3 $name, 4,
  529. "$name MOBILEALERTSGW: Data from " . inet_ntoa($ipaddr) . ":" . $port;
  530. Log3 $name, 5, "$name MOBILEALERTSGW: Data: " . unpack( "H*", $udpdata );
  531. if ( length $udpdata == 186 ) {
  532. my @ip;
  533. my @fip;
  534. my @netmask;
  535. my @gateway;
  536. my @dnsip;
  537. (
  538. my $command,
  539. my $gatewayid,
  540. my $length,
  541. @ip[ 0 .. 3 ],
  542. my $dhcp,
  543. @fip[ 0 .. 3 ],
  544. @netmask[ 0 .. 3 ],
  545. @gateway[ 0 .. 3 ],
  546. my $devicename,
  547. my $dataserver,
  548. my $proxy,
  549. my $proxyname,
  550. my $proxyport,
  551. @dnsip[ 0 .. 3 ]
  552. ) = unpack( "nH12nxCCCCCCCCCCCCCCCCCa21a65Ca65nCCCC", $udpdata );
  553. if ( $command != 3 ) {
  554. Log3 $name, 3,
  555. "$name MOBILEALERTSGW: Unknown Command $command: "
  556. . unpack( "H*", $udpdata );
  557. return;
  558. }
  559. Log3 $name, 4,
  560. "$name MOBILEALERTSGW: Command: "
  561. . $command
  562. . " Gatewayid: "
  563. . $gatewayid
  564. . " length: "
  565. . $length . " IP: "
  566. . join( ".", @ip )
  567. . " DHCP: "
  568. . $dhcp
  569. . " fixedIP: "
  570. . join( ".", @fip )
  571. . " netmask: "
  572. . join( ".", @netmask )
  573. . " gateway: "
  574. . join( ".", @gateway )
  575. . " devicename: "
  576. . $devicename
  577. . " dataserver: "
  578. . $dataserver
  579. . " proxy: "
  580. . $proxy
  581. . " proxyname: "
  582. . $proxyname
  583. . " proxyport: "
  584. . $proxyport
  585. . " dnsip: "
  586. . join( ".", @dnsip );
  587. $gatewayid = uc $gatewayid;
  588. readingsBeginUpdate($hash);
  589. readingsBulkUpdate( $hash, "GW_" . $gatewayid . "_lastSeen",
  590. TimeNow() );
  591. readingsBulkUpdateIfChanged( $hash, "GW_" . $gatewayid . "_ip",
  592. inet_ntoa($ipaddr) );
  593. readingsBulkUpdateIfChanged(
  594. $hash,
  595. "GW_" . $gatewayid . "_config",
  596. unpack( "H*", $udpdata )
  597. );
  598. readingsBulkUpdateIfChanged(
  599. $hash,
  600. "GW_" . $gatewayid . "_proxy",
  601. $proxy == 1 ? "on" : "off"
  602. );
  603. readingsBulkUpdateIfChanged( $hash, "GW_" . $gatewayid . "_proxyname",
  604. $proxyname );
  605. readingsBulkUpdateIfChanged( $hash, "GW_" . $gatewayid . "_proxyport",
  606. $proxyport );
  607. my @gateways = split( ",", ReadingsVal( $name, "Gateways", "" ) );
  608. if ( !grep( /^$gatewayid$/, @gateways ) ) {
  609. push( @gateways, $gatewayid );
  610. readingsBulkUpdate( $hash, "Gateways", join( ",", @gateways ) );
  611. }
  612. readingsEndUpdate( $hash, 1 );
  613. }
  614. elsif ( length $udpdata == 118 ) {
  615. Log3 $name, 5,
  616. "$name MOBILEALERTSGW: Package was defect, this seems to be normal.";
  617. }
  618. else {
  619. Log3 $name, 3,
  620. "$name MOBILEALERTSGW: Unknown Data: " . unpack( "H*", $udpdata );
  621. }
  622. return;
  623. }
  624. # Eval-Rückgabewert für erfolgreiches
  625. # Laden des Moduls
  626. 1;
  627. =pod
  628. =item device
  629. =item summary IO device for german MobileAlerts
  630. =item summary_DE IO device für deutsche MobileAlets
  631. =begin html
  632. <a name="MOBILEALERTSGW"></a>
  633. <h3>MOBILEALERTSGW</h3>
  634. <ul>
  635. The MOBILEALERTSGW is a fhem module for the german MobileAlerts Gateway and TFA WEATHERHUB.
  636. <br><br>
  637. The fhem module makes simulates as http-proxy to intercept messages from the gateway.
  638. In order to use this module you need to configure the gateway to use the fhem-server with the defined port as proxy.
  639. You can do so with the command initgateway or by app.
  640. It automatically detects devices. The other devices are handled by the <a href="#MOBILEALERTS">MOBILELAERTS</a> module,
  641. which uses this module as its backend.<br>
  642. <br>
  643. <a name="MOBILEALERTSGWdefine"></a>
  644. <b>Define</b>
  645. <ul>
  646. <code>define &lt;name&gt; MOBILEALERTSGW &lt;port&gt;</code><br>
  647. <br>
  648. port is the port where the proxy server listens. The port must be free.
  649. </ul>
  650. <br>
  651. <a name="MOBILEALERTSGWreadings"></a>
  652. <b>Readings</b>
  653. <ul>
  654. <li>Gateways<br>List of known gateways</li>
  655. <li>GW_&lt;Gateway-MAC&gt;_lastSeen<br>Time when last message was received from gateway</li>
  656. <li>GW_&lt;Gateway-MAC&gt;_ip<br>IP-Adresse of gateway</li>
  657. <li>GW_&lt;Gateway-MAC&gt;_serial<br>Serialnumber of gateway</li>
  658. <li>GW_&lt;Gateway-MAC&gt;_proxy<br>on, off: setting of proxy (only after get config)</li>
  659. <li>GW_&lt;Gateway-MAC&gt;_proxyname<br>Name/IP of proxy (only after get config)</li>
  660. <li>GW_&lt;Gateway-MAC&gt;_proxyport<br>Port of proxy (only after get config)</li>
  661. <li>GW_&lt;Gateway-MAC&gt;_config<br>Complete configuration as hex-values (only after get config)</li>
  662. </ul>
  663. <br>
  664. <a name="MOBILEALERTSGWset"></a>
  665. <b>Set</b>
  666. <ul>
  667. <li><code>set &lt;name&gt; clear &lt;readings&gt;</code><br>
  668. Clears the readings. </li>
  669. <li><code>set &lt;name&gt; initgateway &lt;gatewayid&gt;</code><br>
  670. Sets the proxy in the gateway to the fhem-server. A reboot of the gateway may be needed in order to take effect.</li>
  671. <li><code>set &lt;name&gt; rebootgateway &lt;gatewayid&gt;</code><br>
  672. Reboots the gateway.</li>
  673. </ul>
  674. <br>
  675. <a name="MOBILEALERTSGWget"></a>
  676. <b>Get</b>
  677. <ul>
  678. <li><code>get &lt;name&gt; config &lt;IP or gatewayid&gt; </code><br>
  679. Gets the config of a gateway or all gateways. IP or gatewayid are optional.
  680. If not specified it will search for alle Gateways in the local lan (Broadcast).</li>
  681. </ul>
  682. <br>
  683. <br>
  684. <a name="MOBILEALERTSGWattr"></a>
  685. <b>Attributes</b>
  686. <ul>
  687. <li>forward<br>
  688. If value 1 is set, the data will be forwarded to the MobileAlerts Server http://www.data199.com/gateway/put .
  689. </li>
  690. </ul>
  691. </ul>
  692. =end html
  693. =begin html_DE
  694. <a name="MOBILEALERTSGW"></a>
  695. <h3>MOBILEALERTSGW</h3>
  696. <ul>
  697. MOBILEALERTSGW ist ein FHEM-Modul f&uuml;r das deutsche MobileAlerts Gateway und TFA WEATHERHUB.
  698. <br><br>
  699. Dieses FHEM-Modul simuliert einen http-proxy, um Nachrichten vom Gateway abzufangen.
  700. Um dies zu erreichen, muss das Gateway so konfiguriert werden, dass es den FHEM-Server mit dem definierten Port als
  701. Proxy nutzt. Sie k&ouml;nnen dies entweder mit der App oder dem Kommando initgateway erreichen.
  702. Es erkennt automatisch Ger&auml;te. Die Ger&auml; werden durch das <a href="#MOBILEALERTS">MOBILELAERTS</a> Modul
  703. bereitgestellt. MOBILEALERTS nutzt dieses Modul als Backend.<br>
  704. <br>
  705. <a name="MOBILEALERTSGWdefine"></a>
  706. <b>Define</b>
  707. <ul>
  708. <code>define &lt;name&gt; MOBILEALERTSGW &lt;port&gt;</code><br>
  709. <br>
  710. port ist der Port auf dem der Proxy-Server h&ouml;rt. Der Port muss frei sein.
  711. </ul>
  712. <br>
  713. <a name="MOBILEALERTSGWreadings"></a>
  714. <b>Readings</b>
  715. <ul>
  716. <li>Gateways<br>Liste der bekannten Gateways</li>
  717. <li>GW_&lt;Gateway-MAC&gt;_lastSeen<br>Zeitpunkt when zuletzt eine Nachricht empfangen wurde</li>
  718. <li>GW_&lt;Gateway-MAC&gt;_ip<br>IP-Adresse des Gateways</li>
  719. <li>GW_&lt;Gateway-MAC&gt;_serial<br>Seriennummer des Gateways</li>
  720. <li>GW_&lt;Gateway-MAC&gt;_proxy<br>on, off: Einstellung des Proxies (nur verf&uuml;nach einem get config)</li>
  721. <li>GW_&lt;Gateway-MAC&gt;_proxyname<br>Name/IP der Proxy (nur verf&uuml;nach einem get config)</li>
  722. <li>GW_&lt;Gateway-MAC&gt;_proxyport<br>Port der Proxy (nur verf&uuml;nach einem get config)</li>
  723. <li>GW_&lt;Gateway-MAC&gt;_config<br>Komplette Konfiguration als HEX-Wert (nur verf&uuml;nach einem get config)</li>
  724. </ul>
  725. <br>
  726. <a name="MOBILEALERTSGWset"></a>
  727. <b>Set</b>
  728. <ul>
  729. <li><code>set &lt;name&gt; clear &lt;readings&gt;</code><br>
  730. L&ouml;scht die Readings. </li>
  731. <li><code>set &lt;name&gt; initgateway &lt;gatewayid&gt;</code><br>
  732. Setzt den Proxy im Gateway auf dem FHEM-Server. Es kann ein Neustart (reboot) des Gateways n&ouml;tig sein, damit die
  733. Einstellung wirksam wird.</li>
  734. <li><code>set &lt;name&gt; rebootgateway &lt;gatewayid&gt;</code><br>
  735. Startet das Gateway neu.</li>
  736. </ul>
  737. <br>
  738. <a name="MOBILEALERTSGWget"></a>
  739. <b>Get</b>
  740. <ul>
  741. <li><code>get &lt;name&gt; config &lt;IP or gatewayid&gt; </code><br>
  742. Holt die Konfiguration eines oder aller Gateways im lokalen Netz. IP bzw. die GatewayId sind optional.
  743. Wenn keines von beiden angegeben ist, werden alle Gateways im lokalen Netz gesucht (Broadcast).</li>
  744. </ul>
  745. <br>
  746. <br>
  747. <a name="MOBILEALERTSGWattr"></a>
  748. <b>Attributes</b>
  749. <ul>
  750. <li>forward<br>
  751. Wenn dieser Wert auf 1 gesetzt ist, werden die Daten zus&auml;tzlich zum MobileAlerts Server http://www.data199.com/gateway/put gesendet.
  752. </li>
  753. </ul>
  754. </ul>
  755. =end html_DE
  756. =cut