From ed7893570ecd20f24c58e3f68ceba9742aa443b4 Mon Sep 17 00:00:00 2001
From: borisneubert <borisneubert@users.noreply.github.com>
Date: Sat, 20 Jun 2009 17:18:59 +0000
Subject: [PATCH] USF1000S support for FHEM added (Boris 2009-06-20)

git-svn-id: https://svn.fhem.de/fhem/trunk@394 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
 fhem/CHANGED              |   3 +-
 fhem/FHEM/00_FHZ.pm       |   5 +-
 fhem/FHEM/09_USF1000.pm   | 170 ++++++++++++++++++++++++++++++++++++++
 fhem/HISTORY              |   5 +-
 fhem/docs/USF1000.txt     |  41 +++++++++
 fhem/docs/commandref.html |  70 +++++++++++++++-
 6 files changed, 288 insertions(+), 6 deletions(-)
 create mode 100644 fhem/FHEM/09_USF1000.pm
 create mode 100755 fhem/docs/USF1000.txt

diff --git a/fhem/CHANGED b/fhem/CHANGED
index fa1d6e0d3..bc9f0839c 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -502,6 +502,7 @@
   - feature: CUL FHT sending added
   - bugfix:  workaround to make M232 counter wraparound
   - feature: sequence module added
-  - feature: Google Weather API support form FHEM (Boris 2009-06-01)
+  - feature: Google Weather API support for FHEM (Boris 2009-06-01)
   - feature: lazy attribute for FHT devices (Boris 2009-06-09)
   - feature: tmpcorr attribute for FHT devices
+  - feature: USF1000S support for FHEM added (Boris 2009-06-20)
diff --git a/fhem/FHEM/00_FHZ.pm b/fhem/FHEM/00_FHZ.pm
index dcd7e6e35..8495bbde4 100755
--- a/fhem/FHEM/00_FHZ.pm
+++ b/fhem/FHEM/00_FHZ.pm
@@ -57,12 +57,13 @@ FHZ_Initialize($)
 # Provider
   $hash->{ReadFn}  = "FHZ_Read";
   $hash->{WriteFn} = "FHZ_Write";
-  $hash->{Clients} = ":FHZ:FS20:FHT:HMS:KS300:";
+  $hash->{Clients} = ":FHZ:FS20:FHT:HMS:KS300:USF1000:";
   my %mc = (
     "1:FS20"  => "^81..(04|0c)..0101a001",
     "2:FHT"   => "^81..(04|09|0d)..(0909a001|83098301|c409c401)..",
     "3:HMS"   => "^810e04....(1|5|9).a001",
-    "4:KS300" => "^810d04..4027a001"
+    "4:KS300" => "^810d04..4027a001",
+    "5:USF1000" => "^810c04..0101a001a5ceaa00...."
   );
   $hash->{MatchList} = \%mc;
   $hash->{ReadyFn} = "FHZ_Ready";
diff --git a/fhem/FHEM/09_USF1000.pm b/fhem/FHEM/09_USF1000.pm
new file mode 100644
index 000000000..312e81b2f
--- /dev/null
+++ b/fhem/FHEM/09_USF1000.pm
@@ -0,0 +1,170 @@
+#
+#
+# 09_USF1000.pm
+# written by Dr. Boris Neubert 2009-06-20
+# e-mail: omega at online dot de
+#
+##############################################
+package main;
+
+use strict;
+use warnings;
+
+my $PI= 3.141592653589793238;
+
+my %defptr;
+my $dev= "a5ce aa";
+
+#############################
+sub
+USF1000_Initialize($)
+{
+  my ($hash) = @_;
+
+  $hash->{Match}     = "^810c04..0101a001a5ceaa00....";
+  $hash->{DefFn}     = "USF1000_Define";
+  $hash->{UndefFn}   = "USF1000_Undef";
+  $hash->{ParseFn}   = "USF1000_Parse";
+  $hash->{AttrList}  = "IODev do_not_notify:1,0 showtime:0,1 dummy:1,0 model:usf1000s loglevel:0,1,2,3,4,5,6";
+
+}
+
+
+#############################
+sub
+USF1000_Define($$)
+{
+  my ($hash, $def) = @_;
+  my @a = split("[ \t][ \t]*", $def);
+
+  my $u= "wrong syntax: define <name> USF1000 geometry";
+  my $g= "wrong geometry for USF1000";
+
+  # geometry (units: meter)
+  #	 cub	length width height offset	cuboid			3+4
+  #	 cylv	diameter height offset		vertical cylinder	3+3
+  # the offset is measured from the TOP of the box!
+
+  return $u if(int(@a)< 6);
+
+  my $name	= $a[0];
+  my $geometry	= $a[2];
+
+  if($geometry eq "cub") {
+  	# cuboid
+  	return $g if(int(@a)< 7);
+  	$hash->{GEOMETRY}= $geometry;
+  	$hash->{LENGTH}=   $a[3];
+  	$hash->{WIDTH}=    $a[4];
+  	$hash->{HEIGHT}=   $a[5];
+  	$hash->{OFFSET}=   $a[6];
+  	$hash->{CAPACITY}= int($hash->{LENGTH}*$hash->{WIDTH}*$hash->{HEIGHT}*100.0+0.5)*10.0;
+  } elsif($geometry eq "cylv") {
+  	# vertical cylinder
+  	return $g if(int(@a)< 6);
+  	$hash->{GEOMETRY}= $geometry;
+  	$hash->{DIAMETER}= $a[3];
+  	$hash->{HEIGHT}=   $a[4];
+  	$hash->{OFFSET}=   $a[5];
+  	$hash->{CAPACITY}= int($PI*$hash->{DIAMETER}*$hash->{DIAMETER}/4.0*$hash->{HEIGHT}*100.0+0.5)*10.0;
+  } else {
+  	 return $g;
+  }
+
+  $defptr{$dev} = $hash;
+  AssignIoPort($hash);
+}
+
+#############################
+sub
+USF1000_Undef($$)
+{
+  my ($hash, $name) = @_;
+  delete($defptr{$dev});
+  return undef;
+}
+
+#############################
+sub
+USF1000_Parse($$)
+{
+  my ($hash, $msg) = @_;	# hash points to the FHZ, not to the USF1000
+
+  if(!defined($defptr{$dev})) {
+    Log 3, "USF1000 Unknown device, please define it";
+    return "UNDEFINED USF1000";
+  }
+
+  my $def= $defptr{$dev};
+  my $name= $def->{NAME};
+
+  return "" if($def->{IODev} && $def->{IODev}{NAME} ne $hash->{NAME});
+
+
+  # Msg format:
+  # 01 23 45 67 8901 2345 6789 01 23 45 67
+  # 81 0c 04 .. 0101 a001 a5ce aa 00 cc xx
+
+  my $cc= substr($msg, 24, 2);
+  my $xx= substr($msg, 26, 2);
+
+
+  my $lowbattery= (hex($cc) & 0x40 ? 1 : 0);
+  my $testmode=   (hex($cc) & 0x80 ? 1 : 0);
+  my $distance=   hex($xx)/100.0; # in meters
+
+  my $wlevel  =   $def->{HEIGHT}-($distance-$def->{OFFSET}); # water level
+
+  my $geometry= $def->{GEOMETRY};
+  my $capacity= $def->{CAPACITY}; # capacity of tank (for distance= offset) in liters
+  my $volume;   # current volume in tank in liters
+  my $flevel;	# fill level in percent
+
+
+  if($geometry eq "cub") {
+  	# cuboid
+  	$volume  = $def->{LENGTH}*$def->{WIDTH}*$wlevel*1000.0;
+  } elsif($geometry eq "cylv") {
+  	# vertical cylinder
+  	$volume  = $PI*$def->{DIAMETER}*$def->{DIAMETER}/4.0*$wlevel*1000.0;
+  } else {
+  	return 0;
+  }
+
+  $flevel  = int($volume/$capacity*100.0+0.5);
+  $volume= int($volume/10.0+0.5)*10.0;
+
+  my $t= TimeNow();
+
+  my $state= sprintf("v: %d  V: %d", $flevel, $volume);
+
+  $def->{CHANGED}[0] = $state;
+  $def->{STATE} = $state;
+  $def->{READINGS}{state}{TIME} = $t;
+  $def->{READINGS}{state}{VAL} = $state;
+  Log GetLogLevel($name, 4), "USF1000 $name: $state";
+
+  $def->{READINGS}{distance}{TIME} = $t;
+  $def->{READINGS}{distance}{VAL} = $distance;
+  $def->{READINGS}{level}{TIME} = $t;
+  $def->{READINGS}{level}{VAL} = $flevel;
+  $def->{READINGS}{volume}{TIME} = $t;
+  $def->{READINGS}{volume}{VAL} = $volume;
+
+  my $warnings= ($lowbattery ? "Battery low" : "");
+  if($testmode) {
+  	$warnings.= "; " if($warnings);
+  	$warnings.= "Test mode";
+  }
+  $warnings= $warnings ? $warnings : "none";
+
+  $def->{READINGS}{"warnings"}{TIME} = $t;
+  $def->{READINGS}{"warnings"}{VAL} = $warnings;
+
+  return $name;
+
+}
+
+#############################
+
+1;
diff --git a/fhem/HISTORY b/fhem/HISTORY
index 24281040e..092cc318a 100644
--- a/fhem/HISTORY
+++ b/fhem/HISTORY
@@ -412,5 +412,8 @@
 - Tue Jun 09 2009 (Boris)
   - 11_FHT.pm: lazy attribute for FHT devices
 
-- Sun Jun 14 2009 (Rudi
+- Sun Jun 14 2009 (Rudi)
   - 11_FHT.pm: tmpcorr attribute for FHT devices
+
+- Sat Jun 20 2009 (Boris)
+  - 09_USF1000.pm: new module to support USF1000S devices.
diff --git a/fhem/docs/USF1000.txt b/fhem/docs/USF1000.txt
new file mode 100755
index 000000000..260cc8475
--- /dev/null
+++ b/fhem/docs/USF1000.txt
@@ -0,0 +1,41 @@
+2009-06-20bn
+
+This document describes the protocol for the ultrasound fill-meter USF 1000 S.
+
+Datagram is of FS20 sensor message type:
+
+81 0C 04 ?? 01 01 A0 01 A5 CE AA 00 cc xx
+
+81: 		begin of FS20/FHT communication
+0C: 		remaining length of datagram (12 bytes)
+04: 		type of datagram
+??: 		checksum
+01 01 A0 01: 	FS20 fix sequence, always 01 01 A0 01
+A5 CE: 		FS20 housecode, always A5CE
+AA: 		FS20 command, always AA
+00:		always 00
+cc:		code, see below
+xx:		measured distance in cm
+
+code:
+
+	hex	bin
+	37	00110111	30s message interval, normal operation
+	B7	10110111	3s message interval, normal operation
+	F7	11110111	3s message interval, battery low
+		|||+++++-- 0x17 always 0x17
+		||+------- 0x20 always 1 (= value byte follows)
+		|+-------- 0x40 battery warning
+		+--------- 0x80 test mode (3s message interval)
+
+
+measured distance from ultrasound sender/transmitter:
+
+	distance	hex	dec
+	0.50 m		0x30	48
+	1.00 m		0x60	96
+	1.50 m		0x92	146
+	2.00 m		0xC4	196
+
+It is assumed that xx is the distance in centimeters from the top of the box
+(4 cm box height + 1 cm height of ultrasound sender/transmitter).
\ No newline at end of file
diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html
index 30df39a93..cbc9a0f34 100644
--- a/fhem/docs/commandref.html
+++ b/fhem/docs/commandref.html
@@ -74,6 +74,7 @@
       <a href="#WS2000">WS2000</a> &nbsp;
       <a href="#WS300">WS300</a> &nbsp;
       <a href="#Weather">Weather</a> &nbsp;
+      <a href="#USF1000">USF1000</a> &nbsp;
       <a href="#X10">X10</a> &nbsp;
       <a href="#FHEMRENDERER">FHEMRENDERER</a> &nbsp;
 
@@ -2149,7 +2150,7 @@ A line ending with \ will be concatenated with the next one, so long lines
 <ul>
   <br>
 
-  <a name="WS300"></a>
+  <a name="WS300define"></a>
   <b>Define</b>
   <ul>
     <code>define WS300Device WS300 &lt;serial device&gt;</code><br>
@@ -2205,7 +2206,7 @@ A line ending with \ will be concatenated with the next one, so long lines
 <ul>
   <br>
 
-  <a name="Weather"></a>
+  <a name="Weatherdefine"></a>
   <b>Define</b>
   <ul>
     <code>define &lt;name&gt; Weather &lt;location&gt; [&lt;interval&gt;]</code><br>
@@ -2277,6 +2278,71 @@ A line ending with \ will be concatenated with the next one, so long lines
 </ul>
 
 
+<a name="USF1000"></a>
+<h3>USF1000</h3>
+<ul>
+  Fhem can receive your tank's fill level from the USF1000S device
+  through a <a href="#FHZ">FHZ</a> device, so one must be defined first.
+  The state contains the fill level in % (lower case v in the device state)
+  and the current volume in liters (upper case V in the device state).
+  Measured distance to the liquid's surface, fill level, volume and warnings
+  (Test mode, Battery low) are available.<br>
+  <br>
+
+  <a name="USF1000Define"></a>
+  <b>Define</b>
+  <ul>
+    <code>define &lt;name&gt; USF1000 &lt;geometry&gt;</code>
+    <br><br>
+
+    <code>&lt;geometry&gt;</code> determines the form of the tank and the
+    position of the sensor. The following geometries are currently
+    supported:<br><br>
+    <ul>
+    	<li><code>cub &lt;length&gt; &lt;width&gt; &lt;height&gt; &lt;offset&gt;</code></li>
+    	<li><code>cylv &lt;diameter&gt; &lt;height&gt; &lt;offset&gt;</code></li>
+    </ul>
+    <br>
+    <code>cub</code> stands for a cuboid whose base is &lt;length&gt; &times; &lt;width&gt;.
+    <code>cylv</code> stands for a vertical cylinder whose diameter is &lt;diameter&gt;.
+    &lt;height&gt; is the distance of the surface of the liquid from the ground
+    if the tank is full. &lt;offset&gt; is the distance of the sensor relative to
+    the surface of the liquid. All quantities are expressed in meters.<br>
+    <br>
+
+    Example:<br>
+    <ul>
+      <code>define MyTank USF1000 cylv 2 1 0.3</code>: a cylindrical water tank with
+      2 meters diameter. The water stands 1 meter high if the tank is full. The
+      sensor is fixed 1,3 meters above ground.<br>
+    </ul>
+  </ul>
+  <br>
+
+  <b>Set </b>
+  <ul>
+    N/A
+  </ul>
+  <br>
+
+  <b>Get</b>
+  <ul>
+    N/A
+  </ul>
+  <br>
+
+  <b>Attributes</b>
+  <ul>
+    <li><a href="#do_not_notify">do_not_notify</a></li>
+    <li><a href="#showtime">showtime</a></li>
+    <li><a href="#loglevel">loglevel</a></li>
+    <li><a href="#model">model</a> (usf1000s)</li>
+  </ul>
+  <br>
+
+</ul>
+
+
 
 <a name="SCIVT"></a>
 <h3>SCIVT</h3>