2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-06 18:56:55 +00:00
fhem-mirror/fhem/FHEM/11_FHT.pm
rudolfkoenig 2fcfcdb9a6 Debug removed
git-svn-id: https://svn.fhem.de/fhem/trunk@2414 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2013-01-03 13:32:48 +00:00

1027 lines
30 KiB
Perl
Executable File

# $Id$
##############################################################################
#
# 11_FHT.pm
# Copyright by
# e-mail:
#
# 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.
#
# Foobar 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
package main;
use strict;
use warnings;
sub doSoftBuffer($);
sub softBufferTimer($);
sub getFhtMin($);
sub getFhtBuffer($);
my %codes = (
"00" => "actuator",
"01" => "actuator1",
"02" => "actuator2",
"03" => "actuator3",
"04" => "actuator4",
"05" => "actuator5",
"06" => "actuator6",
"07" => "actuator7",
"08" => "actuator8",
"14" => "mon-from1",
"15" => "mon-to1",
"16" => "mon-from2",
"17" => "mon-to2",
"18" => "tue-from1",
"19" => "tue-to1",
"1a" => "tue-from2",
"1b" => "tue-to2",
"1c" => "wed-from1",
"1d" => "wed-to1",
"1e" => "wed-from2",
"1f" => "wed-to2",
"20" => "thu-from1",
"21" => "thu-to1",
"22" => "thu-from2",
"23" => "thu-to2",
"24" => "fri-from1",
"25" => "fri-to1",
"26" => "fri-from2",
"27" => "fri-to2",
"28" => "sat-from1",
"29" => "sat-to1",
"2a" => "sat-from2",
"2b" => "sat-to2",
"2c" => "sun-from1",
"2d" => "sun-to1",
"2e" => "sun-from2",
"2f" => "sun-to2",
"3e" => "mode",
"3f" => "holiday1", # Not verified
"40" => "holiday2", # Not verified
"41" => "desired-temp",
"XX" => "measured-temp", # sum of next. two, never really sent
"42" => "measured-low",
"43" => "measured-high",
"44" => "warnings",
"45" => "manu-temp", # No clue what it does.
"4b" => "ack",
"53" => "can-xmit",
"54" => "can-rcv",
"60" => "year",
"61" => "month",
"62" => "day",
"63" => "hour",
"64" => "minute",
"65" => "report1",
"66" => "report2",
"69" => "ack2",
"7d" => "start-xmit",
"7e" => "end-xmit",
"82" => "day-temp",
"84" => "night-temp",
"85" => "lowtemp-offset", # Alarm-Temp.-Differenz
"8a" => "windowopen-temp",
);
my %cantset = (
"actuator" => 1,
"actuator1" => 1,
"actuator2" => 1,
"actuator3" => 1,
"actuator4" => 1,
"actuator5" => 1,
"actuator6" => 1,
"actuator7" => 1,
"actuator8" => 1,
"ack" => 1,
"ack2" => 1,
"battery" => 1,
"can-xmit" => 1,
"can-rcv" => 1,
"start-xmit" => 1,
"end-xmit" => 1,
"lowtemp" => 1,
"measured-temp" => 1,
"measured-high" => 1,
"measured-low" => 1,
"warnings" => 1,
"window" => 1,
"windowsensor" => 1,
);
# additional warnings
my %warnings = (
"battery" => 1,
"lowtemp" => 1,
"window" => 1,
"windowsensor" => 1,
);
my %priority = (
"desired-temp"=> 1,
"mode" => 2,
"report1" => 3,
"report2" => 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; # command->button hash (settable values)
my $defmin = 0; # min fhtbuf free bytes before sending commands
my $retryafter = 240; # in seconds, only when fhtsoftbuffer is active
my $cmdcount = 0;
#####################################
sub
FHT_Initialize($)
{
my ($hash) = @_;
foreach my $k (keys %codes) {
my $v = $codes{$k};
$c2b{$v} = $k;
$c2bset{$v} = $k if(!$cantset{$v});
}
foreach my $k (keys %c2m) {
$m2c{$c2m{$k}} = $k;
}
# 810c0426 0909a001 1111 1600
# 810c04b3 0909a001 1111 44006900
# 810b0402 83098301 1111 41301d
# 81090421 c409c401 1111 00
# 810c0d20 0909a001 3232 7e006724 (NYI)
$hash->{Match} = "^81..(04|09|0d)..(0909a001|83098301|c409c401)..";
$hash->{SetFn} = "FHT_Set";
$hash->{DefFn} = "FHT_Define";
$hash->{UndefFn} = "FHT_Undef";
$hash->{ParseFn} = "FHT_Parse";
$hash->{AttrList} = "IODev do_not_notify:1,0 model:fht80b dummy:1,0 " .
"showtime:1,0 loglevel:0,1,2,3,4,5,6 retrycount " .
"minfhtbuffer lazy tmpcorr ignore:1,0 ".
$readingFnAttributes;
}
sub
FHT_Set($@)
{
my ($hash, @a) = @_;
my $ret = "";
return "\"set $a[0]\" needs at least two parameters" if(@a < 2);
my $name = shift(@a);
# Replace refreshvalues with report1 and report2, and time with hour/minute
for(my $i = 0; $i < @a; $i++) {
splice(@a,$i,1,("report1","255","report2","255"))
if($a[$i] eq "refreshvalues");
if($a[$i] eq "time") {
my @t = localtime;
splice(@a,$i,1,("hour",$t[2],"minute",$t[1]));
}
if($a[$i] eq "date") {
my @t = localtime;
splice(@a,$i,1,("year",$t[5]-100,"month",$t[4]+1,"day",$t[3]));
}
}
my $ncmd = 0;
my $arg = "020183" . $hash->{CODE};
my ($cmd, $allcmd, $val) = ("", "", "");
my $lazy= defined($attr{$name}) &&
defined($attr{$name}{"lazy"}) &&
($attr{$name}{"lazy"}>0);
my $readings= $hash->{READINGS};
while(@a) {
$cmd = shift(@a);
if(!defined($c2b{$cmd})) {
my $cmdList = join(" ",sort keys %c2bset);
my @list = map { ($_.".0", $_+0.5) } (6..30);
pop @list;
my $tmpList="on,off,".join(",",@list);
$cmdList =~ s/-temp/-temp:$tmpList/g; # FHEMWEB sugar
$cmdList =~ s/(-from.|-to.)/$1:time/g;
return "Unknown argument $cmd, choose one of $cmdList";
}
return "Readonly parameter $cmd"
if(defined($cantset{$cmd}));
return "\"set $name $cmd\" needs a parameter"
if(@a < 1);
$val = shift(@a);
$arg .= $c2b{$cmd};
if ($cmd =~ m/-temp/) {
if(!($val eq "on" || $val eq "off" ||
($val =~ m/^\d*\.?\d+$/ && $val >= 5.5 && $val <= 30.5))) {
my @list = map { ($_.".0", $_+0.5) } (6..30);
pop @list;
return "Invalid temperature $val, choose one of on off "
. join(" ",@list);
}
$val = 30.5 if($val eq "on");
$val = 5.5 if($val eq "off");
my $a = int($val*2);
$arg .= sprintf("%02x", $a);
$val = sprintf("%.1f", $a/2);
} elsif($cmd =~ m/-from/ || $cmd =~ 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, int($2/10)*10);
$ret .= "Rounded $cmd to $nt" if($nt ne $val);
$val = $nt;
} elsif($cmd eq "mode") {
return "Invalid mode, choose one of " . join(" ", sort keys %m2c)
if(!defined($m2c{$val}));
$arg .= sprintf("%02x", $m2c{$val});
} elsif ($cmd eq "lowtemp-offset") {
return "Invalid lowtemperature-offset, must between 1 and 5"
if($val !~ m/^[1-5]$/);
$arg .= sprintf("%02x", $val);
$val = "$val.0";
} else { # Holiday1, Holiday2
return "Invalid argument, must be between 1 and 255"
if($val !~ m/^\d+$/ || $val < 0 || $val > 255);
$arg .= sprintf("%02x", $val) if(defined($val));
}
if($lazy &&
$cmd ne "report1" && $cmd ne "report2" && $cmd ne "refreshvalues" &&
defined($readings->{$cmd}) && $readings->{$cmd}{VAL} eq $val) {
$ret .= "Lazy mode ignores $cmd";
Log GetLogLevel($name,2), "Lazy mode ignores $cmd $val";
} else {
$ncmd++;
$allcmd .=" " if($allcmd);
$allcmd .= $cmd;
$allcmd .= " $val" if($val);
}
}
return "Too many commands specified, an FHT only supports up to 8"
if($ncmd > 8);
return $ret if(!$ncmd);
my $ioname = "";
$ioname = $hash->{IODev}->{NAME} if($hash->{IODev});
if($attr{$ioname} && $attr{$ioname}{fhtsoftbuffer}) {
my $io = $hash->{IODev};
my %h = (HASH => $hash, CMD => $allcmd, ARG => $arg);
my $prio = $priority{$cmd};
$prio = "9" if(!$prio);
my $key = $prio . ":" . gettimeofday() . ":" . $cmdcount++;
$io->{SOFTBUFFER}{$key} = \%h;
doSoftBuffer($io);
} else {
IOWrite($hash, "04", $arg);
Log GetLogLevel($name,2), "FHT set $name $allcmd";
}
return $ret;
}
#####################################
sub
FHT_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "wrong syntax: define <name> FHT CODE" if(int(@a) != 3);
$a[2] = lc($a[2]);
return "Define $a[0]: wrong CODE format: specify a 4 digit hex value"
if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/i);
$hash->{CODE} = $a[2];
AssignIoPort($hash);
# Check if the CULs id collides with our id.
if($hash->{IODev} && $hash->{IODev}{TYPE} eq "CUL") {
$hash->{IODev}{FHTID} =~ m/^(..)(..)$/;
my ($i1, $i2) = (hex($1), hex($2));
$a[2] =~ m/^(..)(..)$/;
my ($l1, $l2) = (hex($1), hex($2));
if($l2 == $i2 && $l1 >= $i1 && $l1 <= $i1+7) {
my $err = "$a[0]: CODE collides with the FHTID of the corresponding CUL";
Log 1, $err;
return $err;
}
}
$modules{FHT}{defptr}{$a[2]} = $hash;
$attr{$a[0]}{retrycount} = 3;
#Log GetLogLevel($a[0],2),"Asking the FHT device $a[0]/$a[2] to send its data";
#FHT_Set($hash, ($a[0], "report1", "255", "report2", "255"));
return undef;
}
#####################################
sub
FHT_Undef($$)
{
my ($hash, $name) = @_;
delete($modules{FHT}{defptr}{$hash->{CODE}}) if($hash && $hash->{CODE});
return undef;
}
#####################################
sub
FHT_Parse($$)
{
my ($hash, $msg) = @_;
$msg = lc($msg);
my $dev = substr($msg, 16, 4);
my $cde = substr($msg, 20, 2);
my $val = (length($msg) > 26 ? substr($msg, 26, 2) : undef);
my $confirm = 0;
if(!defined($modules{FHT}{defptr}{$dev})) {
Log 3, "FHT Unknown device $dev, please define it";
return "UNDEFINED FHT_$dev FHT $dev";
}
my $def = $modules{FHT}{defptr}{$dev};
my $name = $def->{NAME};
return "" if(IsIgnored($name));
my $io = $def->{IODev};
my $ll4 = GetLogLevel($name,4);
# Short message
if(length($msg) < 26) {
Log $ll4,"FHT Short message. Device $name, Message: $msg";
return "";
}
if($io->{TYPE} eq "CUL") {
$confirm = 1;
} elsif(!$val || $cde eq "65" || $cde eq "66") {
# This is a confirmation message. We reformat it so that
# it looks like a real message, and let the rest parse it
Log $ll4, "FHT $name confirmation: $cde";
$val = substr($msg, 22, 2);
$confirm = 1;
}
$val = hex($val);
my $cmd = $codes{$cde};
if(!$cmd) {
Log $ll4, "FHT $name (Unknown: $cde => $val)";
readingsSingleUpdate($def, "unknown_$cde", $val, 1);
return $name;
}
#
# special treatment for measured-temp which is actually sent in two bytes
#
# the measured temperature comes in two bytes: measured-low and measured-high
# measured-temp= (measured-high * 256 + measured-low) / 10.
# measured-low and measured-high will only be stored as internals
if($cmd eq "measured-low") {
$def->{".measuredLow"}= $val;
return "";
} elsif($cmd eq "measured-high") {
$def->{".measuredHigh"}= $val;
if(defined($def->{".measuredLow"})) {
$val = ($val*256.0 + $def->{".measuredLow"})/10.0+
AttrVal($name, "tmpcorr", 0.0);
$cmd = "measured-temp";
} else {
return "";
}
}
#
# from here readings are effectively updated
#
readingsBeginUpdate($def);
# The first four are confirmation messages, so they must be converted to
# the same format as the input (for the softbuffer)
if($cmd =~ m/-from/ || $cmd =~ m/-to/) {
$val = sprintf("%02d:%02d", $val/6, ($val%6)*10);
} elsif($cmd eq "mode") {
$val = $c2m{$val} if(defined($c2m{$val}));
} elsif($cmd =~ m/.*-temp/ && $cmd ne "measured-temp") {
$val = sprintf("%.1f", $val / 2);
if($cmd eq "desired-temp") {
$val = ($val > 30 ? "on" : ($val < 6 ? "off" : $val));
}
} elsif($cmd eq "lowtemp-offset") {
$val = sprintf("%d.0", $val)
} elsif($cmd =~ m/^actuator/) {
my $sval = lc(substr($msg,24,2));
my $fv = sprintf("%d%%", int(100*$val/255+0.5));
if($sval =~ m/[ab]0/) { $val = $fv; } # sync in the summer
elsif($sval =~ m/.0/) { $val = "syncnow"; }
elsif($sval =~ m/.1/) { $val = "99%" } # FHT set to 30.5, FHT80B=="ON"
elsif($sval =~ m/.2/) { $val = "0%" } # FHT set to 5.5
elsif($sval =~ m/.6/) { $val = "$fv" }
elsif($sval =~ m/.8/) { $val = "offset: " . ($val>128?(128-$val):$val) }
elsif($sval =~ m/[23]a/) { $val = "lime-protection" }
elsif($sval =~ m/[ab]a/) { $val = $fv } # lime protection bug
elsif($sval =~ m/.c/) { $val = sprintf("synctime: %d", int($val/2)-1); }
elsif($sval =~ m/.e/) { $val = "test" }
elsif($sval =~ m/.f/) { $val = "pair" }
else { $val = "unknown_$sval: $fv" }
} elsif($cmd eq "warnings") {
my $nVal;
# initialize values for additional warnings
my $valBattery;
my $valLowTemp;
my $valWindow;
my $valSensor;
my $nBattery;
my $nLowTemp;
my $nWindow;
my $nSensor;
# parse warnings
if($val & 1) {
$nVal = "Battery low";
$nBattery = "low";
}
if($val & 2) {
$nVal .= "; " if($nVal); $nVal .= "Temperature too low";
$nLowTemp = "warn";
}
if($val &32) {
$nVal .= ", " if($nVal); $nVal .= "Window open";
$nWindow = "open";
}
if($val &16) {
$nVal .= ", " if($nVal); $nVal .= "Fault on window sensor";
$nSensor = "fault";
}
# set default values or new values if they were changed
$valBattery = $nBattery? $nBattery : "ok";
$valLowTemp = $nLowTemp? $nLowTemp : "ok";
$valWindow = $nWindow? $nWindow : "closed";
$valSensor = $nSensor? $nSensor : "ok";
$val = $nVal? $nVal : "none";
# set additional warnings and trigger notify
readingsBulkUpdate($def, "battery", $valBattery);
Log $ll4, "FHT $name battery: $valBattery";
readingsBulkUpdate($def, "lowtemp", $valLowTemp);
Log $ll4, "FHT $name lowtemp: $valLowTemp";
readingsBulkUpdate($def, "window", $valWindow);
Log $ll4, "FHT $name window: $valWindow";
readingsBulkUpdate($def, "windowsensor", $valSensor);
Log $ll4, "FHT $name windowsensor: $valSensor";
}
if(substr($msg,24,1) eq "7") { # Do not store FHZ acks.
$cmd = "FHZ:$cmd";
} else {
readingsBulkUpdate($def, $cmd, $val);
if($cmd eq "measured-temp") {
readingsBulkUpdate($def, "state", "measured-temp: $val");
readingsBulkUpdate($def, "temperature", $val); # For dewpoint
}
}
Log $ll4, "FHT $name $cmd: $val";
#
# now we are done with updating readings
#
readingsEndUpdate($def, 1);
################################
# Softbuffer: delete confirmed commands
if($confirm) {
my $found;
foreach my $key (sort keys %{$io->{SOFTBUFFER}}) {
my $h = $io->{SOFTBUFFER}{$key};
my $hcmd = $h->{CMD};
my $hname = $h->{HASH}->{NAME};
Log $ll4, "FHT softbuffer check: $hname / $hcmd";
if($hname eq $name && $hcmd =~ m/^$cmd $val/) {
$found = $key;
Log $ll4, "FHT softbuffer found";
last;
}
}
delete($io->{SOFTBUFFER}{$found}) if($found);
}
return $name;
}
#####################################
# Check the softwarebuffer and send/resend commands
sub
doSoftBuffer($)
{
my ($io) = @_;
my $now = gettimeofday();
my $count = 0;
my $fhzbuflen = -999;
foreach my $key (keys %{ $io->{SOFTBUFFER} }) {
$count++;
my $h = $io->{SOFTBUFFER}{$key};
my $name = $h->{HASH}->{NAME};
if($h->{NSENT}) {
next if($now-$h->{SENDTIME} < $retryafter);
my $retry = $attr{$name}{retrycount};
if($h->{NSENT} > $retry) {
Log GetLogLevel($name,2), "$name set $h->{CMD}: ".
"no confirmation after $h->{NSENT} tries, giving up";
delete($io->{SOFTBUFFER}{$key});
next;
}
}
# Check if it is still in the CUL buffer.
if($io->{TYPE} eq "CUL") {
my $cul = CallFn($io->{NAME}, "GetFn", $io, (" ", "raw", "T02"));
my $arg = uc($h->{ARG});
$arg =~ s/^020183//;
$arg =~ s/(....)/,$1/g;
$arg =~ s/,(....),/$1:/;
$arg = uc($arg);
if($cul =~ m/$arg/) {
Log GetLogLevel($name,3), "fhtsoftbuffer: $name set $h->{CMD} ".
"is still in the culfw buffer, wont send it again";
$h->{SENDTIME} = $now;
$h->{NSENT}++;
next;
}
}
$fhzbuflen = getFhtBuffer($io) if($fhzbuflen == -999);
my $arglen = length($h->{ARG})/2 - 2; # Length in bytes
next if($fhzbuflen < $arglen || $fhzbuflen < getFhtMin($io));
IOWrite($h->{HASH}, "04", $h->{ARG});
Log GetLogLevel($name,2), "FHT set $name $h->{CMD}";
$fhzbuflen -= $arglen;
$h->{SENDTIME} = $now;
$h->{NSENT}++;
}
if($count && !$io->{SOFTBUFFERTIMER}) {
$io->{SOFTBUFFERTIMER} = 1;
InternalTimer(gettimeofday()+30, "softBufferTimer", $io, 0);
}
}
#####################################
# Wrapper for the InternalTimer
sub
softBufferTimer($)
{
my ($io) = @_;
delete($io->{SOFTBUFFERTIMER});
doSoftBuffer($io);
}
#####################################
sub
getFhtMin($)
{
my ($io) = @_;
my $ioname = $io->{NAME};
return $attr{$ioname}{minfhtbuffer}
if($attr{$ioname} && $attr{$ioname}{minfhtbuffer});
return $defmin;
}
#####################################
# get the FHZ hardwarebuffer without logentry as decimal value
sub
getFhtBuffer($)
{
my ($io) = @_;
my $count = 0;
return getFhtMin($io) if(IsDummy($io->{NAME}));
for(;;) {
return 0 if(!defined($io->{FD})); # Avoid crash if the CUL/FHZ is absent
my $msg = CallFn($io->{NAME}, "GetFn", $io, (" ", "fhtbuf"));
Log 5, "getFhtBuffer: $count $msg";
return hex($1) if($msg && $msg =~ m/=> ([0-9A-F]+)$/i);
return 0 if($count++ >= 5);
}
}
1;
=pod
=begin html
<a name="FHT"></a>
<h3>FHT</h3>
<ul>
Fhem can receive FHT radio (868.35 MHz) messages either through an <a
href="#FHZ">FHZ</a> or an <a href="#CUL">CUL</a> device, so this must be
defined first.<br><br>
<a name="FHTdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FHT &lt;housecode&gt;</code>
<br><br>
<code>&lt;housecode&gt;</code> is a four digit hex number,
corresponding to the address of the FHT80b device.
<br>
Examples:
<ul>
<code>define wz FHT 3232</code><br>
</ul>
<br>
See the FHT section in <a href="#set">set</a> for more.
</ul>
<br>
<a name="FHTset"></a>
<b>Set </b>
<ul>
<code>set &lt;name&gt; &lt;valuetype&gt; &lt;value&gt;</code>
<br><br>
where <code>value</code> is one of:<br>
<pre>
desired-temp
day-temp night-temp
report1 report2
refreshvalues
mode
holiday1 holiday2 # see mode holiday_short or holiday
manu-temp # No clue what it does.
year month day hour minute
time date
lowtemp-offset # Alarm-Temp.-Differenz
windowopen-temp
mon-from1 mon-to1 mon-from2 mon-to2
tue-from1 tue-to1 tue-from2 tue-to2
wed-from1 wed-to1 wed-from2 wed-to2
thu-from1 thu-to1 thu-from2 thu-to2
fri-from1 fri-to1 fri-from2 fri-to2
sat-from1 sat-to1 sat-from2 sat-to2
sun-from1 sun-to1 sun-from2 sun-to2
</pre>
Examples:
<ul>
<code>set wz desired-temp 22.5</code><br>
<code>set fl desired-temp 20.5 day-temp 19.0 night-temp 16.0</code><br>
</ul>
<br>
Notes:
<ul>
<li>Following events are reported (more or less regularly) by each FHT
device: <code>measured-temp actuator actuator1...actuator8
warnings</code><br>
You can use these strings for <a href="#notify">notify</a> or
<a href="#FileLog">FileLog</a> definitions.
<ul>
<li>warnings can contain following strings:
none, Battery low,Temperature too low, Window open,
Fault on window sensor
</li>
<li>actuator (without a suffix) stands for all actuators.</li>
<li>actuator or actuator1..8 can take following values:
<ul>
<li>&lt;value&gt;%<br>
This is the normal case, the actuator is instructed to
open to this value.
</li>
<li>offset &lt;value&gt;%<br>
The actuator is running with this offset.
</li>
<li>lime-protection<br>
The actuator was instructed to execute the lime-protection
procedure.
</li>
<li>synctime<br>
If you select Sond/Sync on the FHT80B, you'll see a count
down.
</li>
<li>test<br>
The actuator was instructed by the FHT80b to emit a beep.
</li>
<li>pair<br>
The the FHT80b sent a "you-belong-to-me" to this actuator.
</li>
</ul></li>
</ul></li>
<br>
<li>The FHT is very economical (or lazy), it accepts one message from the
FHZ1x00 every 115+x seconds, where x depends on the housecode. Don't
be surprised if your command is only accepted 10 minutes later by the
device. FHT commands are buffered in the FHZ1x00/CUL till they are
sent to the FHT, see the related <code>fhtbuf</code> entry in the
<code><a href="#get">get</a></code> section.<br> You can send up to 8
commands in one message at once to the FHT if you specify them all as
arguments to the same set command, see the example above.
</li>
<br>
<li>time sets hour and minute to local time</li><br>
<li>date sets year, month and date to local time</li><br>
<li>refreshvalues is an alias for report1 255 report2 255</li><br>
<li>All <code>*-temp</code> values need a temperature
as argument, which will be rounded to 0.5 Celsius.<br>
Temperature values must between 5.5 and 30.5 Celsius. Value 5.5 sets
the actuator to OFF, value 30.5 set the actuator to ON</li><br>
<li><code>mode</code> is one of <code>auto, manual, holiday or
holiday_short.</code><br>
If the mode is holiday, then the mode switches back to either auto or
manual at 00:00 of the day specified by the following:
<ul>
<li>holiday1 sets the end-day of the holiday</li>
<li>holiday2 sets the end-month of the holiday</li>
</ul>
For holiday_short (party mode)
<ul>
<li> holiday1 sets the absolute hour to switch back from this
mode (in 10-minute steps, max 144)</li>
<li> holiday2 sets the day of month to switch back from this mode
(can only be today or tomorrow, since holiday1 accepts only 24
hours).</li>
Example:
<ul>
<li>current date is 29 Jan, time is 18:05</li>
<li>you want to switch to party mode until tomorrow 1:00</li>
<li>set holiday1 to 6 (6 x 10min = 1hour) and holiday2 to
30</li>
</ul>
</ul>
The temperature for the holiday period is set by the desired-temperature
parameter. <br> Note that you cannot set holiday mode for days earlier than the
day after tomorrow, for this you must use holiday_short.<br>
Note also, you cannot set parameters seperately, you must set them in one command.
Example:
<br>
<code>set FHT1 mode holiday holiday1 24 holiday2 12 desired-temp 14</code>
</li>
<br>
<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
24:00 means off.</li><br>
<li>To synchronize the FHT time and to "wake" muted FHTs it is adviseable
to schedule following command:<br>
<code>define fht_sync at +*3:30 set TYPE=FHT time</code>
</li>
<br>
<li><code>report1</code> with parameter 255 requests all settings for
monday till sunday to be sent. The argument is a bitfield, to request
unique values add up the following:
<ul>
<li> 1: monday</li>
<li> 2: tuesday</li>
<li> 4: thursday</li>
<li> 8: wednesday</li>
<li>16: friday</li>
<li>32: saturday</li>
<li>64: sunday</li>
</ul>
measured-temp and actuator is sent along if it is considered
appropriate
by the FHT.
<br><br>
<b>Note:</b> This command generates a lot of RF traffic, which can
lead to further problems, especially if the reception is not clear.
</li><br>
<li><code>report2</code> with parameter 255 requests the following
settings to be reported: day-temp night-temp windowopen-temp
lowtemp-offset desired-temp measured-temp mode warnings.
The argument is (more or less) a bitfield, to request unique values
add up the following:
<ul>
<li> 1: warnings</li>
<li> 2: mode</li>
<li> 4: day-temp, night-temp, windowopen-temp</li>
<li>64: lowtemp-offset</li>
</ul>
measured-temp and actuator is sent along if it is considered
appropriate by the FHT.</li>
<br>
<li><code>lowtemp-offset</code> needs a temperature as argument, valid
values must be between 1.0 and 5.0 Celsius.<br> It will trigger a
warning if <code>desired-temp - measured-temp &gt;
lowtemp-offset</code> in a room for at least 1.5 hours after the last
desired-temp change.</li>
<br>
<li>FHEM optionally has an internal software buffer for FHT devices.
This buffer should prevent transmission errors. If there is no
confirmation for a given period, FHEM resends the command. You can
see the queued commands with <a href="#list">list</a>
&lt;fht-device&gt;.
See the <a href="#fhtsoftbuffer">fhtsoftbuffer</a>,
<a href="#retrycount">retrycount</a> and
<a href="#minfhtbuffer">minfhtbuffer</a> attributes for details.
</li>
<br>
<li>If a buffer is still in the softbuffer, it will be sent in the
following order:<br> <code>desired-temp,mode,report1,report2,
holiday1,holiday2,day-temp,night-temp, [all other commands]</code>
</li>
<br>
</ul>
</ul>
<br>
<b>Get</b> <ul>N/A</ul><br>
<a name="FHTattr"></a>
<b>Attributes</b>
<ul>
<li><a href="#attrdummy">dummy</a><br>
<b>Note:</b>It makes sense to define an FHT device even for an FHT8b,
else you will receive "unknown FHT device, please define one" message
for each FHT8b as the CUL is reporting the 8b valve messages. But you
should set the dummy attribute for these devices, else the internal FHT
buffer of the CUL will be filled with data for the 8b's which is never
consumed. If the buffer is full, you'll get "EOB" messages from the CUL,
and you cannot transmit any data to the 80b's</li><br>
<a name="retrycount"></a>
<li>retrycount<br>
If the <a href="#fhtsoftbuffer">fhtsoftbuffer</a> attribute is set, then
resend commands <code>retrycount</code> times if after 240 seconds
no confirmation message is received from the corresponding FHT
device.<br>
Default is 3.</li><br>
<a name="minfhtbuffer"></a>
<li>minfhtbuffer<br>
FHEM won't send commands to the FHZ if its fhtbuffer is below
this value, default is 0. If this value is low, then the ordering of
fht commands (see the note in the FHT section of <a href="#set">set</a>)
has little effect, as only commands in the softbuffer can be
prioritized. The maximum value should be 7 below the hardware maximum
(see fhtbuf).
</li><br>
<a name="lazy"></a>
<li>lazy<br>
If the lazy attribute is set, FHEM won't send commands to the FHT if
the current reading and the value to be set are already identical. This
may help avoiding conflicts with the max-1%-time-on-air rule in large
installations. Not set per default.
</li><br>
<a name="tmpcorr"></a>
<li>tmpcorr<br>
Correct the temperature reported by the FHT by the value specified.
Note: only the measured-temp value reported by fhem (used for logging)
will be modified.
</li><br>
<li><a href="#ignore">ignore</a></li>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#loglevel">loglevel</a></li>
<li><a href="#model">model</a> (fht80b)</li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#IODev">IODev</a></li>
<li><a href="#eventMap">eventMap</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>
<br>
<a name="FHTevents"></a>
<b>Generated events:</b>
<ul>
<li>actuator</li>
<li>actuator1 actuator2 actuator3 actuator4<br>
actuator5 actuator6 actuator7 actuator8<br>
(sent if you configured an offset for the associated valve)</li>
<li>mon-from1 mon-to1 mon-from2 mon-to2</li>
<li>tue-from1 tue-to1 tue-from2 tue-to2</li>
<li>wed-from1 wed-to1 wed-from2 wed-to2</li>
<li>thu-from1 thu-to1 thu-from2 thu-to2</li>
<li>fri-from1 fri-to1 fri-from2 fri-to2</li>
<li>sat-from1 sat-to1 sat-from2 sat-to2</li>
<li>sun-from1 sun-to1 sun-from2 sun-to2</li>
<li>mode</li>
<li>holiday1 holiday2</li>
<li>desired-temp</li>
<li>measured-temp measured-low measured-high</li>
<li>warnings</li>
<li>manu-temp</li>
<li>year month day hour minute</li>
<li>day-temp night-temp lowtemp-offset windowopen-temp</li>
<li>ack can-xmit can-rcv ack2 start-xmit end-xmit
(only if the CUL is configured to transmit FHT protocol data)</li>
</ul>
<br>
</ul>
=end html
=cut