diff --git a/fhem/CHANGED b/fhem/CHANGED index c60b6684b..63b0642cb 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. + - feature: added 36_Level.pm - feature: netatmo: added plz support for public stations - change: 70_ENIGMA2: keep reading for recordings up-to-date during standby - bugfix: 98_Text2Speech: - playing mp3files directly, eg: :ring.mp3: diff --git a/fhem/FHEM/36_Level.pm b/fhem/FHEM/36_Level.pm new file mode 100644 index 000000000..13d80666a --- /dev/null +++ b/fhem/FHEM/36_Level.pm @@ -0,0 +1,252 @@ + +# $Id$ +# +# TODO: + +package main; + +use strict; +use warnings; +use SetExtensions; + +sub Level_Parse($$); + +sub +Level_Initialize($) +{ + my ($hash) = @_; + + $hash->{Match} = "^OK\\sLS\\s"; + $hash->{DefFn} = "Level_Define"; + $hash->{UndefFn} = "Level_Undef"; + $hash->{FingerprintFn} = "Level_Fingerprint"; + $hash->{ParseFn} = "Level_Parse"; + $hash->{AttrList} = "IODev" + ." ignore:1" + ." doAverage:1" + ." filterThreshold" + ." litersPerCm" + ." distanceToBottom" + ." $readingFnAttributes"; +} + +sub +Level_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + if(@a != 3 ) { + my $msg = "wrong syntax: define Level "; + Log3 undef, 2, $msg; + return $msg; + } + + $a[2] =~ m/^([\da-f]{1})$/i; + return "$a[2] is not a valid Level address" if( !defined($1) ); + + my $name = $a[0]; + my $addr = $a[2]; + + return "Level device $addr already used for $modules{Level}{defptr}{$addr}->{NAME}." if( $modules{Level}{defptr}{$addr} + && $modules{Level}{defptr}{$addr}->{NAME} ne $name ); + + $hash->{addr} = $addr; + + $modules{Level}{defptr}{$addr} = $hash; + + AssignIoPort($hash); + if(defined($hash->{IODev}->{NAME})) { + Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME}; + } else { + Log3 $name, 1, "$name: no I/O device"; + } + + return undef; +} + +##################################### +sub +Level_Undef($$) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + my $addr = $hash->{addr}; + + delete( $modules{Level}{defptr}{$addr} ); + + return undef; +} + + +##################################### +sub +Level_Get($@) +{ + my ($hash, $name, $cmd, @args) = @_; + + return "\"get $name\" needs at least one parameter" if(@_ < 3); + + my $list = ""; + + return "Unknown argument $cmd, choose one of $list"; +} + +sub +Level_Fingerprint($$) +{ + my ($name, $msg) = @_; + + return ( "", $msg ); +} + +# Format +# +# OK LS 1 0 5 100 4 191 60 = 38,0cm 21,5°C 6,0V +# OK LS 1 0 8 167 4 251 57 = 121,5cm 27,5°C 5,7V +# +# 0 1 2 3 4 5 6 +# OK LS ID T LL LL TT TT VV +# | | | | | | | | | +# | | | | | | | | `--- Voltage * 10 +# | | | | | | | `------- Temp. * 10 + 1000 LSB +# | | | | | | `----------- Temp. * 10 + 1000 MSB +# | | | | | `--------------- Level * 10 + 1000 LSB +# | | | | `------------------- Level * 10 + 1000 MSB +# | | | `------------------------ Sensor type fix 0 at the moment +# | | `--------------------------- Sensor ID ( 0 .. 15) +# | `------------------------------ fix "LS" +# `--------------------------------- fix "OK" +sub +Level_Parse($$) +{ + my ($hash, $msg) = @_; + my $name = $hash->{NAME}; + + my( @bytes, $addr, $type, $distance, $temperature, $voltage ); + if( $msg =~ m/^OK LS/ ) { + @bytes = split( ' ', substr($msg, 5) ); + + $addr = sprintf( "%01X", $bytes[0] ); + $type = $bytes[1]; + $distance = ($bytes[2]*256 + $bytes[3] - 1000)/10; + $temperature = ($bytes[4]*256 + $bytes[5] - 1000)/10; + $voltage = $bytes[6] / 10; + } else { + DoTrigger($name, "UNKNOWNCODE $msg"); + Log3 $name, 3, "$name: Unknown code $msg, help me!"; + return undef; + } + + my $raddr = $addr; + my $rhash = $modules{Level}{defptr}{$raddr}; + my $rname = $rhash?$rhash->{NAME}:$raddr; + + if( !$modules{Level}{defptr}{$raddr} ) { + Log3 $name, 3, "Level Unknown device $rname, please define it"; + + my $iohash = $rhash->{IODev}; + + return undef; + } + + my @list; + push(@list, $rname); + + $rhash->{Level_lastRcv} = TimeNow(); + + readingsBeginUpdate($rhash); + + my $litersPerCm = AttrVal( $rname, "litersPerCm", 1); + my $distanceToBottom = AttrVal( $rname, "distanceToBottom", 100); + + my $level = $distanceToBottom - $distance; + my $liters = $litersPerCm * $level; + + if( AttrVal( $rname, "doAverage", 0 ) && defined($rhash->{"previousLiters"}) ) { + $liters = int(($rhash->{"previousLiters"}*3+$liters)/4); + } + if( AttrVal( $rname, "doAverage", 0 ) && defined($rhash->{"previousTemeprature"}) ) { + $temperature = int(($rhash->{"previousTemeprature"}*3+$temperature)/4); + } + + readingsBulkUpdate($rhash, "distance", $distance); + readingsBulkUpdate($rhash, "level", $level); + readingsBulkUpdate($rhash, "liters", $liters); + readingsBulkUpdate($rhash, "temperature", $temperature); + readingsBulkUpdate($rhash, "voltage", $voltage); + + my $state = "L: $liters"; + $state .= " T: $temperature"; + $state .= " V: $voltage"; + readingsBulkUpdate($rhash, "state", $state) if( Value($rname) ne $state ); + + readingsEndUpdate($rhash,1); + + $rhash->{"previousLiters"} = $liters; + $rhash->{"previousTemperature"} = $temperature; + + return @list; +} + +sub +Level_Attr(@) +{ + my ($cmd, $name, $attrName, $attrVal) = @_; + + return undef; +} + +1; + +=pod +=begin html + + +

Level

+ + + +=end html +=cut diff --git a/fhem/contrib/arduino/36_LevelSender.zip b/fhem/contrib/arduino/36_LevelSender.zip new file mode 100644 index 000000000..938033063 Binary files /dev/null and b/fhem/contrib/arduino/36_LevelSender.zip differ