2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

- Softwarebuffer for FHT devices with queuing unsent commands and repeating commands by transmission failure

- FHT low temperatur warning and setting for lowtemp-offset
- Change naming for state into warnings


git-svn-id: https://svn.fhem.de/fhem/trunk@101 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
dirkh 2007-10-19 09:29:00 +00:00
parent 72b261d6aa
commit 04a95a803f
7 changed files with 2360 additions and 1931 deletions

View File

@ -348,3 +348,6 @@
- feature: webpgm2 output reformatted
- feature: webpgm2 can display multiple plots.
- feature: FHT lime-protection code discovered by Dirk (7.10)
- feature: Softwarebuffer for FHT devices with queuing unsent commands and repeating commands by transmission failure (Dirk 17.10)
- feature: FHT low temperatur warning and setting for lowtemp-offset (Dirk 17.10)
- change: Change naming for state into warnings (Dirk 17.10)

View File

@ -68,7 +68,7 @@ FHZ_Initialize($)
$hash->{SetFn} = "FHZ_Set";
$hash->{StateFn} = "FHZ_SetState";
$hash->{ParseFn} = "FHZ_Parse";
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 filtertimeout repeater:1,0 showtime:1,0 model:fhz1000,fhz1300 loglevel:0,1,2,3,4,5,6";
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 filtertimeout repeater:1,0 showtime:1,0 model:fhz1000,fhz1300 loglevel:0,1,2,3,4,5,6 softbuffer softrepeat softmaxretry";
}
#####################################
@ -160,6 +160,24 @@ FHZ_Get($@)
return "$a[0] $a[1] => $v";
}
#####################################
# get the FHZ hardwarebuffer without logentry
# and as decimal value
sub getFhzBuffer()
{
my $msg = "Timeout";
while (index($msg,"Timeout") >= 0) { # try getting FHZ buffer until no Timeout occurs
FHZ_Write($defs{FHZ}, "04", "c90185") if(!IsDummy("FHZ"));
$msg = FHZ_ReadAnswer($defs{FHZ}, "fhtbuf");
}
my $v = substr($msg, 16, 2);
$v = hex $v;
return "$v";
}
#####################################
sub
FHZ_SetState($$$$)
@ -346,7 +364,8 @@ FHZ_ReadAnswer($$)
my $buf = $hash->{PortObj}->input();
Log 5, "FHZ/RAW: " . unpack('H*',$buf);
# Log 5, "FHZ/RAW: " . unpack('H*',$buf);
Log 4, "FHZ/RAW: " . unpack('H*',$buf);
$mfhzdata .= $buf;
next if(length($mfhzdata) < 2);

View File

@ -50,7 +50,8 @@ my %codes = (
"XX0069" => "measured-temp", # sum of next. two, never "really" sent
"420069" => "measured-low",
"430069" => "measured-high",
"440069" => "state",
"440069" => "warnings",
"450069" => "manu-temp", # Manuelle Temperatur keine ahnung was das bewirkt
"600069" => "year",
"610069" => "month",
"620069" => "day",
@ -59,10 +60,9 @@ my %codes = (
"650069" => "init",
"820069" => "day-temp",
"840069" => "night-temp",
"850069" => "unknown_85",
"850069" => "lowtemp-offset", # Alarm-Temp.-Differenz
"8a0069" => "windowopen-temp",
"00002a" => "lime-protection",
"0000aa" => "code_0000aa",
"0000ba" => "code_0000ba",
"430079" => "code_430079",
@ -86,7 +86,7 @@ my %cantset = (
"measured-temp" => 1,
"measured-high" => 1,
"measured-low" => 1,
"state" => 1,
"warnings" => 1,
"init" => 1,
"lime-protection"=>1,
@ -104,12 +104,25 @@ my %nosetarg = (
"refreshvalues" => 1,
);
my %priority = (
"desired-temp" => 1,
"mode" => 2,
"refreshvalues" => 3,
"holiday1" => 4,
"holiday2" => 5,
"day-temp" => 6,
"night-temp" => 7,
);
my %c2m = (0 => "auto", 1 => "manual", 2 => "holiday", 3 => "holiday_short");
my %m2c; # Reverse c2m
my %c2b; # command->button hash (reverse of codes)
my %c2bset; # Setteable values
my %m2c; # Reverse c2m
my %c2b; # command->button hash (reverse of codes)
my %c2bset; # Setteable values
my %defptr;
my $timerCheckBufferIsRunning = 0; # set to 1 if the timer is running
my $minFhzHardwareBufferSpace = 10; # min. bytes free in hardware buffer before sending commands
my $fhzHardwareBufferSpace = 0; # actual hardware buffer space in fhz
#####################################
sub
@ -143,29 +156,115 @@ FHT_Initialize($)
$hash->{AttrList} = "do_not_notify:0,1 model;fht80b dummy:0,1 showtime:0,1 loglevel:0,1,2,3,4,5,6";
}
#####################################
sub
FHT_Set($@)
# Parse the incomming commands and send them via sendCommand to the FHZ
# or via toSendbuffer in the Softwarebuffer (queue)
#
sub FHT_Set($@)
{
my ($hash, @a) = @_;
my $ret = undef;
my ($hash, @a) = @_;
my $ret = undef;
my $arg = "020183" . $hash->{CODE} . $c2bset{$a[1]};
my $val = $a[2];
return "\"set $a[0]\" needs two parameters" if(@a < 2);
return "Unknown argument $a[1], choose one of " .
join(" ", sort {$c2bset{$a} cmp $c2bset{$b} } keys %c2bset)
if(!defined($c2bset{$a[1]}));
return "\"set $a[0]\" needs two parameters"
if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]}));
Log GetLogLevel($a[0],2), "FHT set " . join(" ", @a);
my $arg = "020183" . $hash->{CODE} . $c2bset{$a[1]};
if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]}));
if($a[1] eq "refreshvalues") {
} elsif ($a[1] =~ m/-temp/) {
return "Invalid temperature, use NN.N" if($val !~ m/^\d*\.?\d+$/);
# additional check for temperature
return "Invalid temperature, must between 5.5 and 30.5" if($val < 5.5 || $val > 30.5);
my $a = int($val*2);
$arg .= sprintf("%02x", $a);
$ret = sprintf("Rounded temperature to %.1f", $a/2) if($a/2 != $val);
$val = sprintf("%.1f", $a/2) if($a/2 != $val);
$val = sprintf("%.1f", $val);
} elsif($a[1] =~ m/-from/ || $a[1] =~ m/-to/) {
return "Invalid timeformat, use HH:MM" if($val !~ m/^([0-2]\d):([0-5]\d)/);
my $a = ($1*6) + ($2/10);
$arg .= sprintf("%02x", $a);
my $nt = sprintf("%02d:%02d", $1, ($2/10)*10);
$val = $nt if($nt ne $val);
$ret = "Rounded time to $nt" if($nt ne $val);
} elsif($a[1] eq "mode") {
return "Invalid mode, use one of " . join(" ", sort keys %m2c)
if(!defined($m2c{$val}));
$arg .= sprintf("%02x", $m2c{$val});
} elsif ($a[1] eq "lowtemp-offset") {
return "Invalid lowtemperature-offset, use N" if($val !~ m/^\d*\.?\d+$/);
# additional check for temperature
return "Invalid lowtemperature-offset, must between 1 and 5" if($val < 1 || $val > 5);
my $a = int($val);
$arg .= sprintf("%02x", $a);
$ret = sprintf("Rounded temperature to %d.0", $a) if($a != $val);
$val = "$a.0";
} else { # Holiday1, Holiday2
$arg .= sprintf("%02x", $val);
}
my $dev = $hash->{CODE};
my $def = $defptr{$dev};
my $name = $def->{NAME};
my $type = $a[1];
my $sbCount = keys(%{$def->{SENDBUFFER}}); # Count of sendbuffer
# get firsttime hardware buffer of FHZ if $fhzHardwareBufferSpace not set
$fhzHardwareBufferSpace = getFhzBuffer () if ($fhzHardwareBufferSpace == 0);
# set default values for config value attr FHZ softbuffer
$attr{FHZ}{softbuffer} = 1 if (!defined($attr{FHZ}{softbuffer}));
$val = "" if (!defined($val));
if ( ($sbCount == 0 && $fhzHardwareBufferSpace >= $minFhzHardwareBufferSpace) || $attr{FHZ}{softbuffer} == 0) {
sendCommand ($hash, $arg, $name, $type, $val); # send command direct to FHZ
} else {
Log GetLogLevel($name,2), "FHT set $name $type $val (Enqueue to buffer)" if ($fhzHardwareBufferSpace >= $minFhzHardwareBufferSpace);
Log GetLogLevel($name,2), "Can't send command set $name $type $val. " .
"No space left in FHZ hardware buffer." if($fhzHardwareBufferSpace < $minFhzHardwareBufferSpace);
}
# only if softbuffer not disabled via config
if ($attr{FHZ}{softbuffer} == 1) {
toSendbuffer ($hash, $type, $val, $arg, "", 0); # send command also to buffer
if ($timerCheckBufferIsRunning == 0 && $init_done) {
$timerCheckBufferIsRunning = 1; # set $timerCheckBufferIsRunning to 1 to remeber a timer is running
InternalTimer(gettimeofday()+70, "timerCheckBuffer", $hash); # start internal Timer to periodical check the buffer
}
}
return $ret;
}
# Send command to FHZ
#
sub sendCommand ($$$$$)
{
my ($hash, $arg, $name, $type, $val) = @_;
if($type eq "refreshvalues") {
# This is special. Without the sleep the next FHT won't send its data
if(!IsDummy($a[0])) {
if(!IsDummy($name)) {
my $havefhz;
$havefhz = 1 if($hash->{IODev} && defined($hash->{IODev}->{FD}));
@ -174,36 +273,40 @@ FHT_Set($@)
IOWrite($hash, "04", "c90185"); # Check the fht buffer
sleep(1) if($havefhz);
}
return $ret;
} elsif($a[1] =~ m/-temp/) {
return "Invalid temperature, use NN.N" if($a[2] !~ m/^\d*\.?\d+$/);
my $a = int($a[2]*2);
$arg .= sprintf("%02x", $a);
$ret = "Rounded temperature to " . $a/2 if($a/2 != $a[2]);
} elsif($a[1] =~ m/-from/ || $a[1] =~ m/-to/) {
return "Invalid timeformat, use HH:MM" if($a[2] !~ m/^([0-2]\d):([0-5]\d)/);
my $a = ($1*6) + ($2/10);
$arg .= sprintf("%02x", $a);
my $nt = sprintf("%02d:%02d", $1, ($2/10)*10);
$ret = "Rounded time to $nt" if($nt ne $a[2]);
} elsif($a[1] eq "mode") {
return "Invalid mode, use one of " . join(" ", sort keys %m2c)
if(!defined($m2c{$a[2]}));
$arg .= sprintf("%02x", $m2c{$a[2]});
} else { # Holiday1, Holiday2
$arg .= sprintf("%02x", $a[2]);
} else {
IOWrite($hash, "04", $arg) if(!IsDummy($name));
}
IOWrite($hash, "04", $arg) if(!IsDummy($a[0]));
return $ret;
Log GetLogLevel($name,2), "FHT set $name $type $val";
# decrease $fhzHardwareBufferSpace for each command sending to the FHZ
$fhzHardwareBufferSpace = $fhzHardwareBufferSpace -5 if(!IsDummy($name));
}
sub resendCommand ($)
{
my ($buffer) = @_;
my $hash = $buffer->{HASH};
my $dev = $hash->{CODE};
my $def = $defptr{$dev};
my $nRetry = $buffer->{RETRY} + 1;
if ($fhzHardwareBufferSpace > $minFhzHardwareBufferSpace) {
Log GetLogLevel($def->{NAME},2), "Resending command to FHT set " . $def->{NAME} . " " . $buffer->{TYPE} . " " . $buffer->{VAL} .
" (Retry $nRetry / ". $attr{FHZ}{softmaxretry} . ")";
sendCommand ($buffer->{HASH}, $buffer->{ARG}, $buffer->{NAME}, $buffer->{TYPE}, $buffer->{VAL});
toSendbuffer ($buffer->{HASH}, $buffer->{TYPE}, $buffer->{VAL}, $buffer->{ARG}, $buffer->{KEY}, $nRetry); # send command also to buffer
} else {
Log GetLogLevel($def->{NAME},2), "Can't send command \"set " . $def->{NAME} . " " . $buffer->{TYPE} . " " . $buffer->{VAL} .
"\". No space in FHZ hardware buffer left. Resending next time if free bufferspace available.";
}
}
#####################################
sub
FHT_SetState($$$$)
@ -252,24 +355,24 @@ FHT_Undef($$)
sub
FHT_Parse($$)
{
my ($hash,$msg) = @_;
my ($hash, $msg) = @_;
my $dev = substr($msg, 16, 4);
my $cde = substr($msg, 20, 6);
my $val = substr($msg, 26, 2) if(length($msg) > 26);
my $confirm = 0;
$fhzHardwareBufferSpace = getFhzBuffer () if ($fhzHardwareBufferSpace == 0);
if(!defined($defptr{$dev})) {
Log 3, "FHT Unknown device $dev, please define it";
return "UNDEFINED FHT $dev";
}
my $def = $defptr{$dev};
# Unknown, but don't want report it. Should come with c409c401
if($cde eq "00") {
return "";
}
return "" if($cde eq "00");
if(length($cde) < 6) {
my $name = $def->{NAME};
@ -284,7 +387,18 @@ FHT_Parse($$)
# it looks like a real message, and let the rest parse it
Log 4, "FHT $def->{NAME} confirmation: $cde)";
$val = substr($cde, 2, 2);
# get the free hardware buffer space in the FHZ after each confirmation message
$fhzHardwareBufferSpace = hex substr($cde, 4, 2);
# increase $fhzHardwareBufferSpace at 5 because the confirmed command is deleted in the FHZ after confirmation
$fhzHardwareBufferSpace = $fhzHardwareBufferSpace + 5;
Log 4, "FHZ new FHT Buffer: $fhzHardwareBufferSpace";
$cde = substr($cde, 0, 2) . "0069";
# set help var to remember this is a confirmation
$confirm = 1;
}
my $type;
@ -303,7 +417,6 @@ FHT_Parse($$)
return $def->{NAME};
}
my $tn = TimeNow();
###########################
@ -348,13 +461,26 @@ FHT_Parse($$)
} elsif($type =~ m/.*-temp/) {
$val = sprintf("%.1f (Celsius)", $val / 2)
} elsif($type eq "state") {
} elsif($type eq "warnings") {
my $nval;
$nval = "Bat: " . (($val & 1) ? "empty" : "ok");
$nval .= ", Window: " . (($val & 32) ? "open" : "closed");
$nval .= ", Fault: " . (($val & 16) ? "yes" : "no");
$val = $nval;
my @nVal;
$nVal[0] = "Battery low" if ($val & 1);
$nVal[1] = "Window open" if ($val & 32);
$nVal[2] = "Fault on window sensor" if ($val & 16);
$nVal[3] = "Temperature to low" if ($val & 2);
if ($val > 0) {
$val = "";
foreach (@nVal) {
$val .= "$_; " if (defined($_));
}
$val = substr($val, 0, length($val)-2);
} else {
$val = "none";
}
} elsif($type eq "lowtemp-offset") {
$val = sprintf("%d.0 (Celsius)", $val)
} elsif($type =~ m/echo_/) { # Ignore these messages
return "";
@ -365,9 +491,192 @@ FHT_Parse($$)
$def->{READINGS}{$type}{VAL} = $val;
Log 4, "FHT $def->{NAME} ($type: $val)";
###########################################################################
# here starts the processing the confirmation to control the softwarebuffer
#
$attr{FHZ}{softbuffer} = 1 if (!defined($attr{FHZ}{softbuffer})); # set default values for config value attr FHZ softbuffer
my $sbCount = keys(%{$def->{SENDBUFFER}}); # count the existing sendbuffer
my $nsCount = keys(%{$def->{NOTSEND}}); # count the existing failbuffer
if ($confirm && ($sbCount > 0 || $nsCount > 0) && $attr{FHZ}{softbuffer} == 1) {
$type = "refreshvalues" if ($type eq "init");
my ($sbPr, $sbTs);
my $sbType = "";
my $sbVal;
my $dKey;
my ($val2) = split (/\s/, $val);
# if the confirmation message for a command recive to late
# (the command moved to the notsend list yet)
# found the specific command ond delete them from the notsend list
foreach my $c (sort keys %{$def->{NOTSEND}}) { # go through the notsend list
($sbPr, $sbTs, $sbType) = split (/:/, $c);
$sbVal = $def->{NOTSEND}->{$c}{VAL};
$dKey = $c;
$sbVal = $val2 if ($type eq "refreshvalues"); # refreshvalues have no value
if ($sbType eq $type && $sbVal eq $val2) {
Log GetLogLevel($def->{NAME},2), "FHT $def->{NAME} late - confirmation ".
"($sbType: $sbVal) (delete from NOTSEND)";
delete($def->{NOTSEND}{$dKey}); # delete command from notsend list
last; # we can leave the loop because the command was deleted from the list
}
}
# get the next entry from the buffer queue
foreach my $c (sort keys %{$def->{SENDBUFFER}}) {
($sbPr, $sbTs, $sbType) = split (/:/, $c);
$sbVal = $def->{SENDBUFFER}->{$c}{VAL};
$dKey = $c;
last; # exit foreach because we need the first entry only
}
$sbVal = $val2 if ($type eq "refreshvalues"); # refreshvalues have no value
# if the actual confirmation message part of the first command in the queue
if ($sbType eq $type && $sbVal eq $val2) {
delete($def->{SENDBUFFER}{$dKey}); # this buffer entry can deleted
foreach my $c (sort keys %{$def->{SENDBUFFER}}) { # get the next buffer entry
my $nType = $def->{SENDBUFFER}->{$c}{TYPE};
my $nArg = $def->{SENDBUFFER}->{$c}{ARG};
my $nName = $def->{SENDBUFFER}->{$c}{NAME};
my $nHash = $def->{SENDBUFFER}->{$c}{HASH};
my $nVal = $def->{SENDBUFFER}->{$c}{VAL};
my $nKey = $def->{SENDBUFFER}->{$c}{KEY};
sendCommand ($nHash, $nArg, $nName, $nType, $nVal); # nächsten Buffereintrag senden
toSendbuffer ($nHash, $nType, $nVal, $nArg, $nKey, 0); # send command also to buffer
last; # exit foreach because we need the next entry only
}
}
}
#
# end processing confirmation to control the softwarebuffer
###########################################################################
$def->{CHANGED}[0] = "$type: $val";
$def->{STATE} = "$type: $val" if($type eq "measured-temp");
return $def->{NAME};
}
# check are commands in softwarebuffer
# ans send the next command to the FHZ
sub timerCheckBuffer ($)
{
Log 4, "Timer (Checking for unsend FHT commands)";
my ($hash) = @_;
my $bufCount = 0; # help counter
my $now = gettimeofday();
my $ts = time;
# set default values for config value attr FHZ softbuffer
$attr{FHZ}{softrepeat} = 240 if (!defined($attr{FHZ}{softrepeat}));
$attr{FHZ}{softmaxretry} = 3 if (!defined($attr{FHZ}{softmaxretry}));
# loop to process all FHT devices
foreach my $d (keys %defptr) {
my $def = $defptr{$d}; # the actual FHT device
# process all buffer entries
foreach my $c (sort keys %{$def->{SENDBUFFER}}) {
my ($rPr, undef, $rType) = split (/:/, $c); # priority and type
my $rVal = $def->{SENDBUFFER}{$c}{VAL}; # value
my $rTs = $def->{SENDBUFFER}{$c}{SENDTIME}; # the time of the sending moment to the FHT
my $rRetry = $def->{SENDBUFFER}{$c}{RETRY}; # retry counter
$rRetry ++ if ($fhzHardwareBufferSpace > $minFhzHardwareBufferSpace); # increase retrycounter if enough hardwarebuffer available
my $rKey = $c; # the bufferkey
$rVal = "" if (!defined($rVal)); # set value to "" if value not defined (e.g. "refreshvalues" have no value)
$bufCount ++; # increase $bufCount
my $buffer = $def->{SENDBUFFER}{$c}; # actual buffer entry
# if the forst command in buffer to old, resend them again to the FHZ
if ($ts-$rTs > $attr{FHZ}{softrepeat}) {
if ($rRetry <= $attr{FHZ}{softmaxretry}) { # resend the command only if the max resend amount not reached
resendCommand ($buffer); # resend the actual command
} else {
# command resend fail after "softmaxretry" attempt to send
Log GetLogLevel($def->{NAME},2), $def->{NAME} . " $rType $rVal no confirmation after $rRetry retry";
$def->{NOTSEND}{$rKey} = $def->{SENDBUFFER}{$rKey}; # put the buffer entry to the notsend list
$def->{NOTSEND}{$rKey}{RETRY} = $rRetry;
delete($def->{SENDBUFFER}{$rKey}); # delete command from buffer queue
}
}
last # exit foreach because we need only the first buffer value
}
}
if ($bufCount > 0) {
Log 4, "Refresh FHT resend timer";
InternalTimer(gettimeofday()+70, "timerCheckBuffer", $hash); # restart the internal Timer if any buffer contains commands
} else {
$timerCheckBufferIsRunning = 0; # remember timer is not running anymore
}
}
# set given command tothe internal software buffer
# each command queued until the previous command become a confirmation
#
sub toSendbuffer ($$$$)
{
my ($hash, $type, $val, $arg, $nBufferKey, $retry) = @_;
if (!$init_done || $attr{FHZ}{softbuffer} == 0) {
return
}
my $dev = $hash->{CODE};
my $def = $defptr{$dev};
my $tn = TimeNow(); # Readable time
my $ts = time; # Unix timestamp
my $pr = 9; # Default priority for command
my $sendTime = 0; # Timestamp for last sending command
my $sbCount = keys(%{$def->{SENDBUFFER}}); # Count of sendbuffer
$pr = $priority{$type} if (defined($priority{$type})); # get priority for specific command type
$val = "" if (!defined($val));
if ($sbCount == 0) {
$pr = 0; # First command in buffer have always priority 0 (highest)
$sendTime = $ts;
}
my $bufferKey = "$pr:$ts:$type"; #Default bufferkey
# if bufferkey existing. delete the entry and save the entry with a new buffer
if ($nBufferKey ne "") {
$sendTime = $ts;
$bufferKey = $nBufferKey;
($pr, $ts, $type) = split (/:/, $bufferKey);
delete($def->{SENDBUFFER}{$bufferKey}); # delete "old" bufferentry
$bufferKey = "0:$ts:$type"; # new bufferkey für new bufferentry
}
$def->{SENDBUFFER}{$bufferKey}{TIME} = $tn;
$def->{SENDBUFFER}{$bufferKey}{VAL} = $val;
$def->{SENDBUFFER}{$bufferKey}{NAME} = $def->{NAME};
$def->{SENDBUFFER}{$bufferKey}{TYPE} = $type;
$def->{SENDBUFFER}{$bufferKey}{ARG} = $arg;
$def->{SENDBUFFER}{$bufferKey}{SENDTIME} = $sendTime;
$def->{SENDBUFFER}{$bufferKey}{RETRY} = $retry;
$def->{SENDBUFFER}{$bufferKey}{KEY} = $bufferKey;
$def->{SENDBUFFER}{$bufferKey}{HASH} = $hash;
}
1;

View File

@ -147,3 +147,13 @@
- weblinks added. Used by webpgm2 to display more than one plot at once
- webpgm2 output reformatted. Using CSS to divide the screen area in 3
parts: command line, room-list and rest
- Dirk Wed Oct 7 12:45:09 MEST 2007
- FHT lime-protection code discovered
- Dirk Wed Oct 18 23:28:00 MEST 2007
- Softwarebuffer for FHT devices with queuing unsent commands and
repeating commands by transmission failure
- FHT low temperatur warning and setting for lowtemp-offset
- Change naming for state into warnings
Tagged as dirkh_20071019_0

View File

@ -56,7 +56,7 @@ You can use all of the following commands in in two ways:
<a href="#define">define</a> lamp FS20 8765 01</pre>
For other configuration files see the examples subdirectory.<br>
<br>
</li>
</li>
<li>Through the TCP/IP connection, which you can either use in a "session"
(via telnet) or single client command (via fhem.pl). Example:
@ -70,7 +70,7 @@ You can use all of the following commands in in two ways:
<ul>
<code>fhem.pl 7072 "set lamp off"</code>
</ul>
</li>
</li>
</ul>
There are three types of commands: "fhem" commands (described in this document),
@ -149,7 +149,7 @@ split in multiple lines<br><br>
lost. E.g. it makes sense to set the <a href="FHZset">FHTcode</a> in
this file or the coordinates of your house via
<a href="#at">sunrise_coord</at>
</li><br>
</li><br>
<a name="logfile"></a>
<li>logfile<br>
@ -157,7 +157,7 @@ split in multiple lines<br><br>
stdout, in this case the server won't background itself.<br>
The logfile name can also take wildcards for easier logfile rotation,
see the <a href="#FileLog">FileLog</a> section.
</li><br>
</li><br>
<a name="modpath"></a>
<li>modpath<br>
@ -165,7 +165,7 @@ split in multiple lines<br><br>
should <b>not</b> contain the directory FHEM. Every file there with the
name pattern &lt;number&gt;_&lt;name&gt;.pm will be loaded in the order
of the number.
</li><br>
</li><br>
<a name="pidfilename"></a>
<li>pidfilename<br>
@ -173,12 +173,12 @@ split in multiple lines<br><br>
server runs as a daemon, and some distributions would like to check by
the pid if we are still running. The file will be deleted upon
shutdown.
</li><br>
</li><br>
<a name="pidfilename"></a>
<li>pidfilename<br>
Works for the global "device"<br>
</li><br>
</li><br>
<a name="port"></a>
<li>port<br>
@ -187,14 +187,14 @@ split in multiple lines<br><br>
will only listen for connections from the localhost per default. If
there is a second value "global" then the server will listen for
non-localhost connections too.
</li><br>
</li><br>
<a name="statefile"></a>
<li>statefile<br>
Set the filename where the state and certain <a href="#at">at</a>
information will be saved before shutdown. If it is not specified, then
no information will be saved.
</li><br>
</li><br>
<a name="title"></a>
<li>title<br>
@ -223,7 +223,7 @@ split in multiple lines<br><br>
<li>5 - is for debugging.</li>
</ul>
Recommended level is 3 for normal use.
</li>
</li>
</ul>
<br>
@ -328,29 +328,29 @@ split in multiple lines<br><br>
Plots on as 1 and off as 0. The corresponding filelog definition
for the device fs20dev is:<br>
define FileLog fslog fs20dev /var/log/fs20dev-%Y-%U.log
</li>
</li>
<li>fht<br>
Plots the measured-temp/desired-temp/actuator lines. The
corresponding filelog definitions (for the FHT device named
fht1) looks like:<br>
define FileLog fhtlog1 fht1:.*(temp|actuator).*
/var/log/fht1-%Y-%U.log
</li>
</li>
<li>ks300_1<br>
Plots the temperature and rain (per hour and per day) of a
ks300. The corresponding filelog definitions (for the KS300
device named ks300) looks like:<br>
define FileLog ks300log ks300:.*H:.*
/var/log/ks300-%Y-%U.log
</li>
</li>
<li>ks300_2<br>
Plots the humidity and wind values of a
ks300. The corresponding filelog definition is the same as
above, both programs evaluate the same log.
</li>
</li>
<li>text<br>
Shows the logfile as it is (plain text).
</li>
</li>
</ul>
The corresponding gnuplot files (up to the "text" one) must be
installed, see the fhemweb.pl configuration for the destination
@ -433,9 +433,27 @@ split in multiple lines<br><br>
<a name="skip_next"></a>
<li>skip_next<br>
Can be applied to at devices.<br>
Used for at commands: skip the execution of the command the next time.
</li><br>
Used for at commands: skip the execution of the command the next time.</li><br />
<a name="softbuffer" id="softbuffer"></a>
<li>softbuffer<br />
Can be applied to FHZ devices.<br />
Used to disable the FHZ softbuffer for FHT deviced (enabled by default).<br />
<strong>Note:</strong> By disabling the softbuffer FHEM works like in version 4.1. It is posible to lost commands to FHT devices by overflow the FHZ hardwarebuffer or on transmission failures.<br />
<br />
<a name="softbuffer" id="softbuffer"></a> </li>
<li>softrepeat<br />
Can be applied to FHZ devices.<br />
Used to set the repeating time while FHEM tries to resend a FHT command which failed (default 240).<br />
<strong>Note:</strong> Don't set this time to small. In this case it is posible that the FHZ Hardwarebuffer runs full in a short time. Than it is posible it takes long time to process the following commands. Normaly it should not necesary to change this value.<br />
<br />
<a name="softbuffer" id="softbuffer"></a> </li>
<li>softmaxretry<br />
Can be applied to FHZ devices.<br />
Used to set the maximal retries in which FHEM try to resend a failed command to FHT devices (default 3).<br />
<strong>Note:</strong> Don't set this value to height. In this case it is posible that the FHZ Hardwarebuffer runs full in a short time. Than it is posible it takes long time to process the following commands. Normaly it should not necesary to change this value. <br />
<br>
</li>
</ul>
Examples:
@ -443,8 +461,11 @@ split in multiple lines<br><br>
<code>attr global verbose 3</code><br>
<code>attr lamp room kitchen</code><br>
<code>attr lamp dummy</code><br>
<code>attr lamp loglevel 6</code><br>
</ul>
<code>attr lamp loglevel 6</code><br />
<br>
<code>attr FHZ softbuffer 0 # disable the softbuffer for FHT devices</code><br />
<code>attr FHZ softrepeat 300 # resend failed commands to FHT devices after 300 seconds </code><br />
<code>attr FHZ softmaxretry 4 # stop resending failed commands to FHT devices after 4 retries.</code> </ul>
<br>
Notes:<br>
@ -829,7 +850,7 @@ split in multiple lines<br><br>
this case the command will be saved to the statefile (as it
considered volatile, i.e. entered by cronjob) and not to the
configfile (see the <a href="#save">save</a> command.)
</li>
</li>
<li>if the current time is greater then the time specified, then the
command will be executed tomorrow. This is why the relative forms
@ -842,12 +863,12 @@ split in multiple lines<br><br>
file, as in the above example. If you are not using sunrise_coord, then
the coordinates for Frankfurt am Main, Germany will be used.
You also have to install the Datetime::Event::Sunrise perl module.
</li>
</li>
<li>For even more complex date handling you either have to call fhem from
cron or filter the date in a perl expression, see the last example and
the section <a href="#perl">Perl special</a>.
</li>
</li>
</ul>
<br>
</ul>
@ -992,7 +1013,7 @@ split in multiple lines<br><br>
<ul>
<li>The mentioned codes are needed for initializing the FHZ1X00</li>
<li>The answer for a command is also displayed by <code>list FHZ</code>
</li>
</li>
<li>
The FHZ1000PC has a message buffer for the FHT, as it
only can send messages to it every 2 (or so) minutes. If the buffer
@ -1105,6 +1126,7 @@ split in multiple lines<br><br>
2006-11-02 04:13:31 fri-from1 06:00
2006-11-02 04:13:31 fri-to1 23:50
2006-11-02 04:11:30 init 255
2006-11-02 04:13:36 lowtemp-offset 2.0 (Celsius)
2006-11-02 10:40:06 measured-high 0
2006-11-02 10:40:06 measured-low 211
2006-11-02 10:40:06 measured-temp 21.1 (Celsius)
@ -1114,18 +1136,18 @@ split in multiple lines<br><br>
2006-11-02 04:13:35 night-temp 18.0 (Celsius)
2006-11-02 04:13:32 sat-from1 08:00
2006-11-02 04:13:33 sat-to1 23:50
2006-11-02 10:40:07 state Bat: ok, Window: closed
2006-11-02 04:13:34 sun-from1 08:00
2006-11-02 04:13:34 sun-to1 23:00
2006-11-02 04:13:29 thu-from1 06:00
2006-11-02 04:13:29 thu-to1 23:00
2006-11-02 04:13:26 tue-from1 06:00
2006-11-02 04:13:26 tue-to1 23:00
2006-11-02 04:13:36 unknown_85 4
2006-11-02 10:40:07 warnings Battery low
2006-11-02 04:13:27 wed-from1 06:00
2006-11-02 04:13:28 wed-to1 23:00
2006-11-02 04:13:36 windowopen-temp 12.0 (Celsius)
Send buffer:<br /> 2007-10-19 00:31:24 desired-temp 22.5
2007-10-19 00:33:20 mode auto<br />
</code></pre>
</ul>
@ -1307,7 +1329,7 @@ split in multiple lines<br><br>
Notes:
<ul>
<li>Use reset with care: the device forgets even the housecode.
</li>
</li>
<li>As the FS20 protocol needs about 0.22 seconds to transmit a
sequence, a pause of 0.22 seconds is inserted after each command.
</li>
@ -1377,7 +1399,6 @@ split in multiple lines<br><br>
mode
holiday1
holiday2
mode
desired-temp
day-temp
night-temp
@ -1386,11 +1407,15 @@ split in multiple lines<br><br>
day
hour
minute
refreshvalues</pre>
refreshvalues
lowtemp-offset
</pre>
Notes:
<ul>
<li>All <code>*-temp</code> values need a temperature
as argument, which will be rounded to 0.5 Celsius</li>
as argument, which will be rounded to 0.5 Celsius.<br />
Temperature values
must between 5.5 and 30.5 Celsius. Value 5.5 set the actuator to OFF, value 30.5 set the actuator to ON </li>
<li><code>mode</code> is one of <code>auto, manual, holiday or
holiday_short.<br>If the mode is holiday, then
<ul>
@ -1402,7 +1427,7 @@ split in multiple lines<br><br>
<li>holiday1 sets the time, in 10-minute steps</li>
<li>holiday2 sets number of days from now on.</li>
</ul>
</li>
</li>
<li>The <code>*-from1/*-from2/*-to1/*-to2</code> valuetypes need a time
spec as argument in the HH:MM format. They define the periods, where
the day-temp is valid. The minute (MM) will be rounded to 10, and
@ -1410,13 +1435,22 @@ split in multiple lines<br><br>
<li><code>refreshvalues</code> does not need an argument, but sends a
plea to the FHT device, so it may send its parameters. If you want
to get these values regularly, then schedule:<br>
<code>at +*01:00:00 set &lt;name&gt; refreshvalues</code><br>
</li>
<code>at +*01:00:00 set &lt;name&gt; refreshvalues</code></li>
<li><code>lowtemp-offset</code> need a temperature
as argument, which will be rounded to 1.0 Celsius.<br />
Temperature values must between 1.0 and 5.0 Celsius.<br />
The
<code>lowtemp-offset</code> is used for temperature warning (to low temperature in a room within more than 1,5 hours after the last desired-temp changing). The low temperatur warning it relates to the actual desired temperature.<br>
</li>
<li>The FHT is very economical (or lazy), it receives messages from the
FHZ1000 every 2 minutes (or so). Don't be surprized if your command
FHZ1x00 every 2 minutes (or so). Don't be surprized if your command
is accepted 10 minutes later by the device. See the related
<code>fhtbuf</code> entry in the <code><a href="#get">get</a></code>
section.
section.<br />
<li>For FHT devices, FHEM have an internal software buffer for sending commands step by step. This buffer should prevent lost commands. In case of transmission failures FHEM tries to resend commands. You can see the queued unsent and the failed commands with the <a href="#list">list</a> command. If a command don't picked up from a FHT device within a <a href="#softrepeat">given time</a>, the command sends again until <a href="#softmaxretry">softmaxretry</a> reached. <br />
<li>To prevent long time waiting for sending important command, there is a priority list. Commands send in this importance:<br />
<code>desired-temp, mode, refreshvalues,holiday1,holiday2,day-temp,night-temp, [all other commands]</code> <br />
<br />
</ul>
</ul>

View File

@ -31,6 +31,10 @@ Formerly known as fhz1000.pl
<li> bugfix: SCIVT solar controller (peterp, 1.7)
<li> bugfix: WS300 loglevel change (from 2 to 5 or device specific loglevel)
<li> feature: First steps for a Fritz!Box port. See the fritzbox.html
<li>feature: FHT lime-protection code discovered by Dirk (7.10)
<li>feature: Softwarebuffer for FHT devices with queuing unsent commands and repeating commands by transmission failure (Dirk 17.10)
<li> feature: FHT low temperatur warning and setting for lowtemp-offset (Dirk 17.10)
<li>change: Change naming for state into warnings (Dirk 17.10)
</ul>
</ul>
@ -51,7 +55,9 @@ Currently implemented features:<br>
<li>reading and sending FS20 events (on/off/dimming, timer commands)<br>
<li>support of FS20 address features function group, local and global master
<li>reading and changing FHT80b parameters (temp, actuator, etc).<br>
The FHT8b seems to work too. <b>Note:</b> the FHT8 wont work.</li>
The FHT8b seems to work too. <b>Note:</b> the FHT8 wont work.<br />
For FHT8b devices, FHEM have an internal software buffer for sending commands step by step. This buffer should prevent lost commands. In case of transmission failures FHEM tries to resend commands. <br />
</li>
<li>reading HMS data (HMS100-T,-TF,-WD,-MG,-TFK and RM100-2)</li>
<li>reading KS300 data</li>
</ul>

View File

@ -134,7 +134,7 @@ my %intAt; # Internal at timer hash.
my $intAtCnt=0;
my $reread_active = 0;
my $AttrList = "room comment";
my $cvsid = '$Id: fhem.pl,v 1.26 2007-09-24 07:09:17 rudolfkoenig Exp $';
my $cvsid = '$Id: fhem.pl,v 1.27 2007-10-19 09:29:00 dirkh Exp $';
$init_done = 0;
@ -1044,10 +1044,58 @@ CommandList($$)
if($r) {
$str .= "Readings:\n";
foreach my $c (sort keys %{$r}) {
$str .= sprintf(" %-19s %-15s %s\n",$r->{$c}{TIME},$c,$r->{$c}{VAL});
my $val = "";
if (defined($r->{$c}{VAL})) {
$val = $r->{$c}{VAL};
}
$str .= sprintf(" %-19s %-15s %s\n",$r->{$c}{TIME},$c,$val);
}
}
$attr{FHZ}{softbuffer} = 1 if (!defined($attr{FHZ}{softbuffer}));
if ($attr{FHZ}{softbuffer} == 1) {
my %lists = (
"SENDBUFFER" => "Send buffer",
"NOTSEND" => "Send fail list",
);
foreach my $list (keys %lists) {
my $l = $d->{$list};
if(keys (%{$l}) > 0) {
$str .= $lists{$list} .":\n";
foreach my $c (sort keys %{$l}) {
my (undef, undef, $vC) = split (/:/, $c);
# $str .= sprintf(" %-19s %-15s %-10s %s\n",$l->{$c}{TIME},$c,$l->{$c}{VAL},$l->{$c}{SENDTIME});
$str .= sprintf(" %-19s %-15s %s\n",$l->{$c}{TIME},$vC,$l->{$c}{VAL});
}
}
if ($defs{$param}->{NAME} eq "FHZ") {
$str .= $lists{$list} .":\n";
foreach my $d (sort keys %defs) {
my $p = $defs{$d};
my $t = $p->{TYPE};
if ($t eq "FHT") {
$l = $p->{$list};
if(keys (%{$l}) > 0) {
foreach my $c (sort keys %{$l}) {
my (undef, undef, $vC) = split (/:/, $c);
my $val = "";
if (defined($l->{$c}{VAL})) {
$val = $l->{$c}{VAL};
}
$str .= sprintf(" %-19s %-15s %-15s %s\n",$l->{$c}{TIME},$p->{NAME},$vC,$val);
}
}
}
}
}
}
}
}
return $str;