diff --git a/fhem/CHANGED b/fhem/CHANGED
index aec03fa95..86662702b 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - new: 12_HProtocolGateway / 12_HProtocolTank
- bugfix: 72_XiaomiDevice: remove unused battery readings for new fans
- feature: 49_SSCam: activate/deactivate cam internal PIR-sensor
- new: 10_MQTT_GENERIC_BRIDGE an MQTT bridge, which simultaneously
diff --git a/fhem/FHEM/12_HProtocolGateway.pm b/fhem/FHEM/12_HProtocolGateway.pm
new file mode 100644
index 000000000..923a2e079
--- /dev/null
+++ b/fhem/FHEM/12_HProtocolGateway.pm
@@ -0,0 +1,430 @@
+# $Id$
+####################################################################################################
+#
+# 12_HProtocolGateway.pm
+#
+# Copyright: Stephan Eisler
+# Email: fhem.dev@hausautomatisierung.co
+#
+# 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 .
+#
+####################################################################################################
+
+package main;
+
+use strict;
+use warnings;
+use DevIo;
+
+my @tankList = undef;
+
+my %sets = (
+ 'readValues' => 1,
+);
+
+sub HProtocolGateway_Initialize($) {
+ my ($hash) = @_;
+
+ $hash->{Clients} = "HProtocolTank";
+ $hash->{DefFn} = "HProtocolGateway_Define";
+ $hash->{InitFn} = "HProtocolGateway_Init";
+ $hash->{GetFn} = "HProtocolGateway_Get";
+ $hash->{SetFn} = "HProtocolGateway_Set";
+ $hash->{AttrFn} = "HProtocolGateway_Attr";
+ $hash->{AttrList} = "device " .
+ "baudrate:300,600,1200,2400,4800,9600 " .
+ "parityBit:N,E,O " .
+ "databitsLength:5,6,7,8 " .
+ "stopBit " .
+ "pollIntervalMins " .
+ "mode:Filllevel,Volume,Ullage " .
+ "path";
+}
+
+sub HProtocolGateway_Define($$) {
+ my ($hash, $def) = @_;
+ my @a = split("[ \t][ \t]*", $def);
+
+ return "Wrong syntax: use define HProtocolGateway " if(int(@a) != 3);
+
+ $hash->{helper}{DeviceName} = $a[2];
+ $hash->{Clients} = "HProtocolTank";
+ $hash->{STATE} = "Initialized";
+
+ HProtocolGateway_DeviceConfig($hash);
+
+ HProtocolGateway_Poll($hash) if defined(AttrVal($hash->{NAME}, 'pollIntervalMins', undef)); # if pollIntervalMins defind -> start timer
+
+ return undef;
+}
+
+sub HProtocolGateway_Get($$@) {
+ my ($hash, $name, $opt, @args) = @_;
+
+ return "\"get $name\" needs at least one argument" unless(defined($opt));
+
+ if ($opt eq "update") {
+ HProtocolGateway_GetUpdate($hash);
+ return "Done.";
+ } else {
+ # IMPORTANT! This defines the list of possible commands
+ my $list = "update:noArg";
+ return "Unknown argument $opt, choose one of $list";
+ }
+
+ return -1;
+}
+
+sub HProtocolGateway_Set($@) {
+ my ($hash, @a) = @_;
+
+ my $name = $a[0];
+ my $cmd = $a[1];
+
+ if(!defined($sets{$cmd})) {
+ return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets) . ":noArg"
+ }
+
+ if ($cmd eq 'readValues') {
+
+ RemoveInternalTimer($hash);
+ InternalTimer(gettimeofday() + 1, 'HProtocolGateway_GetUpdate', $hash, 0);
+ }
+ return undef
+}
+
+sub HProtocolGateway_GetUpdate($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ foreach (@tankList) {
+ my $tankHash = $_;
+ my $command = "\$A";
+ if ($attr{$name}{mode} eq "Volume") {
+ $command = "\$B";
+ } elsif ($attr{$name}{mode} eq "Ullage") {
+ $command = "\$C";
+ }
+
+ my $msg = $command . $tankHash->{READINGS}{hID}{VAL} . "\r\n";
+ DevIo_SimpleWrite($hash, $msg , 2);
+ my ($err, $data) = HProtocolGateway_ReadAnswer($hash,$tankHash);
+ Log3 $name, 5, "err:". $err;
+ Log3 $name, 5, "data:". $data;
+ }
+
+ my $pollInterval = AttrVal($hash->{NAME}, 'pollIntervalMins', 0); #restore pollIntervalMins Timer
+ InternalTimer(gettimeofday() + ($pollInterval * 60), 'HProtocolGateway_Poll', $hash, 0) if ($pollInterval > 0);
+}
+
+sub HProtocolGateway_ReadAnswer($$) {
+ my ($hash,$tankHash) = @_;
+ my $name = $hash->{NAME};
+ return ("No FD (dummy device?)", undef)
+ if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
+
+ for(;;) {
+ return ("Device lost when reading answer", undef)
+ if(!$hash->{FD});
+ my $rin = '';
+ vec($rin, $hash->{FD}, 1) = 1;
+ my $nfound = select($rin, undef, undef, 3);
+ if($nfound <= 0) {
+ next if ($! == EAGAIN() || $! == EINTR());
+ my $err = ($! ? $! : "Timeout");
+ return("ProtocolGateway_ReadAnswer $err", undef);
+ }
+ my $buf = DevIo_SimpleRead($hash);
+ return ("No data", undef) if(!defined($buf));
+
+ my $ret = HProtocolGateway_Read($hash, $buf,$tankHash);
+ return (undef, $ret) if(defined($ret));
+ }
+
+}
+
+sub HProtocolGateway_Read($@) {
+
+ my ($hash, $data, $tankHash) = @_;
+ my $name = $hash->{NAME};
+ my $buffer = $hash->{PARTIAL};
+
+ Log3 $name, 5, "HProtocolGateway ($name) - received $data (buffer contains: $buffer)";
+
+ $buffer .= $data;
+
+ my $msg;
+ # as long as the buffer contains CR (complete datagramm)
+ while($buffer =~ m/\r/)
+ {
+ ($msg, $buffer) = split("\r", $buffer, 2);
+ chomp $msg;
+ HProtocolGateway_ParseMessage($hash, $msg, $tankHash);
+ }
+
+ $hash->{PARTIAL} = $buffer;
+
+ return $msg if(defined($data));
+ return undef;
+}
+
+sub HProtocolGateway_ParseMessage($$) {
+ my ($hash, $data, $tankHash) = @_;
+ my $name = $hash->{NAME};
+
+ $data =~ s/^.//; # remove #
+
+ my ($tankdata,$water,$temperature,$probe_offset,$version,$error,$checksum)=split(/@/,$data);
+ my $test = "#".$data.$water.$temperature.$probe_offset.$version.$error;
+
+ # calculate XOR CRC
+ my $check = 0;
+ $check ^= $_ for unpack 'C*', $test;
+ # convert to HEX
+ $check = sprintf '%02X', $check;
+
+ return if($check ne $checksum);
+
+ my ($filllevel,$volume,$ullage) = 0;
+
+ if ($attr{$name}{mode} eq "Filllevel") {
+ $filllevel = $tankdata;
+ $volume = HProtocolGateway_Tank($hash,$tankHash,$filllevel);
+ } elsif ($attr{$name}{mode} eq "Volume") {
+ $volume = $tankdata;
+ } elsif ($attr{$name}{mode} eq "Ullage") {
+ $ullage = $tankdata;
+ }
+
+ my $sign = substr($temperature,0,1);
+ $temperature =~ s/^.//;
+ if ($sign eq "-") { $temperature = int($temperature) * -1 };
+ $temperature = $temperature / 10;
+
+ $sign = substr($probe_offset,0,1);
+ $probe_offset =~ s/^.//;
+ if ($sign eq "-") { $probe_offset = int($probe_offset) * -1 };
+
+ my $volume_15C = $volume * (1 + 0.00084 * ( 15 - $temperature ));
+ $volume_15C = int($volume_15C);
+
+ # Update all received readings
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "ullage", $ullage);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "filllevel", $filllevel);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "volume", $volume);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "volume_15C", $volume_15C);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "temperature", $temperature);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "waterlevel", $water);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "probe_offset", $probe_offset);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "version", $version);
+ HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "error", $error);
+}
+
+sub HProtocolGateway_UpdateTankDevice($$$$) {
+ my ($hash, $tankName, $reading, $value) = @_;
+
+ my $message = $tankName . " " . $reading . " " . $value;
+
+ my $success = Dispatch($hash, $message, undef);
+
+ if (!$success) {
+ my $name = $hash->{NAME};
+ Log3 $name, 1, "$name: failed to update tank device";
+ }
+}
+
+sub HProtocolGateway_RegisterTank($) {
+ my ($tankHash) = @_;
+
+ # Remove undefined elements of empty array
+ @tankList = grep defined, @tankList;
+
+ @tankList = (@tankList, $tankHash);
+}
+
+# called when definition is undefined
+# (config reload, shutdown or delete of definition)
+sub HProtocolGateway_Undef($$)
+{
+ my ($hash, $name) = @_;
+
+ # close the connection
+ DevIo_CloseDev($hash);
+
+ return undef;
+}
+
+# called repeatedly if device disappeared
+sub HProtocolGateway_Ready($)
+{
+ my ($hash) = @_;
+
+ # try to reopen the connection in case the connection is lost
+ return DevIo_OpenDev($hash, 1, "HProtocolGateway_Init");
+}
+
+# will be executed upon successful connection establishment (see DevIo_OpenDev())
+sub HProtocolGateway_Init($)
+{
+ my ($hash) = @_;
+
+ # send a status request to the device
+ # DevIo_SimpleWrite($hash, "get_status\r\n", 2);
+
+ return undef;
+}
+
+# Called during attribute create/change
+sub HProtocolGateway_Attr (@) {
+ my ($command, $name, $attr, $val) = @_;
+ my $hash = $defs{$name};
+ my $msg = '';
+
+ if ($attr eq 'poll_interval') {
+ if (defined($val)) {
+ if ($val =~ m/^(0*[1-9][0-9]*)$/) {
+ RemoveInternalTimer($hash);
+ HProtocolGateway_Poll($hash) if ($main::init_done);
+ } else {
+ $msg = 'Wrong poll intervall defined. pollIntervalMins must be a number > 0';
+ }
+ } else {
+ RemoveInternalTimer($hash);
+ }
+ } elsif ($attr eq 'mode') {
+ $attr{$name}{mode} = $val;
+ } elsif ($attr eq 'path') {
+ $attr{$name}{path} = $val;
+ } elsif ($attr eq 'baudrate') {
+ $attr{$name}{baudrate} = $val;
+ HProtocolGateway_DeviceConfig($hash);
+ } elsif ($attr eq 'databitsLength') {
+ $attr{$name}{databitsLength} = $val;
+ HProtocolGateway_DeviceConfig($hash);
+ } elsif ($attr eq 'parityBit') {
+ $attr{$name}{parityBit} = $val;
+ HProtocolGateway_DeviceConfig($hash);
+ } elsif ($attr eq 'stopBit') {
+ $attr{$name}{stopBit} = $val;
+ HProtocolGateway_DeviceConfig($hash);
+ }
+
+}
+
+sub HProtocolGateway_DeviceConfig($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+ my $deviceName = $hash->{helper}{DeviceName};
+ $hash->{DeviceName} = $deviceName."@".$attr{$name}{baudrate}.",".$attr{$name}{databitsLength}.",".$attr{$name}{parityBit}.",".$attr{$name}{stopBit};
+ DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));
+ my $ret = DevIo_OpenDev($hash, 0, "HProtocolGateway_Init");
+ return $ret
+}
+
+# Request measurements regularly
+sub HProtocolGateway_Poll($) {
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ HProtocolGateway_Set($hash, ($name, 'readValues'));
+
+ my $pollInterval = AttrVal($hash->{NAME}, 'pollIntervalMins', 0);
+ if ($pollInterval > 0) {
+ InternalTimer(gettimeofday() + ($pollInterval * 60), 'HProtocolGateway_Poll', $hash, 0);
+ }
+}
+
+sub HProtocolGateway_Tank($$$) {
+ my ($hash,$tankHash,$filllevel) = @_;
+ my $name = $hash->{NAME};
+
+ my %TankChartHash;
+ open my $fh, '<', $attr{$name}{path}.'tank'.$tankHash->{READINGS}{hID}{VAL}.'.csv' or die "Cannot open: $!";
+ while (my $line = <$fh>) {
+ $line =~ s/\s*\z//;
+ my @array = split /,/, $line;
+ my $key = shift @array;
+ $TankChartHash{$key} = $array[0];
+ }
+ close $fh;
+
+ my $messwert = $filllevel/10;
+ my $volume = 0;
+
+ foreach my $level (sort keys %TankChartHash) {
+ if ($messwert <= $level && $level ne "level") {
+ $volume = $TankChartHash{$level};
+ last;
+ }
+ }
+ retun $volume;
+}
+
+1;
+
+
+=pod
+=item summary support for the HLS 6010 Probes
+=begin html
+
+
+HProtocolGateway
+
+ The HProtocolGateway is a fhem module for the RS232 standard interface for HLS 6010 Probes connected to a Hectronic OPTILEVEL Supply.
+
+
+
+
+ Define
+
+ define <name> HProtocolGateway /dev/tty???
+ attr <name> pollIntervalMins 2
+ attr <name> mode Filllevel
+ attr <name> path /opt/fhem/
+ attr <name> baudrate 1200
+ attr <name> databitsLength 8
+ attr <name> parityBit N
+ attr <name> stopBit 1
+
+
+ Defines an HProtocolGateway connected to RS232 serial standard interface.
+ path is the path for tank<01>.csv strapping table files.
+
+
+ level,volume
+ 10,16
+ 520,7781
+ 1330,29105
+ 1830,43403
+ 2070,49844
+ 2220,53580
+ 2370,57009
+ 2400,57650
+ 2430,58275
+ 2370,57009
+ 2400,57650
+ 2430,58275
+
+
+
+
+
+
+
+
+=end html
+
+=cut
diff --git a/fhem/FHEM/12_HProtocolTank.pm b/fhem/FHEM/12_HProtocolTank.pm
new file mode 100644
index 000000000..3f1cce08f
--- /dev/null
+++ b/fhem/FHEM/12_HProtocolTank.pm
@@ -0,0 +1,151 @@
+# $Id$
+####################################################################################################
+#
+# 12_HProtocolTank.pm
+#
+# Copyright: Stephan Eisler
+# Email: fhem.dev@hausautomatisierung.co
+#
+# 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 .
+#
+####################################################################################################
+
+package main;
+
+sub HProtocolTank_Initialize($) {
+ my ($hash) = @_;
+
+ $hash->{DefFn} = "HProtocolTank_Define";
+ $hash->{ParseFn} = "HProtocolTank_Parse";
+ $hash->{FingerprintFn} = "HProtocolTank_Fingerprint";
+ $hash->{Match} = "^[a-zA-Z0-9_]+ [a-zA-Z0-9_]+ [+-]*[0-9]+([.][0-9]+)?";
+ $hash->{AttrList} = "event-on-update-reading";
+}
+
+sub HProtocolTank_Define($$) {
+ my ($hash, $def) = @_;
+ my @a = split("[ \t][ \t]*", $def);
+
+ return "Wrong syntax: use define HProtocolTank " if(int(@a) != 3);
+
+ my $name = $a[0];
+ my $gateway = $a[2];
+
+ if (!$hash->{IODev}) {
+ AssignIoPort($hash, $gateway);
+ }
+
+ if (defined($hash->{IODev})) {
+ Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME};
+ } else {
+ Log3 $name, 1, "$name: no I/O device";
+ }
+
+ if (defined($hash->{IODev})) {
+ $iodev = $hash->{IODev}->{NAME};
+ }
+
+ $hash->{STATE} = "Initialized";
+
+ $attr{$name}{room} = "HProtocol";
+
+ # TODO This has to be updated when renaming the device.
+ $modules{HProtocolTank}{defptr}{$name} = $hash;
+
+ # TODO A Tank has to be unregistered when it's removed or renamed.
+ HProtocolGateway_RegisterTank($hash);
+
+ return undef;
+}
+
+sub HProtocolTank_Parse($$) {
+ my ($iohash, $message) = @_;
+
+ # $message = " "
+ my @array = split("[ \t][ \t]*", $message);
+ my $tankName = @array[0];
+ my $reading = @array[1];
+ my $value = @array[2];
+
+ my $hash = $modules{HProtocolTank}{defptr}{$tankName};
+
+ readingsSingleUpdate($hash, $reading, $value, 1);
+
+ return $tankName;
+}
+
+sub HProtocolTank_Fingerprint($$) {
+ # this subroutine is called before running Parse to check if
+ # this message is a duplicate message. Refer to FHEM Wiki.
+}
+
+1;
+
+
+=pod
+=item summary devices communicating via the HProtocolGateway
+=begin html
+
+
+HProtocolTank
+
+ The HProtocolTank is a fhem module defines a device connected to a HProtocolGateway.
+
+
+
+
+ Define
+
+ define tank01 HProtocolTank HProtocolGateway
+ setreading tank01 hID 01
+
+
+
+ Defines an HProtocolTank connected to a HProtocolGateway.
+
+
+
+
+ Readings
+
+ - hID
+ 01 - 99 Tank Number / Tank Address
+ - ullage
+ 0..999999 Ullage in litres
+ - filllevel
+ 0..99999 Fill level in cm
+ - volume
+ 0..999999 Volume in litres
+ - volume_15C
+ 0..999999 Volume in litres at 15 °C
+ - temperature
+ -999 - +999 Temperature in °C
+ - waterlevel
+ 0..9999 Water level in mm
+ - probe_offset
+ -9999 - +9999 Probe offset in mm)
+ - version
+ 00..999 Software version
+ - error
+ 0..9 00.. Probe error
+
+
+
+
+
+=end html
+
+=cut
diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index 0081e60e4..50bcde737 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -86,6 +86,8 @@ FHEM/11_OWX_FRM.pm pahenning 1Wire
FHEM/11_OWX_CCC.pm pahenning 1Wire
FHEM/14_Hideki.pm Sidey/Ralf9 Sonstige Systeme
FHEM/12_HMS.pm rudolfkoenig SlowRF
+FHEM/12_HProtocolGateway.pm eisler Sonstige Systeme
+FHEM/12_HProtocolTank.pm eisler Sonstige Systeme
FHEM/13_KS300.pm rudolfkoenig SlowRF
FHEM/14_CUL_MAX.pm rudolfkoenig/orphan MAX
FHEM/14_CUL_REDIRECT.pm dancer0705/bjoernh Sonstiges