mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 12:49:34 +00:00
FHT softbuffer rewrite, module reorganization, support for M232
git-svn-id: https://svn.fhem.de/fhem/trunk@109 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e48b09cd2c
commit
8410288c1e
18
fhem/CHANGED
18
fhem/CHANGED
@ -346,8 +346,18 @@
|
||||
- bugfix: FHT mode holiday_short added (9.9, Dirk)
|
||||
- bugfix: Modifying a device from its own trigger crashes (Klaus, 10.9)
|
||||
- feature: webpgm2 output reformatted
|
||||
- feature: webpgm2 can display multiple plots.
|
||||
- feature: webpgm2 displaying 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)
|
||||
- feature: Softwarebuffer for FHT devices (Dirk 17.10)
|
||||
- feature: FHT low temperatur warning and offset (Dirk 17.10)
|
||||
- change: Change FHT state into warnings (Dirk 17.10)
|
||||
- feature: Softwarebuffer code simplified (Rudi 22.11)
|
||||
- bugfix: bug #12327 doppeltes my
|
||||
- bugfix: set STATE from trigger
|
||||
- bugfix: readings state vs STATE problem (xmllist/trigger)
|
||||
- change: SUNRISE doc changed (99_SUNRISE.pm -> 99_SUNRISE_EL.pm)
|
||||
- feature: Support for the M232 ELV device (Boris, 25.11)
|
||||
|
||||
- TODO
|
||||
emem -2.5kW / getDevData for emwz -1
|
||||
dummy type / dummy attribute
|
||||
|
@ -68,7 +68,9 @@ 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 softbuffer softrepeat softmaxretry";
|
||||
$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" .
|
||||
"fhtsoftbuffer:1,0";
|
||||
}
|
||||
|
||||
#####################################
|
||||
@ -160,24 +162,6 @@ 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($$$$)
|
||||
@ -220,11 +204,15 @@ FHZ_Define($$)
|
||||
delete $hash->{PortObj};
|
||||
delete $hash->{FD};
|
||||
|
||||
my $name = $a[0];
|
||||
my $dev = $a[2];
|
||||
$attr{$a[0]}{savefirst} = 1;
|
||||
|
||||
$attr{$name}{savefirst} = 1;
|
||||
$attr{$name}{fhtsoftbuffer} = 1;
|
||||
|
||||
if($dev eq "none") {
|
||||
Log 1, "FHZ device is none, commands will be echoed only";
|
||||
$attr{$name}{dummy} = 1;
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -255,7 +243,7 @@ FHZ_Define($$)
|
||||
$hash->{DeviceName} = $dev;
|
||||
$hash->{PARTIAL} = "";
|
||||
|
||||
DoInit($a[0]);
|
||||
DoInit($name);
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -423,7 +411,7 @@ FHZ_Write($$$)
|
||||
##############
|
||||
# Write the next buffer not earlier than 0.22 seconds (= 65.6ms + 10ms +
|
||||
# 65.6ms + 10ms + 65.6ms), else it will be discarded by the FHZ1X00 PC
|
||||
InternalTimer(gettimeofday()+0.25, "FHZ_HandleWriteQueue", $hash);
|
||||
InternalTimer(gettimeofday()+0.25, "FHZ_HandleWriteQueue", $hash, 1);
|
||||
} elsif($hash->{QUEUECNT} == 1) {
|
||||
$hash->{QUEUE} = [ $bstring ];
|
||||
} else {
|
||||
@ -443,7 +431,7 @@ FHZ_HandleWriteQueue($)
|
||||
if($cnt > 0) {
|
||||
my $bstring = shift(@{$hash->{QUEUE}});
|
||||
$hash->{PortObj}->write($bstring);
|
||||
InternalTimer(gettimeofday()+0.25, "FHZ_HandleWriteQueue", $hash);
|
||||
InternalTimer(gettimeofday()+0.25, "FHZ_HandleWriteQueue", $hash, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,10 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub doSoftBuffer($);
|
||||
sub softBufferTimer($);
|
||||
sub sendCommand($$$$);
|
||||
|
||||
my %codes = (
|
||||
"0000.6" => "actuator",
|
||||
"00002c" => "synctime", # Not verified
|
||||
@ -100,14 +104,13 @@ my %cantset = (
|
||||
);
|
||||
|
||||
my %nosetarg = (
|
||||
"help" => 1,
|
||||
"refreshvalues" => 1,
|
||||
);
|
||||
|
||||
my %priority = (
|
||||
"desired-temp" => 1,
|
||||
"mode" => 2,
|
||||
"refreshvalues" => 3,
|
||||
"desired-temp"=> 1,
|
||||
"mode" => 2,
|
||||
"refreshvalues"=> 3,
|
||||
"holiday1" => 4,
|
||||
"holiday2" => 5,
|
||||
"day-temp" => 6,
|
||||
@ -115,14 +118,14 @@ my %priority = (
|
||||
);
|
||||
|
||||
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
|
||||
my $minFhzHardwareBuffer = 10; # min fhtbuf free bytes before sending commands
|
||||
my $retryafter = 240; # in seconds, only when softbuffer is active
|
||||
my $cmdcount = 0;
|
||||
|
||||
#####################################
|
||||
sub
|
||||
@ -144,169 +147,103 @@ FHT_Initialize($)
|
||||
# 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->{StateFn} = "FHT_SetState";
|
||||
$hash->{DefFn} = "FHT_Define";
|
||||
$hash->{UndefFn} = "FHT_Undef";
|
||||
$hash->{ParseFn} = "FHT_Parse";
|
||||
$hash->{AttrList} = "do_not_notify:0,1 model;fht80b dummy:0,1 showtime:0,1 loglevel:0,1,2,3,4,5,6";
|
||||
$hash->{AttrList} = "do_not_notify:0,1 model;fht80b dummy:0,1 " .
|
||||
"showtime:0,1 loglevel:0,1,2,3,4,5,6 retrycount";
|
||||
}
|
||||
|
||||
# Parse the incomming commands and send them via sendCommand to the FHZ
|
||||
# or via toSendbuffer in the Softwarebuffer (queue)
|
||||
#
|
||||
sub FHT_Set($@)
|
||||
|
||||
|
||||
sub
|
||||
FHT_Set($@)
|
||||
{
|
||||
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 " .
|
||||
|
||||
my $name = $a[0];
|
||||
my $cmd = $a[1];
|
||||
|
||||
return "Unknown argument $cmd, choose one of " .
|
||||
join(" ", sort {$c2bset{$a} cmp $c2bset{$b} } keys %c2bset)
|
||||
if(!defined($c2bset{$a[1]}));
|
||||
if(!defined($c2bset{$cmd}));
|
||||
return "\"set $a[0]\" needs two parameters"
|
||||
if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]}));
|
||||
if(@a != 3 && !(@a == 2 && $nosetarg{$cmd}));
|
||||
|
||||
my $val = $a[2];
|
||||
my $arg = "020183" . $hash->{CODE} . $c2bset{$cmd};
|
||||
|
||||
if ($cmd =~ m/-temp/) {
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
$val = sprintf("%.1f", $a/2);
|
||||
|
||||
} elsif($cmd =~ m/-from/ || $cmd =~ m/-to/) {
|
||||
|
||||
} 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);
|
||||
$val = $nt;
|
||||
|
||||
} elsif($cmd eq "mode") {
|
||||
|
||||
} 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+$/);
|
||||
} elsif ($cmd eq "lowtemp-offset") {
|
||||
|
||||
# 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";
|
||||
return "Invalid lowtemperature-offset, must between 1 and 5"
|
||||
if($val !~ m/^[1-5]$/);
|
||||
$arg .= sprintf("%02x", $val);
|
||||
$val = "$val.0";
|
||||
|
||||
} else { # Holiday1, Holiday2
|
||||
$arg .= sprintf("%02x", $val);
|
||||
|
||||
$arg .= sprintf("%02x", $val) if(defined($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
|
||||
my $ioname = $hash->{IODev}->{NAME};
|
||||
if($attr{$ioname} && $attr{$ioname}{fhtsoftbuffer}) {
|
||||
|
||||
my $io = $hash->{IODev};
|
||||
my %h = (HASH => $hash, CMD => $cmd, VAL => $val, ARG => $arg);
|
||||
|
||||
my $prio = $priority{$cmd};
|
||||
$prio = "9" if(!$prio);
|
||||
my $key = $prio . ":" . gettimeofday() . ":" . $cmdcount++;
|
||||
|
||||
$io->{SOFTBUFFER}{$key} = \%h;
|
||||
doSoftBuffer($io);
|
||||
|
||||
} else {
|
||||
|
||||
Log GetLogLevel($name,2), "FHT set $name $type $val (Enqueue to buffer)" if ($fhzHardwareBufferSpace >= $minFhzHardwareBufferSpace);
|
||||
sendCommand($hash, $cmd, $val, $arg);
|
||||
|
||||
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($name)) {
|
||||
my $havefhz;
|
||||
$havefhz = 1 if($hash->{IODev} && defined($hash->{IODev}->{FD}));
|
||||
|
||||
IOWrite($hash, "04", $arg);
|
||||
sleep(1) if($havefhz);
|
||||
IOWrite($hash, "04", "c90185"); # Check the fht buffer
|
||||
sleep(1) if($havefhz);
|
||||
}
|
||||
} else {
|
||||
IOWrite($hash, "04", $arg) if(!IsDummy($name));
|
||||
}
|
||||
|
||||
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($$$$)
|
||||
@ -331,8 +268,10 @@ FHT_Define($$)
|
||||
if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/i);
|
||||
|
||||
|
||||
$hash->{CODE} = $a[2];
|
||||
$hash->{CODE} = $a[2];
|
||||
$defptr{$a[2]} = $hash;
|
||||
$attr{$a[0]}{retrycount} = 3;
|
||||
|
||||
AssignIoPort($hash);
|
||||
|
||||
@ -362,42 +301,29 @@ FHT_Parse($$)
|
||||
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};
|
||||
my $name = $def->{NAME};
|
||||
|
||||
# Unknown, but don't want report it. Should come with c409c401
|
||||
return "" if($cde eq "00");
|
||||
|
||||
if(length($cde) < 6) {
|
||||
my $name = $def->{NAME};
|
||||
Log GetLogLevel($name,2), "FHT Unknown code from $name : $cde";
|
||||
$def->{CHANGED}[0] = "unknown code $cde";
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
if(!$val) {
|
||||
# This is a confirmation message. We reformat it so that
|
||||
# it looks like a real message, and let the rest parse it
|
||||
Log 4, "FHT $def->{NAME} confirmation: $cde)";
|
||||
Log 4, "FHT $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;
|
||||
}
|
||||
|
||||
@ -412,9 +338,9 @@ FHT_Parse($$)
|
||||
$val = hex($val);
|
||||
|
||||
if(!$type) {
|
||||
Log 4, "FHT $def->{NAME} (Unknown: $cde => $val)";
|
||||
Log 4, "FHT $name (Unknown: $cde => $val)";
|
||||
$def->{CHANGED}[0] = "unknown $cde: $val";
|
||||
return $def->{NAME};
|
||||
return $name;
|
||||
}
|
||||
|
||||
my $tn = TimeNow();
|
||||
@ -427,7 +353,7 @@ FHT_Parse($$)
|
||||
} elsif($type eq "lime-protection") {
|
||||
$val = sprintf("(actuator: %02d%%)", int(100*$val/255 + 0.5));
|
||||
} elsif($cde ge "140069" && $cde le "2f0069") { # Time specs
|
||||
Log 5, "FHT $def->{NAME} ($type: $val)";
|
||||
Log 5, "FHT $name ($type: $val)";
|
||||
return "" if($val == 144); # Empty, forget it
|
||||
my $hour = $val / 6;
|
||||
my $min = ($val % 6) * 10;
|
||||
@ -463,21 +389,12 @@ FHT_Parse($$)
|
||||
|
||||
} elsif($type eq "warnings") {
|
||||
|
||||
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";
|
||||
}
|
||||
my $nVal;
|
||||
if($val & 1) { $nVal = "Battery low"; }
|
||||
if($val & 2) { $nVal .= "; " if($nVal); $nVal .= "Temperature too low"; }
|
||||
if($val &32) { $nVal .= "; " if($nVal); $nVal .= "Window open"; }
|
||||
if($val &16) { $nVal .= "; " if($nVal); $nVal .= "Fault on window sensor"; }
|
||||
$val = $nVal? $nVal : "none";
|
||||
|
||||
} elsif($type eq "lowtemp-offset") {
|
||||
$val = sprintf("%d.0 (Celsius)", $val)
|
||||
@ -489,194 +406,127 @@ FHT_Parse($$)
|
||||
|
||||
$def->{READINGS}{$type}{TIME} = $tn;
|
||||
$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, "FHT $name ($type: $val)";
|
||||
|
||||
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
|
||||
}
|
||||
################################
|
||||
# Softbuffer: deleted confirmed commands
|
||||
my $io = $hash->{IODev};
|
||||
if($confirm && keys(%{$io->{SOFTBUFFER}})) {
|
||||
my $found;
|
||||
foreach my $key (sort keys %{$io->{SOFTBUFFER}}) {
|
||||
my $h = $io->{SOFTBUFFER}{$key};
|
||||
if($h->{HASH}->{NAME} eq $name &&
|
||||
$h->{CMD} eq $type) {
|
||||
$found = $key;
|
||||
last;
|
||||
}
|
||||
last # exit foreach because we need only the first buffer value
|
||||
}
|
||||
delete($io->{SOFTBUFFER}{$found}) if($found);
|
||||
}
|
||||
|
||||
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
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
# Check the softwarebuffer and send/resend commands
|
||||
sub
|
||||
doSoftBuffer($)
|
||||
{
|
||||
my ($io) = @_;
|
||||
|
||||
my $now = gettimeofday();
|
||||
|
||||
my $count = 0;
|
||||
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} $h->{VAL}: ".
|
||||
"no confirmation after $h->{NSENT} tries, giving up";
|
||||
delete($io->{SOFTBUFFER}{$key});
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
next if(getFhzBuffer($io) < $minFhzHardwareBuffer);
|
||||
sendCommand($h->{HASH}, $h->{CMD}, $h->{VAL}, $h->{ARG});
|
||||
$h->{SENDTIME} = $now;
|
||||
$h->{NSENT}++;
|
||||
|
||||
}
|
||||
|
||||
if($count && !$io->{SOFTBUFFERTIMER}) {
|
||||
$io->{SOFTBUFFERTIMER} = 1;
|
||||
InternalTimer(gettimeofday()+30, "softBufferTimer", $io, 0);
|
||||
}
|
||||
}
|
||||
|
||||
# set given command tothe internal software buffer
|
||||
# each command queued until the previous command become a confirmation
|
||||
#
|
||||
sub toSendbuffer ($$$$)
|
||||
#####################################
|
||||
# Wrapper for the InternalTimer
|
||||
sub
|
||||
softBufferTimer($)
|
||||
{
|
||||
my ($io) = @_;
|
||||
delete($io->{SOFTBUFFERTIMER});
|
||||
doSoftBuffer($io);
|
||||
}
|
||||
|
||||
my ($hash, $type, $val, $arg, $nBufferKey, $retry) = @_;
|
||||
|
||||
if (!$init_done || $attr{FHZ}{softbuffer} == 0) {
|
||||
return
|
||||
#####################################
|
||||
# get the FHZ hardwarebuffer without logentry as decimal value
|
||||
sub
|
||||
getFhzBuffer($)
|
||||
{
|
||||
my ($io) = @_;
|
||||
my $count = 0;
|
||||
|
||||
return $minFhzHardwareBuffer if(IsDummy($io->{NAME}));
|
||||
|
||||
Log 4, "getFhzBuffer";
|
||||
for(;;) {
|
||||
FHZ_Write($io, "04", "c90185");
|
||||
|
||||
my $msg = FHZ_ReadAnswer($io, "fhtbuf");
|
||||
|
||||
return hex(substr($msg, 16, 2)) if($msg && $msg =~ m/^[0-9]+$/);
|
||||
return 0 if($count++ > 5);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
#####################################
|
||||
# Send FHZ command
|
||||
sub
|
||||
sendCommand($$$$)
|
||||
{
|
||||
my ($hash, $cmd, $val, $arg) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
$pr = $priority{$type} if (defined($priority{$type})); # get priority for specific command type
|
||||
$val = "" if (!defined($val));
|
||||
if($cmd eq "refreshvalues") {
|
||||
|
||||
# This is special. Without the sleep the next FHT won't send its data
|
||||
if(!IsDummy($name)) {
|
||||
my $havefhz = ($hash->{IODev} && defined($hash->{IODev}->{FD}));
|
||||
IOWrite($hash, "04", $arg);
|
||||
sleep(1) if($havefhz);
|
||||
IOWrite($hash, "04", "c90185"); # Check the fht buffer
|
||||
sleep(1) if($havefhz);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
IOWrite($hash, "04", $arg) if(!IsDummy($name));
|
||||
|
||||
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;
|
||||
Log GetLogLevel($name,2), "FHT set $name $cmd $val";
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -43,11 +43,14 @@ EM_Define($$)
|
||||
delete $hash->{PortObj};
|
||||
delete $hash->{FD};
|
||||
|
||||
my $name = $a[0];
|
||||
my $dev = $a[2];
|
||||
$attr{$a[0]}{savefirst} = 1;
|
||||
|
||||
$attr{$name}{savefirst} = 1;
|
||||
|
||||
if($dev eq "none") {
|
||||
Log 1, "EM device is none, commands will be echoed only";
|
||||
$attr{$name}{dummy} = 1;
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
@ -31,15 +31,17 @@ EMWZ_GetStatus($)
|
||||
my ($hash) = @_;
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
InternalTimer(gettimeofday()+300, "EMWZ_GetStatus", $hash);
|
||||
InternalTimer(gettimeofday()+300, "EMWZ_GetStatus", $hash, 0);
|
||||
}
|
||||
|
||||
my $dnr = $hash->{DEVNR};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
return "Empty status: dummy IO device" if(IsIoDummy($name));
|
||||
|
||||
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
||||
if(!defined($d)) {
|
||||
my $msg = "EMWZ $name read error";
|
||||
my $msg = "EMWZ $name read error (GetStatus 1)";
|
||||
Log GetLogLevel($name,2), $msg;
|
||||
return $msg;
|
||||
}
|
||||
@ -54,7 +56,7 @@ EMWZ_GetStatus($)
|
||||
my $pulses=w($d,13);
|
||||
my $ec=w($d,49) / 10;
|
||||
if($ec <= 0) {
|
||||
my $msg = "EMWZ read error";
|
||||
my $msg = "EMWZ read error (GetStatus 2)";
|
||||
Log GetLogLevel($name,2), $msg;
|
||||
return $msg;
|
||||
}
|
||||
@ -124,10 +126,12 @@ EMWZ_Set($@)
|
||||
|
||||
return $u if(int(@a) != 3);
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
return "" if(IsIoDummy($name));
|
||||
|
||||
my $v = $a[2];
|
||||
my $d = $hash->{DEVNR};
|
||||
my $msg;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
if($a[1] eq "price") {
|
||||
$v *= 10000; # Make display and input the same
|
||||
@ -141,9 +145,10 @@ EMWZ_Set($@)
|
||||
return $u;
|
||||
}
|
||||
|
||||
|
||||
my $ret = IOWrite($hash, $msg);
|
||||
if(!defined($ret)) {
|
||||
my $msg = "EMWZ $name read error";
|
||||
my $msg = "EMWZ $name read error (Set)";
|
||||
Log GetLogLevel($name,2), $msg;
|
||||
return $msg;
|
||||
}
|
||||
@ -170,11 +175,7 @@ EMWZ_Define($$)
|
||||
AssignIoPort($hash);
|
||||
|
||||
|
||||
# InternalTimer blocks if init_done is not true
|
||||
my $oid = $init_done;
|
||||
$init_done = 1;
|
||||
EMWZ_GetStatus($hash);
|
||||
$init_done = $oid;
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ use warnings;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
sub EMEM_Get($@);
|
||||
sub EMEM_Set($@);
|
||||
sub EMEM_Define($$);
|
||||
sub EMEM_GetStatus($);
|
||||
|
||||
@ -29,15 +28,17 @@ EMEM_GetStatus($)
|
||||
my ($hash) = @_;
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
InternalTimer(gettimeofday()+300, "EMEM_GetStatus", $hash);
|
||||
InternalTimer(gettimeofday()+300, "EMEM_GetStatus", $hash, 0);
|
||||
}
|
||||
|
||||
my $dnr = $hash->{DEVNR};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
return "Empty status: dummy IO device" if(IsIoDummy($name));
|
||||
|
||||
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
||||
if(!defined($d)) {
|
||||
my $msg = "EMEM $name read error";
|
||||
my $msg = "EMEM $name read error (GetStatus 1)";
|
||||
Log GetLogLevel($name,2), $msg;
|
||||
return $msg;
|
||||
}
|
||||
@ -122,11 +123,7 @@ EMEM_Define($$)
|
||||
AssignIoPort($hash);
|
||||
|
||||
|
||||
# InternalTimer blocks if init_done is not true
|
||||
my $oid = $init_done;
|
||||
$init_done = 1;
|
||||
EMEM_GetStatus($hash);
|
||||
$init_done = $oid;
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
190
fhem/FHEM/70_SCIVT.pm
Normal file
190
fhem/FHEM/70_SCIVT.pm
Normal file
@ -0,0 +1,190 @@
|
||||
##############################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Device::SerialPort;
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SCIVT_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
# Consumer
|
||||
$hash->{DefFn} = "SCIVT_Define";
|
||||
$hash->{GetFn} = "SCIVT_Get";
|
||||
$hash->{AttrList}= "model:SCD loglevel:0,1,2,3,4,5,6";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SCIVT_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "Define the serial device as a parameter, use none for a fake device"
|
||||
if(@a != 3);
|
||||
$hash->{STATE} = "Initialized";
|
||||
|
||||
my $dev = $a[2];
|
||||
|
||||
Log 1, "SCIVT device is none, commands will be echoed only"
|
||||
if($dev eq "none");
|
||||
|
||||
if($dev ne "none") {
|
||||
Log 2, "SCIVT opening device $dev";
|
||||
my $po = new Device::SerialPort ($dev);
|
||||
return "Can't open $dev: $!" if(!$po);
|
||||
Log 2, "SCIVT opened device $dev";
|
||||
$po->close();
|
||||
}
|
||||
|
||||
$hash->{DeviceName} = $dev;
|
||||
|
||||
SCIVT_GetStatus($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SCIVT_Get($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "get for an SCIVT device needs exactly one parameter" if(@a != 2);
|
||||
|
||||
my $v;
|
||||
if($a[1] eq "data") {
|
||||
$v = SCIVT_GetLine($hash->{DeviceName});
|
||||
$v =~ s/[\r\n]//g; # Delete the NewLine
|
||||
} else {
|
||||
return "Unknown argument $a[1], must be data";
|
||||
}
|
||||
|
||||
$hash->{READINGS}{$a[1]}{VAL} = $v;
|
||||
$hash->{READINGS}{$a[1]}{TIME} = TimeNow();
|
||||
|
||||
return "$a[0] $a[1] => $v";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SCIVT_GetStatus($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
# Call us in 5 minutes again.
|
||||
InternalTimer(gettimeofday()+300, "SCIVT_GetStatus", $hash, 0);
|
||||
|
||||
my $dnr = $hash->{DEVNR};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my %vals;
|
||||
my $result = SCIVT_GetLine($hash->{DeviceName});
|
||||
|
||||
if(!defined($result))
|
||||
{
|
||||
Log GetLogLevel($name,2), "SCIVT read error, retry";
|
||||
$result = SCIVT_GetLine($hash->{DeviceName});
|
||||
}
|
||||
|
||||
if(!defined($result))
|
||||
{
|
||||
Log GetLogLevel($name,2), "SCIVT read error, abort";
|
||||
$hash->{STATE} = "timeout";
|
||||
return $hash->{STATE};
|
||||
}
|
||||
if (length($result) < 10)
|
||||
{
|
||||
Log GetLogLevel($name,2), "SCIVT incomplete line ($result)";
|
||||
$hash->{STATE} = "incomplete";
|
||||
}
|
||||
else
|
||||
{
|
||||
$result =~ s/^.*R://;
|
||||
$result =~ s/[\r\n ]//g;
|
||||
Log GetLogLevel($name,2), "SCIVT $result (raw)";
|
||||
$result=~ s/,/./g;
|
||||
my @data = split(";", $result);
|
||||
|
||||
my @names = ("Vs", "Is", "Temp", "minV", "maxV", "minI", "maxI");
|
||||
my $tn = TimeNow();
|
||||
for(my $i = 0; $i < int(@names); $i++) {
|
||||
$hash->{CHANGED}[$i] = "$names[$i]: $data[$i]";
|
||||
$hash->{READINGS}{$names[$i]}{TIME} = $tn;
|
||||
$hash->{READINGS}{$names[$i]}{VAL} = $data[$i];
|
||||
}
|
||||
|
||||
DoTrigger($name, undef) if($init_done);
|
||||
|
||||
$result =~ s/;/ /g;
|
||||
$hash->{STATE} = "$result";
|
||||
}
|
||||
|
||||
return $hash->{STATE};
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SCIVT_GetLine($)
|
||||
{
|
||||
my $retry = 0;
|
||||
my ($dev) = @_;
|
||||
|
||||
return "R:13,66; 0,0;30;13,62;15,09;- 0,2; 2,8;\n"
|
||||
if($dev eq "none"); # Fake-mode
|
||||
|
||||
my $serport = new Device::SerialPort ($dev);
|
||||
if(!$serport) {
|
||||
Log 1, "SCIVT: Can't open $dev: $!";
|
||||
return undef;
|
||||
}
|
||||
$serport->reset_error();
|
||||
$serport->baudrate(1200);
|
||||
$serport->databits(8);
|
||||
$serport->parity('none');
|
||||
$serport->stopbits(1);
|
||||
$serport->handshake('none');
|
||||
|
||||
my $rm = "SCIVT timeout reading the answer";
|
||||
my $data="";
|
||||
|
||||
$serport->write('F');
|
||||
sleep(1);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
my ($rout, $rin) = ('', '');
|
||||
vec($rin, $serport->FILENO, 1) = 1;
|
||||
my $nfound = select($rout=$rin, undef, undef, 3.0);
|
||||
|
||||
if($nfound < 0) {
|
||||
$rm = "SCIVT Select error $nfound / $!";
|
||||
goto DONE;
|
||||
}
|
||||
last if($nfound == 0);
|
||||
|
||||
my $buf = $serport->input();
|
||||
if(!defined($buf) || length($buf) == 0) {
|
||||
$rm = "SCIVT EOF on $dev";
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
|
||||
$data .= $buf;
|
||||
if($data =~ m/[\r\n]/) { # Newline received
|
||||
$serport->close();
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
DONE:
|
||||
$serport->close();
|
||||
Log 3, "SCIVT $rm";
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
268
fhem/FHEM/80_M232.pm
Normal file
268
fhem/FHEM/80_M232.pm
Normal file
@ -0,0 +1,268 @@
|
||||
##############################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Device::SerialPort;
|
||||
|
||||
sub M232Write($$);
|
||||
sub M232GetData($$);
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
# Provider
|
||||
$hash->{WriteFn} = "M232_Write";
|
||||
$hash->{Clients} = ":M232Counter:";
|
||||
|
||||
# Consumer
|
||||
$hash->{DefFn} = "M232_Define";
|
||||
$hash->{UndefFn} = "M232_Undef";
|
||||
$hash->{GetFn} = "M232_Get";
|
||||
$hash->{SetFn} = "M232_Set";
|
||||
$hash->{AttrList}= "model:m232 loglevel:0,1,2,3,4,5";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
$hash->{STATE} = "Initialized";
|
||||
|
||||
delete $hash->{PortObj};
|
||||
delete $hash->{FD};
|
||||
|
||||
my $dev = $a[2];
|
||||
$attr{$a[0]}{savefirst} = 1;
|
||||
|
||||
if($dev eq "none") {
|
||||
Log 1, "M232 device is none, commands will be echoed only";
|
||||
return undef;
|
||||
}
|
||||
|
||||
Log 3, "M232 opening device $dev";
|
||||
my $po = new Device::SerialPort ($dev);
|
||||
return "Can't open $dev: $!" if(!$po);
|
||||
Log 3, "M232 opened device $dev";
|
||||
$po->close();
|
||||
|
||||
$hash->{DeviceName} = $dev;
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Undef($$)
|
||||
{
|
||||
my ($hash, $arg) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
foreach my $d (sort keys %defs) {
|
||||
if(defined($defs{$d}) &&
|
||||
defined($defs{$d}{IODev}) &&
|
||||
$defs{$d}{IODev} == $hash)
|
||||
{
|
||||
Log GetLogLevel($name,2), "deleting port for $d";
|
||||
delete $defs{$d}{IODev};
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $u1 = "Usage: set <name> auto <value>\n" .
|
||||
"set <name> stop\n" .
|
||||
"set <name> start";
|
||||
|
||||
return $u1 if(int(@a) < 2);
|
||||
my $msg;
|
||||
|
||||
if($a[1] eq "auto") {
|
||||
return $u1 if(int(@a) !=3);
|
||||
my $value= $a[2];
|
||||
my @legal= (0..5,"none");
|
||||
if(!grep($value eq $_, @legal)) {
|
||||
return "Illegal value $value, possible values: @legal";
|
||||
}
|
||||
if($value eq "none") { $value= 0; } else { $value+=1; }
|
||||
$msg= "M" . $value;
|
||||
}
|
||||
|
||||
elsif($a[1] eq "start") {
|
||||
return $u1 if(int(@a) !=2);
|
||||
$msg= "Z1";
|
||||
}
|
||||
|
||||
elsif($a[1] eq "stop") {
|
||||
return $u1 if(int(@a) !=2);
|
||||
$msg= "Z0";
|
||||
}
|
||||
|
||||
else { return $u1; }
|
||||
|
||||
my $d = M232GetData($hash->{DeviceName}, $msg);
|
||||
return "Read error" if(!defined($d));
|
||||
return $d;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Get($@)
|
||||
{
|
||||
|
||||
my ($hash, @a) = @_;
|
||||
my $u1 = "Usage: get <name> [an0..an5]\n" .
|
||||
"get <name> [io0..io7]\n" .
|
||||
"get <name> octet\n" .
|
||||
"get <name> counter";
|
||||
|
||||
return $u1 if(int(@a) != 2);
|
||||
|
||||
my $name= $a[0];
|
||||
my $reading= $a[1];
|
||||
my $msg;
|
||||
my $retval;
|
||||
|
||||
|
||||
if($reading eq "counter") {
|
||||
$msg= "z";
|
||||
my $d = M232GetData($hash->{DeviceName}, $msg);
|
||||
return "Read error" if(!defined($d));
|
||||
my $count= hex $d;
|
||||
$retval= $count;
|
||||
}
|
||||
|
||||
elsif($reading =~ /^an[0-5]$/) {
|
||||
$msg= "a" . substr($reading,2,1);
|
||||
my $d = M232GetData($hash->{DeviceName}, $msg);
|
||||
return "Read error" if(!defined($d));
|
||||
my $voltage= hex substr($d,0,3);
|
||||
my $iscurrent= substr($d,3,1);
|
||||
$retval= $voltage; # . " " . $iscurrent;
|
||||
}
|
||||
|
||||
elsif($reading =~ /^io[0-7]$/) {
|
||||
$msg= "d" . substr($reading,2,1);
|
||||
my $d = M232GetData($hash->{DeviceName}, $msg);
|
||||
return "Read error" if(!defined($d));
|
||||
my $state= hex $d;
|
||||
$retval= $state;
|
||||
}
|
||||
|
||||
elsif($reading eq "octet") {
|
||||
$msg= "w";
|
||||
my $d = M232GetData($hash->{DeviceName}, $msg);
|
||||
return "Read error" if(!defined($d));
|
||||
my $state= hex $d;
|
||||
$retval= $state;
|
||||
}
|
||||
|
||||
else { return $u1; }
|
||||
|
||||
$hash->{READINGS}{$reading}{VAL}= $retval;
|
||||
$hash->{READINGS}{$reading}{TIME}= TimeNow();
|
||||
|
||||
return "$name $reading => $retval";
|
||||
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232_Write($$)
|
||||
{
|
||||
my ($hash,$msg) = @_;
|
||||
|
||||
return M232GetData($hash->{DeviceName}, $msg);
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
M232GetData($$)
|
||||
{
|
||||
my ($dev, $d) = @_;
|
||||
|
||||
my $MSGSTART= chr 1;
|
||||
my $MSGEND= chr 13;
|
||||
my $MSGACK= chr 6;
|
||||
my $MSGNACK= chr 21;
|
||||
|
||||
$d = $MSGSTART . $d . $MSGEND;
|
||||
|
||||
my $serport = new Device::SerialPort ($dev);
|
||||
if(!$serport) {
|
||||
Log 3, "M232: Can't open $dev: $!";
|
||||
return undef;
|
||||
}
|
||||
$serport->reset_error();
|
||||
$serport->baudrate(2400);
|
||||
$serport->databits(8);
|
||||
$serport->parity('none');
|
||||
$serport->stopbits(1);
|
||||
$serport->handshake('none');
|
||||
|
||||
Log 4, "M232: Sending $d";
|
||||
|
||||
my $rm = "M232: ?";
|
||||
|
||||
$serport->lookclear;
|
||||
$serport->write($d);
|
||||
|
||||
my $retval = "";
|
||||
my $status = "";
|
||||
|
||||
for(;;) {
|
||||
my ($rout, $rin) = ('', '');
|
||||
vec($rin, $serport->FILENO, 1) = 1;
|
||||
my $nfound = select($rout=$rin, undef, undef, 1.0);
|
||||
|
||||
if($nfound < 0) {
|
||||
$rm = "M232: Select error $nfound / $!";
|
||||
goto DONE;
|
||||
}
|
||||
last if($nfound == 0);
|
||||
|
||||
my $out = $serport->read(1);
|
||||
if(!defined($out) || length($out) == 0) {
|
||||
$rm = "M232 EOF on $dev";
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
if($out eq $MSGACK) {
|
||||
$rm= "M232: acknowledged";
|
||||
Log 4, "M232: return value \'" . $retval . "\'";
|
||||
$status= "ACK";
|
||||
} elsif($out eq $MSGNACK) {
|
||||
$rm= "M232: not acknowledged";
|
||||
$status= "NACK";
|
||||
$retval= undef;
|
||||
} else {
|
||||
$retval .= $out;
|
||||
}
|
||||
|
||||
if($status) {
|
||||
$serport->close();
|
||||
Log 4, $rm;
|
||||
return $retval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DONE:
|
||||
$serport->close();
|
||||
Log 4, $rm;
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
176
fhem/FHEM/81_M232Counter.pm
Normal file
176
fhem/FHEM/81_M232Counter.pm
Normal file
@ -0,0 +1,176 @@
|
||||
##############################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
sub M232Counter_Get($@);
|
||||
sub M232Counter_Set($@);
|
||||
sub M232Counter_SetBasis($@);
|
||||
sub M232Counter_Define($$);
|
||||
sub M232Counter_GetStatus($);
|
||||
|
||||
###################################
|
||||
sub
|
||||
M232Counter_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{GetFn} = "M232Counter_Get";
|
||||
$hash->{SetFn} = "M232Counter_Set";
|
||||
$hash->{DefFn} = "M232Counter_Define";
|
||||
|
||||
$hash->{AttrList} = "dummy:1,0 model;M232Counter loglevel:0,1,2,3,4,5";
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
M232Counter_GetStatus($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
InternalTimer(gettimeofday()+60, "M232Counter_GetStatus", $hash);
|
||||
}
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $d = IOWrite($hash, "z");
|
||||
if(!defined($d)) {
|
||||
my $msg = "M232Counter $name read error";
|
||||
Log GetLogLevel($name,2), $msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
my $tn = TimeNow();
|
||||
if(!defined($hash->{READINGS}{basis})) {
|
||||
$hash->{READINGS}{basis}{VAL}= 0;
|
||||
$hash->{READINGS}{basis}{TIME}= $tn;
|
||||
}
|
||||
if(!defined($hash->{READINGS}{count})) {
|
||||
$hash->{READINGS}{count}{VAL}= 0;
|
||||
$hash->{READINGS}{count}{TIME}= $tn;
|
||||
}
|
||||
my $count= hex $d;
|
||||
if($count< $hash->{READINGS}{count}{VAL}) {
|
||||
$hash->{READINGS}{basis}{VAL}+= 65536;
|
||||
$hash->{READINGS}{basis}{TIME}= $tn;
|
||||
}
|
||||
my $value= ($hash->{READINGS}{basis}{VAL}+$count) * $hash->{FACTOR};
|
||||
|
||||
$hash->{READINGS}{count}{TIME} = $tn;
|
||||
$hash->{READINGS}{count}{VAL} = $count;
|
||||
$hash->{READINGS}{value}{TIME} = $tn;
|
||||
$hash->{READINGS}{value}{VAL} = $value;
|
||||
|
||||
$hash->{CHANGED}[0]= "value: $value";
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
DoTrigger($name, undef) if($init_done);
|
||||
}
|
||||
|
||||
$hash->{STATE} = $value;
|
||||
Log GetLogLevel($name,4), "M232Counter $name: $value $hash->{UNIT}";
|
||||
|
||||
return $hash->{STATE};
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
M232Counter_Get($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "argument is missing" if(int(@a) != 2);
|
||||
|
||||
my $msg;
|
||||
|
||||
if($a[1] ne "status") {
|
||||
return "unknown get value, valid is status";
|
||||
}
|
||||
$hash->{LOCAL} = 1;
|
||||
my $v = M232Counter_GetStatus($hash);
|
||||
delete $hash->{LOCAL};
|
||||
|
||||
return "$a[0] $a[1] => $v";
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
M232Counter_Calibrate($@)
|
||||
{
|
||||
my ($hash, $value) = @_;
|
||||
my $rm= undef;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
|
||||
# adjust basis
|
||||
my $tn = TimeNow();
|
||||
$hash->{READINGS}{basis}{VAL}= $value / $hash->{FACTOR};
|
||||
$hash->{READINGS}{basis}{TIME}= $tn;
|
||||
$hash->{READINGS}{count}{VAL}= 0;
|
||||
$hash->{READINGS}{count}{TIME}= $tn;
|
||||
|
||||
# recalculate value
|
||||
$hash->{READINGS}{value}{VAL} = $value;
|
||||
$hash->{READINGS}{value}{TIME} = $tn;
|
||||
|
||||
# reset counter
|
||||
my $ret = IOWrite($hash, "Z1");
|
||||
if(!defined($ret)) {
|
||||
my $rm = "M232Counter $name read error";
|
||||
Log GetLogLevel($name,2), $rm;
|
||||
}
|
||||
|
||||
return $rm;
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
M232Counter_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $u = "Usage: set <name> value <value>";
|
||||
|
||||
return $u if(int(@a) != 3);
|
||||
my $reading= $a[1];
|
||||
my $value = $a[2];
|
||||
return $u unless($reading eq "value");
|
||||
|
||||
my $rm= M232Counter_Calibrate($hash, $value);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#############################
|
||||
sub
|
||||
M232Counter_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "syntax: define <name> M232Counter [unit] [multiplicator]"
|
||||
if(int(@a) < 2 && int(@a) > 4);
|
||||
|
||||
my $unit= ((int(@a) > 2) ? $a[2] : "ticks");
|
||||
my $factor= ((int(@a) > 3) ? $a[3] : 1.0);
|
||||
$hash->{UNIT}= $unit;
|
||||
$hash->{FACTOR}= $factor;
|
||||
|
||||
AssignIoPort($hash);
|
||||
|
||||
# InternalTimer blocks if init_done is not true
|
||||
my $oid = $init_done;
|
||||
$init_done = 1;
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
InternalTimer(gettimeofday()+60, "M232Counter_GetStatus", $hash);
|
||||
}
|
||||
|
||||
$init_done = $oid;
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
19
fhem/FHEM/99_Utils.pm
Normal file
19
fhem/FHEM/99_Utils.pm
Normal file
@ -0,0 +1,19 @@
|
||||
package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use POSIX;
|
||||
|
||||
sub
|
||||
Utils_Initialize($$)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
}
|
||||
|
||||
sub
|
||||
time_str2num($)
|
||||
{
|
||||
my ($str) = @_;
|
||||
my @a = split("[- :]", $str);
|
||||
return mktime($a[5],$a[4],$a[3],$a[2],$a[1]-1,$a[0]-1900,0,0,-1);
|
||||
}
|
||||
|
@ -156,4 +156,4 @@
|
||||
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
|
||||
Tagged as dirkh_20071019_0
|
||||
|
@ -1,12 +1,28 @@
|
||||
- 70_SCIVT.pm
|
||||
Support for an SCD series solar controler device. Details see
|
||||
http://english.ivt-hirschau.de/content.php?parent_id=CAT_64&doc_id=DOC_118
|
||||
- 80_M232.pm/81_M232Counter.pm
|
||||
Support for the M232 device from ELV by Boris.
|
||||
- 91_DbLog.pm
|
||||
Example to log data in a (DBI supported) database (MySQL, Oracle, etc)
|
||||
- 99_SUNRISE.pm
|
||||
The original Sunrise/Sunset support. Uses DateTime::Event::Sunrise. Uses the
|
||||
99_SUNRISE_EL.pm module instead.
|
||||
- 99_SUNRISE_EL.pm
|
||||
Support foor computins sunrise/sunset times.
|
||||
- 99_Utils.pm
|
||||
skeleton for self-written perl funtions.
|
||||
- 99_ALARM.pm
|
||||
Example for a Low Budget ALARM System by Martin
|
||||
- checkmsg.pl
|
||||
Check cwthe CRC of an FS20 hex message
|
||||
- fhem
|
||||
RC script by Stefan to be put into /etc/init.d and then symlinked
|
||||
to /etc/rc3.d or similar.
|
||||
Check header/function/crc of an FS20 hex message
|
||||
- crc.pl
|
||||
Computing CRC16 in perl
|
||||
- em1010.pl
|
||||
Standalone EM1010PC reader program
|
||||
- init-scripts
|
||||
RC scripts to be put into /etc/init.d and then symlinked to /etc/rc3.d or
|
||||
similar.
|
||||
- four2hex
|
||||
Convert housecode from ELV notation (4) to fhem.pl notation (hex)
|
||||
- fs20_holidays.sh
|
||||
@ -19,3 +35,5 @@
|
||||
Martin's "don't lock me out" program: look at the comment
|
||||
- rrd
|
||||
Peter's RRD support. See the HOWTO
|
||||
- serial.pm
|
||||
Serial line analyzer
|
||||
|
@ -433,26 +433,26 @@ 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>
|
||||
<a name="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>
|
||||
Can be applied to FHZ devices.<br />
|
||||
As the FHZ command buffer for FHT devices is limited, and commands
|
||||
are only sent to the FHT devices every 150 seconds, the hardware
|
||||
buffer may overflow and FHT commands get lost. Setting this attribute
|
||||
to 1 implements an "unlimited" software buffer<br>
|
||||
Default is disabled (i.e. not set or set to 0).</li><br>
|
||||
|
||||
<a name="retrycount"></a>
|
||||
<li>retrycount<br />
|
||||
Can be applied to FHT devices.<br />
|
||||
If the <a href="#softbuffer">softbuffer</a> attribute is set, then
|
||||
resend commands <code>retrycount</code> times if after 240 seconds
|
||||
no confirmation message is rececived from the corresponding FHT
|
||||
device.<br>
|
||||
Default is 3.</li><br>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -825,8 +825,8 @@ split in multiple lines<br><br>
|
||||
define n1 notify piri:on.* define a8 at +*{3}00:00:02 set lamp on-for-timer 1
|
||||
|
||||
# Switch the lamp on from sunset to 11 PM
|
||||
# Copy 99_SUNRISE.pm in the FHEM directory to have sunset_rel()
|
||||
{ sunrise_coord("8.686", "50.112", "Europe/Berlin") }
|
||||
# Copy 99_SUNRISE_EL.pm in the FHEM directory to have sunset_rel()
|
||||
{ sunrise_coord("8.686", "50.112", "") }
|
||||
define a9 at +*{sunset_rel()} set lamp on
|
||||
define a10 at *23:00:00 set lamp off
|
||||
|
||||
@ -858,11 +858,11 @@ split in multiple lines<br><br>
|
||||
(+) flag</li>
|
||||
|
||||
<li>In order to use the sunrise_rel()/sunset_rel() functions, copy the
|
||||
99_SUNRISE.pm file from the contrib into the modules (FHEM)
|
||||
directory, and put { sunrise_coord(long, lat, tz) } into your config
|
||||
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.
|
||||
99_SUNRISE_EL.pm file from the contrib into the modules (FHEM)
|
||||
directory, and put { sunrise_coord(long, lat, "") } into your
|
||||
<a href="#lastinclude">lastinclude</a> file, as in the above example.
|
||||
If you are not using sunrise_coord, then the coordinates for
|
||||
Frankfurt am Main, Germany will be used.
|
||||
</li>
|
||||
|
||||
<li>For even more complex date handling you either have to call fhem from
|
||||
@ -1626,10 +1626,8 @@ must between 5.5 and 30.5 Celsius. Value 5.5 set the actuator to OFF, value 30.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
If you add the 99_SUNRISE.pm from the contrib directory to your module
|
||||
directory (NOTE: you have to install the Perl module
|
||||
DateTime::Event::Sunrise first), then you have access to the follwing
|
||||
functions: <br>
|
||||
If you add the 99_SUNRISE_EL.pm from the contrib directory to your module
|
||||
directory, then you have access to the following functions: <br>
|
||||
<ul>
|
||||
sunset_rel()<br>
|
||||
sunset_abs()<br>
|
||||
|
@ -227,40 +227,21 @@ by fhem.pl?</b>
|
||||
<a name="faq11"></a>
|
||||
<b>11. I'd like to use this sunrise/sunset stuff, can you help me?</b>
|
||||
<ul>
|
||||
First you (most probably) have to install the DateTime::Event::Sunrise perl
|
||||
module, as it is not part of the standard distributions. If it is not
|
||||
installed and you copy the contrib/99_SUNRISE.pm into your module (FHEM)
|
||||
directory, then the program will not start up, telling you that this module
|
||||
is missing.
|
||||
The (IMHO) easiest way to install it is via the following command (probably
|
||||
as root):<br>
|
||||
<pre>
|
||||
perl -MCPAN -e shell
|
||||
cpan> install DateTime::Event::Sunrise</pre>
|
||||
This will fetch the module from a CPAN archive, compile it and install it,
|
||||
and will do the same with each perl module which is needed by this one.<br>
|
||||
|
||||
Copy contrib/99_SUNRISE_EL.pm into your FHEM directory.
|
||||
Next look for the geographic coordinates of your home, e.g with a GPS
|
||||
receiver or with googleearth. Compute the latitude/longitude as needed, and
|
||||
enter them in your init file (fhem.cfg) with the command:
|
||||
<pre>{sunrise_coord("<latitude>", "<longitude>", "Europe/Berlin") }</pre>
|
||||
If you are living in a different timezone, then change the string above
|
||||
according to the <code>perldoc DateTime</code> manpage.<br>
|
||||
|
||||
Now copy the contrib/99_SUNRISE.pm file into your module directory, and
|
||||
restart the program. If everything is ok, typing
|
||||
enter them in your lastinclude file with the command:
|
||||
<pre>{sunrise_coord("<latitude>", "<longitude>", "") }</pre>
|
||||
If everything is ok, typing
|
||||
<pre>{ sunrise_abs() }</pre>
|
||||
in the telnet prompt, will return the time of the sunrise today, in a
|
||||
HH:MM:SS format.<br><br>
|
||||
|
||||
<b>Note:</b> As fhem.cfg will be overwritten if you use the save command,
|
||||
it is better to put the sunrise_coord command in the "lastinclude" file,
|
||||
e.g. /home/fhem/fhem.cfg.static. This file will be read, if you set the
|
||||
lastinclude attribute:<br><code><br>
|
||||
|
||||
attr global lastinclude /home/fhem/fhem.cfg.static</code><br><br>
|
||||
|
||||
and will not be overwritten by save.
|
||||
99_SUNRISE_EL.pm is the ExtraLight version of the original 99_SUNRISE.pm,
|
||||
which needs the DateTime::Event::Sunrise perl module, which in turn is
|
||||
usually difficult to install. If you still want to use the original module,
|
||||
then the initialization string will be slightly different:
|
||||
<pre>{sunrise_coord("<latitude>", "<longitude>", "Europe/Berlin") }</pre>
|
||||
|
||||
</ul>
|
||||
|
||||
|
@ -22,13 +22,13 @@
|
||||
<li>FHZ1000 and FHZ1300 (both tested)</li>
|
||||
<li>FS20, FHT and KS300-2 (tested)</li>
|
||||
<li>HMS (untested, but should work)</li>
|
||||
<li>SunSet/SunRise.</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
What does not work right now:<br>
|
||||
<ul>
|
||||
<li>Automatic startup after reboot of the fritzbox</li>
|
||||
<li>SunSet/SunRise modules (planning a lightweight alternative).</li>
|
||||
<li>HTML Frontend. Planning a webfrontend/pgm2 "fhem" module, as do not
|
||||
know how to start CGI programs from the builtin web browser.
|
||||
<li>Gnuplot. No idea how to replace it, perhaps we compile a fritzbox
|
||||
|
@ -16,9 +16,9 @@ define a7 at +*{3}00:00:02 set lamp on-for-timer 1 # Blink 3 times
|
||||
|
||||
##################################
|
||||
# Switch the lamp on from sunset to 11 PM each day
|
||||
# You have to install 99_SUNRISE.pm in the FHEM directory to have sunset()
|
||||
# You have to install 99_SUNRISE_EL.pm in the FHEM directory to have sunset()
|
||||
# We have to use the relative versions, as the next event is computed now
|
||||
{ sunrise_coord("8.686", "50.112", "Europe/Berlin") }
|
||||
{ sunrise_coord("8.686", "50.112", "") }
|
||||
define a8 at +*{sunset_rel()} set lamp on
|
||||
define a9 at *23:00:00 set lamp off
|
||||
|
||||
|
@ -79,7 +79,7 @@ attr global userattr freigabe
|
||||
|
||||
define FHZ FHZ /dev/ttyUSB0
|
||||
|
||||
{ sunrise_coord("10.000", "53.550", "Europe/Berlin") }
|
||||
{ sunrise_coord("10.000", "53.550", "") }
|
||||
|
||||
# devices
|
||||
|
||||
|
46
fhem/fhem.pl
46
fhem/fhem.pl
@ -49,7 +49,7 @@ sub GetLogLevel(@);
|
||||
sub HandleTimeout();
|
||||
sub HandleArchiving($);
|
||||
sub IOWrite($@);
|
||||
sub InternalTimer($$$);
|
||||
sub InternalTimer($$$$);
|
||||
sub Log($$);
|
||||
sub OpenLogfile($);
|
||||
sub ResolveDateWildcards($@);
|
||||
@ -135,7 +135,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.28 2007-10-21 11:35:58 rudolfkoenig Exp $';
|
||||
my $cvsid = '$Id: fhem.pl,v 1.29 2007-11-26 08:27:04 rudolfkoenig Exp $';
|
||||
|
||||
$init_done = 0;
|
||||
|
||||
@ -226,6 +226,9 @@ if(int(@ARGV) == 2) {
|
||||
# End of client code
|
||||
###################################################
|
||||
|
||||
|
||||
###################################################
|
||||
# Server initialization
|
||||
my $ret = CommandInclude(undef, $attr{global}{configfile});
|
||||
die($ret) if($ret);
|
||||
|
||||
@ -244,20 +247,17 @@ if($attr{global}{statefile} && -r $attr{global}{statefile}) {
|
||||
}
|
||||
SignalHandling();
|
||||
|
||||
################################################
|
||||
# Main loop
|
||||
|
||||
my $pfn = $attr{global}{pidfilename};
|
||||
if($pfn) {
|
||||
die "$pfn: $!\n" if(!open(PID, ">$pfn"));
|
||||
print PID $$ . "\n";
|
||||
close(PID);
|
||||
}
|
||||
|
||||
$init_done = 1;
|
||||
|
||||
Log 0, "Server started (version $attr{global}{version}, pid $$)";
|
||||
|
||||
|
||||
################################################
|
||||
# Main Loop
|
||||
while (1) {
|
||||
my ($rout, $rin) = ('', '');
|
||||
@ -327,12 +327,24 @@ while (1) {
|
||||
sub
|
||||
IsDummy($)
|
||||
{
|
||||
my $dev = shift;
|
||||
my $devname = shift;
|
||||
|
||||
return 1 if(defined($attr{$dev}) && defined($attr{$dev}{dummy}));
|
||||
return 1 if(defined($attr{$devname}) && defined($attr{$devname}{dummy}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
################################################
|
||||
sub
|
||||
IsIoDummy($)
|
||||
{
|
||||
my $name = shift;
|
||||
|
||||
return IsDummy($defs{$name}{IODev}{NAME})
|
||||
if($defs{$name} && $defs{$name}{IODev});
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
################################################
|
||||
sub
|
||||
GetLogLevel(@)
|
||||
@ -1007,7 +1019,6 @@ PrintHash($$)
|
||||
my ($h, $lev) = @_;
|
||||
|
||||
my ($str,$sstr) = ("","");
|
||||
my $str = "";
|
||||
foreach my $c (sort keys %{$h}) {
|
||||
|
||||
if(ref($h->{$c})) {
|
||||
@ -1015,7 +1026,7 @@ PrintHash($$)
|
||||
if(defined($h->{$c}{TIME}) && defined($h->{$c}{VAL})) {
|
||||
$str .= sprintf("%*s %-19s %-15s %s\n",
|
||||
$lev," ", $h->{$c}{TIME},$c,$h->{$c}{VAL});
|
||||
} elsif($c eq "IODev") {
|
||||
} elsif($c eq "IODev" || $c eq "HASH") {
|
||||
$str .= sprintf("%*s %-10s %s\n", $lev," ",$c, $h->{$c}{NAME});
|
||||
} else {
|
||||
$sstr .= sprintf("%*s %s:\n",
|
||||
@ -1464,11 +1475,11 @@ HandleTimeout()
|
||||
|
||||
#####################################
|
||||
sub
|
||||
InternalTimer($$$)
|
||||
InternalTimer($$$$)
|
||||
{
|
||||
my ($tim, $fn, $arg) = @_;
|
||||
my ($tim, $fn, $arg, $waitIfInitNotDone) = @_;
|
||||
|
||||
if(!$init_done) {
|
||||
if(!$init_done && $waitIfInitNotDone) {
|
||||
select(undef, undef, undef, $tim-gettimeofday());
|
||||
no strict "refs";
|
||||
&{$fn}($arg);
|
||||
@ -1583,9 +1594,14 @@ DoTrigger($$)
|
||||
} elsif(!defined($defs{$dev}{CHANGED})) {
|
||||
return "";
|
||||
}
|
||||
$defs{$dev}{STATE} = $defs{$dev}{CHANGED}[0];
|
||||
|
||||
# STATE && {READINGS}{state} should be the same
|
||||
my $r = $defs{$dev}{READINGS};
|
||||
$r->{state}{VAL} = $defs{$dev}{STATE} if($r && $r->{state});
|
||||
|
||||
my $max = int(@{$defs{$dev}{CHANGED}});
|
||||
Log 5, "Triggering $dev ($max canges)";
|
||||
Log 5, "Triggering $dev ($max changes)";
|
||||
return "" if(defined($attr{$dev}) && defined($attr{$dev}{do_not_notify}));
|
||||
|
||||
################
|
||||
|
Loading…
Reference in New Issue
Block a user