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:
18
fhem/CHANGED
18
fhem/CHANGED
@@ -346,8 +346,18 @@
|
|||||||
- bugfix: FHT mode holiday_short added (9.9, Dirk)
|
- bugfix: FHT mode holiday_short added (9.9, Dirk)
|
||||||
- bugfix: Modifying a device from its own trigger crashes (Klaus, 10.9)
|
- bugfix: Modifying a device from its own trigger crashes (Klaus, 10.9)
|
||||||
- feature: webpgm2 output reformatted
|
- 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: 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: Softwarebuffer for FHT devices (Dirk 17.10)
|
||||||
- feature: FHT low temperatur warning and setting for lowtemp-offset (Dirk 17.10)
|
- feature: FHT low temperatur warning and offset (Dirk 17.10)
|
||||||
- change: Change naming for state into warnings (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->{SetFn} = "FHZ_Set";
|
||||||
$hash->{StateFn} = "FHZ_SetState";
|
$hash->{StateFn} = "FHZ_SetState";
|
||||||
$hash->{ParseFn} = "FHZ_Parse";
|
$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";
|
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
|
sub
|
||||||
FHZ_SetState($$$$)
|
FHZ_SetState($$$$)
|
||||||
@@ -220,11 +204,15 @@ FHZ_Define($$)
|
|||||||
delete $hash->{PortObj};
|
delete $hash->{PortObj};
|
||||||
delete $hash->{FD};
|
delete $hash->{FD};
|
||||||
|
|
||||||
|
my $name = $a[0];
|
||||||
my $dev = $a[2];
|
my $dev = $a[2];
|
||||||
$attr{$a[0]}{savefirst} = 1;
|
|
||||||
|
$attr{$name}{savefirst} = 1;
|
||||||
|
$attr{$name}{fhtsoftbuffer} = 1;
|
||||||
|
|
||||||
if($dev eq "none") {
|
if($dev eq "none") {
|
||||||
Log 1, "FHZ device is none, commands will be echoed only";
|
Log 1, "FHZ device is none, commands will be echoed only";
|
||||||
|
$attr{$name}{dummy} = 1;
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +243,7 @@ FHZ_Define($$)
|
|||||||
$hash->{DeviceName} = $dev;
|
$hash->{DeviceName} = $dev;
|
||||||
$hash->{PARTIAL} = "";
|
$hash->{PARTIAL} = "";
|
||||||
|
|
||||||
DoInit($a[0]);
|
DoInit($name);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,7 +411,7 @@ FHZ_Write($$$)
|
|||||||
##############
|
##############
|
||||||
# Write the next buffer not earlier than 0.22 seconds (= 65.6ms + 10ms +
|
# 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
|
# 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) {
|
} elsif($hash->{QUEUECNT} == 1) {
|
||||||
$hash->{QUEUE} = [ $bstring ];
|
$hash->{QUEUE} = [ $bstring ];
|
||||||
} else {
|
} else {
|
||||||
@@ -443,7 +431,7 @@ FHZ_HandleWriteQueue($)
|
|||||||
if($cnt > 0) {
|
if($cnt > 0) {
|
||||||
my $bstring = shift(@{$hash->{QUEUE}});
|
my $bstring = shift(@{$hash->{QUEUE}});
|
||||||
$hash->{PortObj}->write($bstring);
|
$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 strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
sub doSoftBuffer($);
|
||||||
|
sub softBufferTimer($);
|
||||||
|
sub sendCommand($$$$);
|
||||||
|
|
||||||
my %codes = (
|
my %codes = (
|
||||||
"0000.6" => "actuator",
|
"0000.6" => "actuator",
|
||||||
"00002c" => "synctime", # Not verified
|
"00002c" => "synctime", # Not verified
|
||||||
@@ -100,14 +104,13 @@ my %cantset = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
my %nosetarg = (
|
my %nosetarg = (
|
||||||
"help" => 1,
|
|
||||||
"refreshvalues" => 1,
|
"refreshvalues" => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
my %priority = (
|
my %priority = (
|
||||||
"desired-temp" => 1,
|
"desired-temp"=> 1,
|
||||||
"mode" => 2,
|
"mode" => 2,
|
||||||
"refreshvalues" => 3,
|
"refreshvalues"=> 3,
|
||||||
"holiday1" => 4,
|
"holiday1" => 4,
|
||||||
"holiday2" => 5,
|
"holiday2" => 5,
|
||||||
"day-temp" => 6,
|
"day-temp" => 6,
|
||||||
@@ -120,9 +123,9 @@ my %c2b; # command->button hash (reverse of codes)
|
|||||||
my %c2bset; # Setteable values
|
my %c2bset; # Setteable values
|
||||||
my %defptr;
|
my %defptr;
|
||||||
|
|
||||||
my $timerCheckBufferIsRunning = 0; # set to 1 if the timer is running
|
my $minFhzHardwareBuffer = 10; # min fhtbuf free bytes before sending commands
|
||||||
my $minFhzHardwareBufferSpace = 10; # min. bytes free in hardware buffer before sending commands
|
my $retryafter = 240; # in seconds, only when softbuffer is active
|
||||||
my $fhzHardwareBufferSpace = 0; # actual hardware buffer space in fhz
|
my $cmdcount = 0;
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
sub
|
sub
|
||||||
@@ -144,169 +147,103 @@ FHT_Initialize($)
|
|||||||
# 810c04b3 0909a001 1111 44006900
|
# 810c04b3 0909a001 1111 44006900
|
||||||
# 810b0402 83098301 1111 41301d
|
# 810b0402 83098301 1111 41301d
|
||||||
# 81090421 c409c401 1111 00
|
# 81090421 c409c401 1111 00
|
||||||
|
|
||||||
# 810c0d20 0909a001 3232 7e006724 (NYI)
|
# 810c0d20 0909a001 3232 7e006724 (NYI)
|
||||||
|
|
||||||
$hash->{Match} = "^81..(04|09|0d)..(0909a001|83098301|c409c401)..";
|
$hash->{Match} = "^81..(04|09|0d)..(0909a001|83098301|c409c401)..";
|
||||||
$hash->{SetFn} = "FHT_Set";
|
$hash->{SetFn} = "FHT_Set";
|
||||||
$hash->{StateFn} = "FHT_SetState";
|
$hash->{StateFn} = "FHT_SetState";
|
||||||
$hash->{DefFn} = "FHT_Define";
|
$hash->{DefFn} = "FHT_Define";
|
||||||
$hash->{UndefFn} = "FHT_Undef";
|
$hash->{UndefFn} = "FHT_Undef";
|
||||||
$hash->{ParseFn} = "FHT_Parse";
|
$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
|
||||||
sub FHT_Set($@)
|
FHT_Set($@)
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, @a) = @_;
|
||||||
my $ret = undef;
|
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 "\"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)
|
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"
|
return "\"set $a[0]\" needs two parameters"
|
||||||
if(@a != 3 && !(@a == 2 && $nosetarg{$a[1]}));
|
if(@a != 3 && !(@a == 2 && $nosetarg{$cmd}));
|
||||||
|
|
||||||
if($a[1] eq "refreshvalues") {
|
my $val = $a[2];
|
||||||
|
my $arg = "020183" . $hash->{CODE} . $c2bset{$cmd};
|
||||||
|
|
||||||
|
if ($cmd =~ m/-temp/) {
|
||||||
|
|
||||||
} elsif ($a[1] =~ m/-temp/) {
|
|
||||||
return "Invalid temperature, use NN.N" if($val !~ m/^\d*\.?\d+$/);
|
return "Invalid temperature, use NN.N" if($val !~ m/^\d*\.?\d+$/);
|
||||||
|
return "Invalid temperature, must between 5.5 and 30.5"
|
||||||
# additional check for temperature
|
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);
|
my $a = int($val*2);
|
||||||
$arg .= sprintf("%02x", $a);
|
$arg .= sprintf("%02x", $a);
|
||||||
$ret = sprintf("Rounded temperature to %.1f", $a/2) if($a/2 != $val);
|
$ret = sprintf("Rounded temperature to %.1f", $a/2) if($a/2 != $val);
|
||||||
$val = sprintf("%.1f", $a/2) if($a/2 != $val);
|
$val = sprintf("%.1f", $a/2);
|
||||||
$val = sprintf("%.1f", $val);
|
|
||||||
|
} 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)/);
|
return "Invalid timeformat, use HH:MM" if($val !~ m/^([0-2]\d):([0-5]\d)/);
|
||||||
my $a = ($1*6) + ($2/10);
|
my $a = ($1*6) + ($2/10);
|
||||||
$arg .= sprintf("%02x", $a);
|
$arg .= sprintf("%02x", $a);
|
||||||
|
|
||||||
my $nt = sprintf("%02d:%02d", $1, ($2/10)*10);
|
my $nt = sprintf("%02d:%02d", $1, ($2/10)*10);
|
||||||
$val = $nt if($nt ne $val);
|
|
||||||
$ret = "Rounded time to $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)
|
return "Invalid mode, use one of " . join(" ", sort keys %m2c)
|
||||||
if(!defined($m2c{$val}));
|
if(!defined($m2c{$val}));
|
||||||
$arg .= sprintf("%02x", $m2c{$val});
|
$arg .= sprintf("%02x", $m2c{$val});
|
||||||
|
|
||||||
} elsif ($a[1] eq "lowtemp-offset") {
|
} elsif ($cmd 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"
|
||||||
return "Invalid lowtemperature-offset, must between 1 and 5" if($val < 1 || $val > 5);
|
if($val !~ m/^[1-5]$/);
|
||||||
|
$arg .= sprintf("%02x", $val);
|
||||||
my $a = int($val);
|
$val = "$val.0";
|
||||||
$arg .= sprintf("%02x", $a);
|
|
||||||
$ret = sprintf("Rounded temperature to %d.0", $a) if($a != $val);
|
|
||||||
$val = "$a.0";
|
|
||||||
|
|
||||||
} else { # Holiday1, Holiday2
|
} 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));
|
$val = "" if (!defined($val));
|
||||||
|
|
||||||
if ( ($sbCount == 0 && $fhzHardwareBufferSpace >= $minFhzHardwareBufferSpace) || $attr{FHZ}{softbuffer} == 0) {
|
my $ioname = $hash->{IODev}->{NAME};
|
||||||
sendCommand ($hash, $arg, $name, $type, $val); # send command direct to FHZ
|
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 {
|
} 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;
|
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
|
sub
|
||||||
FHT_SetState($$$$)
|
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);
|
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];
|
$hash->{CODE} = $a[2];
|
||||||
$defptr{$a[2]} = $hash;
|
$defptr{$a[2]} = $hash;
|
||||||
|
$attr{$a[0]}{retrycount} = 3;
|
||||||
|
|
||||||
AssignIoPort($hash);
|
AssignIoPort($hash);
|
||||||
|
|
||||||
@@ -362,42 +301,29 @@ FHT_Parse($$)
|
|||||||
my $val = substr($msg, 26, 2) if(length($msg) > 26);
|
my $val = substr($msg, 26, 2) if(length($msg) > 26);
|
||||||
my $confirm = 0;
|
my $confirm = 0;
|
||||||
|
|
||||||
$fhzHardwareBufferSpace = getFhzBuffer () if ($fhzHardwareBufferSpace == 0);
|
|
||||||
|
|
||||||
if(!defined($defptr{$dev})) {
|
if(!defined($defptr{$dev})) {
|
||||||
Log 3, "FHT Unknown device $dev, please define it";
|
Log 3, "FHT Unknown device $dev, please define it";
|
||||||
return "UNDEFINED FHT $dev";
|
return "UNDEFINED FHT $dev";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $def = $defptr{$dev};
|
my $def = $defptr{$dev};
|
||||||
|
my $name = $def->{NAME};
|
||||||
|
|
||||||
# Unknown, but don't want report it. Should come with c409c401
|
# Unknown, but don't want report it. Should come with c409c401
|
||||||
return "" if($cde eq "00");
|
return "" if($cde eq "00");
|
||||||
|
|
||||||
if(length($cde) < 6) {
|
if(length($cde) < 6) {
|
||||||
my $name = $def->{NAME};
|
|
||||||
Log GetLogLevel($name,2), "FHT Unknown code from $name : $cde";
|
Log GetLogLevel($name,2), "FHT Unknown code from $name : $cde";
|
||||||
$def->{CHANGED}[0] = "unknown code $cde";
|
$def->{CHANGED}[0] = "unknown code $cde";
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(!$val) {
|
if(!$val) {
|
||||||
# This is a confirmation message. We reformat it so that
|
# This is a confirmation message. We reformat it so that
|
||||||
# it looks like a real message, and let the rest parse it
|
# 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);
|
$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";
|
$cde = substr($cde, 0, 2) . "0069";
|
||||||
|
|
||||||
# set help var to remember this is a confirmation
|
|
||||||
$confirm = 1;
|
$confirm = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,9 +338,9 @@ FHT_Parse($$)
|
|||||||
$val = hex($val);
|
$val = hex($val);
|
||||||
|
|
||||||
if(!$type) {
|
if(!$type) {
|
||||||
Log 4, "FHT $def->{NAME} (Unknown: $cde => $val)";
|
Log 4, "FHT $name (Unknown: $cde => $val)";
|
||||||
$def->{CHANGED}[0] = "unknown $cde: $val";
|
$def->{CHANGED}[0] = "unknown $cde: $val";
|
||||||
return $def->{NAME};
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $tn = TimeNow();
|
my $tn = TimeNow();
|
||||||
@@ -427,7 +353,7 @@ FHT_Parse($$)
|
|||||||
} elsif($type eq "lime-protection") {
|
} elsif($type eq "lime-protection") {
|
||||||
$val = sprintf("(actuator: %02d%%)", int(100*$val/255 + 0.5));
|
$val = sprintf("(actuator: %02d%%)", int(100*$val/255 + 0.5));
|
||||||
} elsif($cde ge "140069" && $cde le "2f0069") { # Time specs
|
} 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
|
return "" if($val == 144); # Empty, forget it
|
||||||
my $hour = $val / 6;
|
my $hour = $val / 6;
|
||||||
my $min = ($val % 6) * 10;
|
my $min = ($val % 6) * 10;
|
||||||
@@ -463,21 +389,12 @@ FHT_Parse($$)
|
|||||||
|
|
||||||
} elsif($type eq "warnings") {
|
} elsif($type eq "warnings") {
|
||||||
|
|
||||||
my @nVal;
|
my $nVal;
|
||||||
$nVal[0] = "Battery low" if ($val & 1);
|
if($val & 1) { $nVal = "Battery low"; }
|
||||||
$nVal[1] = "Window open" if ($val & 32);
|
if($val & 2) { $nVal .= "; " if($nVal); $nVal .= "Temperature too low"; }
|
||||||
$nVal[2] = "Fault on window sensor" if ($val & 16);
|
if($val &32) { $nVal .= "; " if($nVal); $nVal .= "Window open"; }
|
||||||
$nVal[3] = "Temperature to low" if ($val & 2);
|
if($val &16) { $nVal .= "; " if($nVal); $nVal .= "Fault on window sensor"; }
|
||||||
|
$val = $nVal? $nVal : "none";
|
||||||
if ($val > 0) {
|
|
||||||
$val = "";
|
|
||||||
foreach (@nVal) {
|
|
||||||
$val .= "$_; " if (defined($_));
|
|
||||||
}
|
|
||||||
$val = substr($val, 0, length($val)-2);
|
|
||||||
} else {
|
|
||||||
$val = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
} elsif($type eq "lowtemp-offset") {
|
} elsif($type eq "lowtemp-offset") {
|
||||||
$val = sprintf("%d.0 (Celsius)", $val)
|
$val = sprintf("%d.0 (Celsius)", $val)
|
||||||
@@ -489,194 +406,127 @@ FHT_Parse($$)
|
|||||||
|
|
||||||
$def->{READINGS}{$type}{TIME} = $tn;
|
$def->{READINGS}{$type}{TIME} = $tn;
|
||||||
$def->{READINGS}{$type}{VAL} = $val;
|
$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->{CHANGED}[0] = "$type: $val";
|
||||||
$def->{STATE} = "$type: $val" if($type eq "measured-temp");
|
$def->{STATE} = "$type: $val" if($type eq "measured-temp");
|
||||||
return $def->{NAME};
|
|
||||||
|
Log 4, "FHT $name ($type: $val)";
|
||||||
|
|
||||||
|
################################
|
||||||
|
# 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete($io->{SOFTBUFFER}{$found}) if($found);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
# check are commands in softwarebuffer
|
|
||||||
# ans send the next command to the FHZ
|
# Check the softwarebuffer and send/resend commands
|
||||||
sub timerCheckBuffer ($)
|
sub
|
||||||
|
doSoftBuffer($)
|
||||||
{
|
{
|
||||||
|
my ($io) = @_;
|
||||||
|
|
||||||
Log 4, "Timer (Checking for unsend FHT commands)";
|
|
||||||
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $bufCount = 0; # help counter
|
|
||||||
my $now = gettimeofday();
|
my $now = gettimeofday();
|
||||||
my $ts = time;
|
|
||||||
|
|
||||||
# set default values for config value attr FHZ softbuffer
|
my $count = 0;
|
||||||
$attr{FHZ}{softrepeat} = 240 if (!defined($attr{FHZ}{softrepeat}));
|
foreach my $key (keys %{ $io->{SOFTBUFFER} }) {
|
||||||
$attr{FHZ}{softmaxretry} = 3 if (!defined($attr{FHZ}{softmaxretry}));
|
|
||||||
|
|
||||||
# loop to process all FHT devices
|
$count++;
|
||||||
foreach my $d (keys %defptr) {
|
my $h = $io->{SOFTBUFFER}{$key};
|
||||||
my $def = $defptr{$d}; # the actual FHT device
|
my $name = $h->{HASH}->{NAME};
|
||||||
|
|
||||||
# process all buffer entries
|
if($h->{NSENT}) {
|
||||||
foreach my $c (sort keys %{$def->{SENDBUFFER}}) {
|
next if($now-$h->{SENDTIME} < $retryafter);
|
||||||
my ($rPr, undef, $rType) = split (/:/, $c); # priority and type
|
my $retry = $attr{$name}{retrycount};
|
||||||
my $rVal = $def->{SENDBUFFER}{$c}{VAL}; # value
|
if($h->{NSENT} > $retry) {
|
||||||
my $rTs = $def->{SENDBUFFER}{$c}{SENDTIME}; # the time of the sending moment to the FHT
|
Log GetLogLevel($name,2), "$name set $h->{CMD} $h->{VAL}: ".
|
||||||
my $rRetry = $def->{SENDBUFFER}{$c}{RETRY}; # retry counter
|
"no confirmation after $h->{NSENT} tries, giving up";
|
||||||
$rRetry ++ if ($fhzHardwareBufferSpace > $minFhzHardwareBufferSpace); # increase retrycounter if enough hardwarebuffer available
|
delete($io->{SOFTBUFFER}{$key});
|
||||||
my $rKey = $c; # the bufferkey
|
next;
|
||||||
|
|
||||||
$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) {
|
next if(getFhzBuffer($io) < $minFhzHardwareBuffer);
|
||||||
Log 4, "Refresh FHT resend timer";
|
sendCommand($h->{HASH}, $h->{CMD}, $h->{VAL}, $h->{ARG});
|
||||||
InternalTimer(gettimeofday()+70, "timerCheckBuffer", $hash); # restart the internal Timer if any buffer contains commands
|
$h->{SENDTIME} = $now;
|
||||||
} else {
|
$h->{NSENT}++;
|
||||||
$timerCheckBufferIsRunning = 0; # remember timer is not running anymore
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
# Wrapper for the InternalTimer
|
||||||
#
|
sub
|
||||||
sub toSendbuffer ($$$$)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# Send FHZ command
|
||||||
|
sub
|
||||||
|
sendCommand($$$$)
|
||||||
|
{
|
||||||
|
my ($hash, $cmd, $val, $arg) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dev = $hash->{CODE};
|
} else {
|
||||||
my $def = $defptr{$dev};
|
|
||||||
|
|
||||||
my $tn = TimeNow(); # Readable time
|
IOWrite($hash, "04", $arg) if(!IsDummy($name));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
Log GetLogLevel($name,2), "FHT set $name $cmd $val";
|
||||||
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;
|
1;
|
||||||
|
|||||||
@@ -43,11 +43,14 @@ EM_Define($$)
|
|||||||
delete $hash->{PortObj};
|
delete $hash->{PortObj};
|
||||||
delete $hash->{FD};
|
delete $hash->{FD};
|
||||||
|
|
||||||
|
my $name = $a[0];
|
||||||
my $dev = $a[2];
|
my $dev = $a[2];
|
||||||
$attr{$a[0]}{savefirst} = 1;
|
|
||||||
|
$attr{$name}{savefirst} = 1;
|
||||||
|
|
||||||
if($dev eq "none") {
|
if($dev eq "none") {
|
||||||
Log 1, "EM device is none, commands will be echoed only";
|
Log 1, "EM device is none, commands will be echoed only";
|
||||||
|
$attr{$name}{dummy} = 1;
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,15 +31,17 @@ EMWZ_GetStatus($)
|
|||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
if(!$hash->{LOCAL}) {
|
if(!$hash->{LOCAL}) {
|
||||||
InternalTimer(gettimeofday()+300, "EMWZ_GetStatus", $hash);
|
InternalTimer(gettimeofday()+300, "EMWZ_GetStatus", $hash, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dnr = $hash->{DEVNR};
|
my $dnr = $hash->{DEVNR};
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
return "Empty status: dummy IO device" if(IsIoDummy($name));
|
||||||
|
|
||||||
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
||||||
if(!defined($d)) {
|
if(!defined($d)) {
|
||||||
my $msg = "EMWZ $name read error";
|
my $msg = "EMWZ $name read error (GetStatus 1)";
|
||||||
Log GetLogLevel($name,2), $msg;
|
Log GetLogLevel($name,2), $msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
@@ -54,7 +56,7 @@ EMWZ_GetStatus($)
|
|||||||
my $pulses=w($d,13);
|
my $pulses=w($d,13);
|
||||||
my $ec=w($d,49) / 10;
|
my $ec=w($d,49) / 10;
|
||||||
if($ec <= 0) {
|
if($ec <= 0) {
|
||||||
my $msg = "EMWZ read error";
|
my $msg = "EMWZ read error (GetStatus 2)";
|
||||||
Log GetLogLevel($name,2), $msg;
|
Log GetLogLevel($name,2), $msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
@@ -124,10 +126,12 @@ EMWZ_Set($@)
|
|||||||
|
|
||||||
return $u if(int(@a) != 3);
|
return $u if(int(@a) != 3);
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
return "" if(IsIoDummy($name));
|
||||||
|
|
||||||
my $v = $a[2];
|
my $v = $a[2];
|
||||||
my $d = $hash->{DEVNR};
|
my $d = $hash->{DEVNR};
|
||||||
my $msg;
|
my $msg;
|
||||||
my $name = $hash->{NAME};
|
|
||||||
|
|
||||||
if($a[1] eq "price") {
|
if($a[1] eq "price") {
|
||||||
$v *= 10000; # Make display and input the same
|
$v *= 10000; # Make display and input the same
|
||||||
@@ -141,9 +145,10 @@ EMWZ_Set($@)
|
|||||||
return $u;
|
return $u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
my $ret = IOWrite($hash, $msg);
|
my $ret = IOWrite($hash, $msg);
|
||||||
if(!defined($ret)) {
|
if(!defined($ret)) {
|
||||||
my $msg = "EMWZ $name read error";
|
my $msg = "EMWZ $name read error (Set)";
|
||||||
Log GetLogLevel($name,2), $msg;
|
Log GetLogLevel($name,2), $msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
@@ -170,11 +175,7 @@ EMWZ_Define($$)
|
|||||||
AssignIoPort($hash);
|
AssignIoPort($hash);
|
||||||
|
|
||||||
|
|
||||||
# InternalTimer blocks if init_done is not true
|
|
||||||
my $oid = $init_done;
|
|
||||||
$init_done = 1;
|
|
||||||
EMWZ_GetStatus($hash);
|
EMWZ_GetStatus($hash);
|
||||||
$init_done = $oid;
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use warnings;
|
|||||||
use Time::HiRes qw(gettimeofday);
|
use Time::HiRes qw(gettimeofday);
|
||||||
|
|
||||||
sub EMEM_Get($@);
|
sub EMEM_Get($@);
|
||||||
sub EMEM_Set($@);
|
|
||||||
sub EMEM_Define($$);
|
sub EMEM_Define($$);
|
||||||
sub EMEM_GetStatus($);
|
sub EMEM_GetStatus($);
|
||||||
|
|
||||||
@@ -29,15 +28,17 @@ EMEM_GetStatus($)
|
|||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
if(!$hash->{LOCAL}) {
|
if(!$hash->{LOCAL}) {
|
||||||
InternalTimer(gettimeofday()+300, "EMEM_GetStatus", $hash);
|
InternalTimer(gettimeofday()+300, "EMEM_GetStatus", $hash, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $dnr = $hash->{DEVNR};
|
my $dnr = $hash->{DEVNR};
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
return "Empty status: dummy IO device" if(IsIoDummy($name));
|
||||||
|
|
||||||
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
my $d = IOWrite($hash, sprintf("7a%02x", $dnr-1));
|
||||||
if(!defined($d)) {
|
if(!defined($d)) {
|
||||||
my $msg = "EMEM $name read error";
|
my $msg = "EMEM $name read error (GetStatus 1)";
|
||||||
Log GetLogLevel($name,2), $msg;
|
Log GetLogLevel($name,2), $msg;
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
@@ -122,11 +123,7 @@ EMEM_Define($$)
|
|||||||
AssignIoPort($hash);
|
AssignIoPort($hash);
|
||||||
|
|
||||||
|
|
||||||
# InternalTimer blocks if init_done is not true
|
|
||||||
my $oid = $init_done;
|
|
||||||
$init_done = 1;
|
|
||||||
EMEM_GetStatus($hash);
|
EMEM_GetStatus($hash);
|
||||||
$init_done = $oid;
|
|
||||||
return undef;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
- 91_DbLog.pm
|
||||||
Example to log data in a (DBI supported) database (MySQL, Oracle, etc)
|
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
|
- 99_ALARM.pm
|
||||||
Example for a Low Budget ALARM System by Martin
|
Example for a Low Budget ALARM System by Martin
|
||||||
- checkmsg.pl
|
- checkmsg.pl
|
||||||
Check cwthe CRC of an FS20 hex message
|
Check header/function/crc of an FS20 hex message
|
||||||
- fhem
|
- crc.pl
|
||||||
RC script by Stefan to be put into /etc/init.d and then symlinked
|
Computing CRC16 in perl
|
||||||
to /etc/rc3.d or similar.
|
- 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
|
- four2hex
|
||||||
Convert housecode from ELV notation (4) to fhem.pl notation (hex)
|
Convert housecode from ELV notation (4) to fhem.pl notation (hex)
|
||||||
- fs20_holidays.sh
|
- fs20_holidays.sh
|
||||||
@@ -19,3 +35,5 @@
|
|||||||
Martin's "don't lock me out" program: look at the comment
|
Martin's "don't lock me out" program: look at the comment
|
||||||
- rrd
|
- rrd
|
||||||
Peter's RRD support. See the HOWTO
|
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>
|
<a name="skip_next"></a>
|
||||||
<li>skip_next<br>
|
<li>skip_next<br>
|
||||||
Can be applied to at devices.<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 />
|
<li>softbuffer<br />
|
||||||
Can be applied to FHZ devices.<br />
|
Can be applied to FHZ devices.<br />
|
||||||
Used to disable the FHZ softbuffer for FHT deviced (enabled by default).<br />
|
As the FHZ command buffer for FHT devices is limited, and commands
|
||||||
<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 />
|
are only sent to the FHT devices every 150 seconds, the hardware
|
||||||
<br />
|
buffer may overflow and FHT commands get lost. Setting this attribute
|
||||||
<a name="softbuffer" id="softbuffer"></a> </li>
|
to 1 implements an "unlimited" software buffer<br>
|
||||||
<li>softrepeat<br />
|
Default is disabled (i.e. not set or set to 0).</li><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 />
|
<a name="retrycount"></a>
|
||||||
<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 />
|
<li>retrycount<br />
|
||||||
<br />
|
Can be applied to FHT devices.<br />
|
||||||
<a name="softbuffer" id="softbuffer"></a> </li>
|
If the <a href="#softbuffer">softbuffer</a> attribute is set, then
|
||||||
<li>softmaxretry<br />
|
resend commands <code>retrycount</code> times if after 240 seconds
|
||||||
Can be applied to FHZ devices.<br />
|
no confirmation message is rececived from the corresponding FHT
|
||||||
Used to set the maximal retries in which FHEM try to resend a failed command to FHT devices (default 3).<br />
|
device.<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 />
|
Default is 3.</li><br>
|
||||||
<br>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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
|
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
|
# Switch the lamp on from sunset to 11 PM
|
||||||
# Copy 99_SUNRISE.pm in the FHEM directory to have sunset_rel()
|
# Copy 99_SUNRISE_EL.pm in the FHEM directory to have sunset_rel()
|
||||||
{ sunrise_coord("8.686", "50.112", "Europe/Berlin") }
|
{ sunrise_coord("8.686", "50.112", "") }
|
||||||
define a9 at +*{sunset_rel()} set lamp on
|
define a9 at +*{sunset_rel()} set lamp on
|
||||||
define a10 at *23:00:00 set lamp off
|
define a10 at *23:00:00 set lamp off
|
||||||
|
|
||||||
@@ -858,11 +858,11 @@ split in multiple lines<br><br>
|
|||||||
(+) flag</li>
|
(+) flag</li>
|
||||||
|
|
||||||
<li>In order to use the sunrise_rel()/sunset_rel() functions, copy the
|
<li>In order to use the sunrise_rel()/sunset_rel() functions, copy the
|
||||||
99_SUNRISE.pm file from the contrib into the modules (FHEM)
|
99_SUNRISE_EL.pm file from the contrib into the modules (FHEM)
|
||||||
directory, and put { sunrise_coord(long, lat, tz) } into your config
|
directory, and put { sunrise_coord(long, lat, "") } into your
|
||||||
file, as in the above example. If you are not using sunrise_coord, then
|
<a href="#lastinclude">lastinclude</a> file, as in the above example.
|
||||||
the coordinates for Frankfurt am Main, Germany will be used.
|
If you are not using sunrise_coord, then the coordinates for
|
||||||
You also have to install the Datetime::Event::Sunrise perl module.
|
Frankfurt am Main, Germany will be used.
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>For even more complex date handling you either have to call fhem from
|
<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>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
If you add the 99_SUNRISE.pm from the contrib directory to your module
|
If you add the 99_SUNRISE_EL.pm from the contrib directory to your module
|
||||||
directory (NOTE: you have to install the Perl module
|
directory, then you have access to the following functions: <br>
|
||||||
DateTime::Event::Sunrise first), then you have access to the follwing
|
|
||||||
functions: <br>
|
|
||||||
<ul>
|
<ul>
|
||||||
sunset_rel()<br>
|
sunset_rel()<br>
|
||||||
sunset_abs()<br>
|
sunset_abs()<br>
|
||||||
|
|||||||
@@ -227,40 +227,21 @@ by fhem.pl?</b>
|
|||||||
<a name="faq11"></a>
|
<a name="faq11"></a>
|
||||||
<b>11. I'd like to use this sunrise/sunset stuff, can you help me?</b>
|
<b>11. I'd like to use this sunrise/sunset stuff, can you help me?</b>
|
||||||
<ul>
|
<ul>
|
||||||
First you (most probably) have to install the DateTime::Event::Sunrise perl
|
Copy contrib/99_SUNRISE_EL.pm into your FHEM directory.
|
||||||
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>
|
|
||||||
|
|
||||||
Next look for the geographic coordinates of your home, e.g with a GPS
|
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
|
receiver or with googleearth. Compute the latitude/longitude as needed, and
|
||||||
enter them in your init file (fhem.cfg) with the command:
|
enter them in your lastinclude file with the command:
|
||||||
<pre>{sunrise_coord("<latitude>", "<longitude>", "Europe/Berlin") }</pre>
|
<pre>{sunrise_coord("<latitude>", "<longitude>", "") }</pre>
|
||||||
If you are living in a different timezone, then change the string above
|
If everything is ok, typing
|
||||||
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
|
|
||||||
<pre>{ sunrise_abs() }</pre>
|
<pre>{ sunrise_abs() }</pre>
|
||||||
in the telnet prompt, will return the time of the sunrise today, in a
|
in the telnet prompt, will return the time of the sunrise today, in a
|
||||||
HH:MM:SS format.<br><br>
|
HH:MM:SS format.<br><br>
|
||||||
|
|
||||||
<b>Note:</b> As fhem.cfg will be overwritten if you use the save command,
|
99_SUNRISE_EL.pm is the ExtraLight version of the original 99_SUNRISE.pm,
|
||||||
it is better to put the sunrise_coord command in the "lastinclude" file,
|
which needs the DateTime::Event::Sunrise perl module, which in turn is
|
||||||
e.g. /home/fhem/fhem.cfg.static. This file will be read, if you set the
|
usually difficult to install. If you still want to use the original module,
|
||||||
lastinclude attribute:<br><code><br>
|
then the initialization string will be slightly different:
|
||||||
|
<pre>{sunrise_coord("<latitude>", "<longitude>", "Europe/Berlin") }</pre>
|
||||||
attr global lastinclude /home/fhem/fhem.cfg.static</code><br><br>
|
|
||||||
|
|
||||||
and will not be overwritten by save.
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -22,13 +22,13 @@
|
|||||||
<li>FHZ1000 and FHZ1300 (both tested)</li>
|
<li>FHZ1000 and FHZ1300 (both tested)</li>
|
||||||
<li>FS20, FHT and KS300-2 (tested)</li>
|
<li>FS20, FHT and KS300-2 (tested)</li>
|
||||||
<li>HMS (untested, but should work)</li>
|
<li>HMS (untested, but should work)</li>
|
||||||
|
<li>SunSet/SunRise.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
What does not work right now:<br>
|
What does not work right now:<br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Automatic startup after reboot of the fritzbox</li>
|
<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
|
<li>HTML Frontend. Planning a webfrontend/pgm2 "fhem" module, as do not
|
||||||
know how to start CGI programs from the builtin web browser.
|
know how to start CGI programs from the builtin web browser.
|
||||||
<li>Gnuplot. No idea how to replace it, perhaps we compile a fritzbox
|
<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
|
# 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
|
# 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 a8 at +*{sunset_rel()} set lamp on
|
||||||
define a9 at *23:00:00 set lamp off
|
define a9 at *23:00:00 set lamp off
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ attr global userattr freigabe
|
|||||||
|
|
||||||
define FHZ FHZ /dev/ttyUSB0
|
define FHZ FHZ /dev/ttyUSB0
|
||||||
|
|
||||||
{ sunrise_coord("10.000", "53.550", "Europe/Berlin") }
|
{ sunrise_coord("10.000", "53.550", "") }
|
||||||
|
|
||||||
# devices
|
# devices
|
||||||
|
|
||||||
|
|||||||
46
fhem/fhem.pl
46
fhem/fhem.pl
@@ -49,7 +49,7 @@ sub GetLogLevel(@);
|
|||||||
sub HandleTimeout();
|
sub HandleTimeout();
|
||||||
sub HandleArchiving($);
|
sub HandleArchiving($);
|
||||||
sub IOWrite($@);
|
sub IOWrite($@);
|
||||||
sub InternalTimer($$$);
|
sub InternalTimer($$$$);
|
||||||
sub Log($$);
|
sub Log($$);
|
||||||
sub OpenLogfile($);
|
sub OpenLogfile($);
|
||||||
sub ResolveDateWildcards($@);
|
sub ResolveDateWildcards($@);
|
||||||
@@ -135,7 +135,7 @@ my %intAt; # Internal at timer hash.
|
|||||||
my $intAtCnt=0;
|
my $intAtCnt=0;
|
||||||
my $reread_active = 0;
|
my $reread_active = 0;
|
||||||
my $AttrList = "room comment";
|
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;
|
$init_done = 0;
|
||||||
|
|
||||||
@@ -226,6 +226,9 @@ if(int(@ARGV) == 2) {
|
|||||||
# End of client code
|
# End of client code
|
||||||
###################################################
|
###################################################
|
||||||
|
|
||||||
|
|
||||||
|
###################################################
|
||||||
|
# Server initialization
|
||||||
my $ret = CommandInclude(undef, $attr{global}{configfile});
|
my $ret = CommandInclude(undef, $attr{global}{configfile});
|
||||||
die($ret) if($ret);
|
die($ret) if($ret);
|
||||||
|
|
||||||
@@ -244,20 +247,17 @@ if($attr{global}{statefile} && -r $attr{global}{statefile}) {
|
|||||||
}
|
}
|
||||||
SignalHandling();
|
SignalHandling();
|
||||||
|
|
||||||
################################################
|
|
||||||
# Main loop
|
|
||||||
|
|
||||||
my $pfn = $attr{global}{pidfilename};
|
my $pfn = $attr{global}{pidfilename};
|
||||||
if($pfn) {
|
if($pfn) {
|
||||||
die "$pfn: $!\n" if(!open(PID, ">$pfn"));
|
die "$pfn: $!\n" if(!open(PID, ">$pfn"));
|
||||||
print PID $$ . "\n";
|
print PID $$ . "\n";
|
||||||
close(PID);
|
close(PID);
|
||||||
}
|
}
|
||||||
|
|
||||||
$init_done = 1;
|
$init_done = 1;
|
||||||
|
|
||||||
Log 0, "Server started (version $attr{global}{version}, pid $$)";
|
Log 0, "Server started (version $attr{global}{version}, pid $$)";
|
||||||
|
|
||||||
|
################################################
|
||||||
# Main Loop
|
# Main Loop
|
||||||
while (1) {
|
while (1) {
|
||||||
my ($rout, $rin) = ('', '');
|
my ($rout, $rin) = ('', '');
|
||||||
@@ -327,12 +327,24 @@ while (1) {
|
|||||||
sub
|
sub
|
||||||
IsDummy($)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################
|
||||||
|
sub
|
||||||
|
IsIoDummy($)
|
||||||
|
{
|
||||||
|
my $name = shift;
|
||||||
|
|
||||||
|
return IsDummy($defs{$name}{IODev}{NAME})
|
||||||
|
if($defs{$name} && $defs{$name}{IODev});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
sub
|
sub
|
||||||
GetLogLevel(@)
|
GetLogLevel(@)
|
||||||
@@ -1007,7 +1019,6 @@ PrintHash($$)
|
|||||||
my ($h, $lev) = @_;
|
my ($h, $lev) = @_;
|
||||||
|
|
||||||
my ($str,$sstr) = ("","");
|
my ($str,$sstr) = ("","");
|
||||||
my $str = "";
|
|
||||||
foreach my $c (sort keys %{$h}) {
|
foreach my $c (sort keys %{$h}) {
|
||||||
|
|
||||||
if(ref($h->{$c})) {
|
if(ref($h->{$c})) {
|
||||||
@@ -1015,7 +1026,7 @@ PrintHash($$)
|
|||||||
if(defined($h->{$c}{TIME}) && defined($h->{$c}{VAL})) {
|
if(defined($h->{$c}{TIME}) && defined($h->{$c}{VAL})) {
|
||||||
$str .= sprintf("%*s %-19s %-15s %s\n",
|
$str .= sprintf("%*s %-19s %-15s %s\n",
|
||||||
$lev," ", $h->{$c}{TIME},$c,$h->{$c}{VAL});
|
$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});
|
$str .= sprintf("%*s %-10s %s\n", $lev," ",$c, $h->{$c}{NAME});
|
||||||
} else {
|
} else {
|
||||||
$sstr .= sprintf("%*s %s:\n",
|
$sstr .= sprintf("%*s %s:\n",
|
||||||
@@ -1464,11 +1475,11 @@ HandleTimeout()
|
|||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
sub
|
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());
|
select(undef, undef, undef, $tim-gettimeofday());
|
||||||
no strict "refs";
|
no strict "refs";
|
||||||
&{$fn}($arg);
|
&{$fn}($arg);
|
||||||
@@ -1583,9 +1594,14 @@ DoTrigger($$)
|
|||||||
} elsif(!defined($defs{$dev}{CHANGED})) {
|
} elsif(!defined($defs{$dev}{CHANGED})) {
|
||||||
return "";
|
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}});
|
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}));
|
return "" if(defined($attr{$dev}) && defined($attr{$dev}{do_not_notify}));
|
||||||
|
|
||||||
################
|
################
|
||||||
|
|||||||
Reference in New Issue
Block a user