mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-01 20:20:10 +00:00
711 lines
17 KiB
Perl
Executable File
711 lines
17 KiB
Perl
Executable File
##############################################
|
|
|
|
package main;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Time::HiRes qw(gettimeofday);
|
|
|
|
|
|
sub CUL_Write($$$);
|
|
sub CUL_Read($);
|
|
sub CUL_ReadAnswer($$);
|
|
sub CUL_Ready($);
|
|
sub CUL_HandleCurRequest($$);
|
|
|
|
my $initstr = "X21"; # Only translated messages + RSSI
|
|
my %gets = (
|
|
"version" => "V",
|
|
"raw" => "",
|
|
"ccconf" => "=",
|
|
"uptime" => "t",
|
|
);
|
|
|
|
my %sets = (
|
|
"raw" => "",
|
|
"freq" => "",
|
|
"bWidth" => "",
|
|
"rAmpl" => "",
|
|
"sens" => "",
|
|
"verbose" => "X",
|
|
"led" => "l",
|
|
"patable" => "x",
|
|
);
|
|
|
|
my @ampllist = (24, 27, 30, 33, 36, 38, 40, 42);
|
|
|
|
sub
|
|
CUL_Initialize($)
|
|
{
|
|
my ($hash) = @_;
|
|
|
|
# Provider
|
|
$hash->{ReadFn} = "CUL_Read";
|
|
$hash->{WriteFn} = "CUL_Write";
|
|
$hash->{Clients} = ":FS20:FHT:KS300:CUL_EM:CUL_WS:";
|
|
$hash->{ReadyFn} = "CUL_Ready";
|
|
|
|
# Normal devices
|
|
$hash->{DefFn} = "CUL_Define";
|
|
$hash->{UndefFn} = "CUL_Undef";
|
|
$hash->{GetFn} = "CUL_Get";
|
|
$hash->{SetFn} = "CUL_Set";
|
|
$hash->{StateFn} = "CUL_SetState";
|
|
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 filtertimeout repeater:1,0 " .
|
|
"showtime:1,0 model:CUL,CUR loglevel:0,1,2,3,4,5,6 " .
|
|
"CUR_id_list";
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Define($$)
|
|
{
|
|
my ($hash, $def) = @_;
|
|
my @a = split("[ \t][ \t]*", $def);
|
|
my $po;
|
|
|
|
return "wrong syntax: define <name> CUL devicename [mobile]"
|
|
if(@a < 3 || @a > 4);
|
|
|
|
delete $hash->{PortObj};
|
|
delete $hash->{FD};
|
|
|
|
my $name = $a[0];
|
|
my $dev = $a[2];
|
|
$hash->{MOBILE} = 1 if($a[3] && $a[3] eq "mobile");
|
|
$hash->{STATE} = "defined";
|
|
|
|
$attr{$name}{savefirst} = 1;
|
|
$attr{$name}{repeater} = 1;
|
|
|
|
if($dev eq "none") {
|
|
Log 1, "CUL device is none, commands will be echoed only";
|
|
$attr{$name}{dummy} = 1;
|
|
return undef;
|
|
}
|
|
|
|
$hash->{DeviceName} = $dev;
|
|
$hash->{PARTIAL} = "";
|
|
Log 3, "CUL opening CUL device $dev";
|
|
if ($^O=~/Win/) {
|
|
require Win32::SerialPort;
|
|
$po = new Win32::SerialPort ($dev);
|
|
} else {
|
|
require Device::SerialPort;
|
|
$po = new Device::SerialPort ($dev);
|
|
}
|
|
if(!$po) {
|
|
my $msg = "Can't open $dev: $!";
|
|
Log(3, $msg) if($hash->{MOBILE});
|
|
return $msg if(!$hash->{MOBILE});
|
|
$readyfnlist{"$name.$dev"} = $hash;
|
|
return "";
|
|
}
|
|
Log 3, "CUL opened CUL device $dev";
|
|
|
|
$hash->{PortObj} = $po;
|
|
if( $^O !~ /Win/ ) {
|
|
$hash->{FD} = $po->FILENO;
|
|
$selectlist{"$name.$dev"} = $hash;
|
|
} else {
|
|
$readyfnlist{"$name.$dev"} = $hash;
|
|
}
|
|
|
|
my $ret = CUL_DoInit($hash);
|
|
if($ret) {
|
|
delete($selectlist{"$name.$dev"});
|
|
delete($readyfnlist{"$name.$dev"});
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_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)
|
|
{
|
|
my $lev = ($reread_active ? 4 : 2);
|
|
Log GetLogLevel($name,$lev), "deleting port for $d";
|
|
delete $defs{$d}{IODev};
|
|
}
|
|
}
|
|
$hash->{PortObj}->close() if($hash->{PortObj});
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Set($@)
|
|
{
|
|
my ($hash, @a) = @_;
|
|
|
|
return "\"set CUL\" needs at least one parameter" if(@a < 2);
|
|
return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
|
|
if(!defined($sets{$a[1]}));
|
|
|
|
my $name = shift @a;
|
|
my $type = shift @a;
|
|
my $arg = join("", @a);
|
|
|
|
if($type eq "freq") { # MHz
|
|
|
|
my $f = $arg/26*65536;
|
|
|
|
my $f2 = sprintf("%02x", $f / 65536);
|
|
my $f1 = sprintf("%02x", int($f % 65536) / 256);
|
|
my $f0 = sprintf("%02x", $f % 256);
|
|
$arg = sprintf("%.3f", (hex($f2)*65536+hex($f1)*256+hex($f0))/65536*26);
|
|
my $msg = "Setting FREQ2..0 (0D,0E,0F) to $f2 $f1 $f0 = $arg MHz";
|
|
Log GetLogLevel($name,4), $msg;
|
|
CUL_SimpleWrite($hash, "W0F$f2");
|
|
CUL_SimpleWrite($hash, "W10$f1");
|
|
CUL_SimpleWrite($hash, "W11$f0");
|
|
CUL_SimpleWrite($hash, $initstr); # Will reprogram the CC1101
|
|
return $msg;
|
|
|
|
} elsif($type eq "bWidth") { # KHz
|
|
|
|
my $ob = 5;
|
|
if(!IsDummy($hash->{NAME})) {
|
|
CUL_SimpleWrite($hash, "C10");
|
|
$ob = CUL_ReadAnswer($hash, $type);
|
|
return "Can't get old MDMCFG4 value" if($ob !~ m,/ (.*)\r,);
|
|
$ob = $1 & 0x0f;
|
|
}
|
|
|
|
my ($bits, $bw) = (0,0);
|
|
for (my $e = 0; $e < 4; $e++) {
|
|
for (my $m = 0; $m < 4; $m++) {
|
|
$bits = ($e<<6)+($m<<4);
|
|
$bw = int(26000/(8 * (4+$m) * (1 << $e))); # KHz
|
|
goto GOTBW if($arg >= $bw);
|
|
}
|
|
}
|
|
|
|
GOTBW:
|
|
$ob = sprintf("%02x", $ob+$bits);
|
|
my $msg = "Setting MDMCFG4 (10) to $ob = $bw KHz";
|
|
|
|
Log GetLogLevel($name,4), $msg;
|
|
CUL_SimpleWrite($hash, "W12$ob");
|
|
CUL_SimpleWrite($hash, $initstr);
|
|
return $msg;
|
|
|
|
} elsif($type eq "rAmpl") { # dB
|
|
|
|
return "a numerical value between 24 and 42 is expected"
|
|
if($arg !~ m/^\d+$/ || $arg < 24 || $arg > 42);
|
|
my ($v, $w);
|
|
for($v = 0; $v < @ampllist; $v++) {
|
|
last if($ampllist[$v] > $arg);
|
|
}
|
|
$v = sprintf("%02d", $v-1);
|
|
$w = $ampllist[$v];
|
|
my $msg = "Setting AGCCTRL2 (1B) to $v / $w dB";
|
|
CUL_SimpleWrite($hash, "W1D$v");
|
|
CUL_SimpleWrite($hash, $initstr);
|
|
return $msg;
|
|
|
|
} elsif($type eq "sens") { # dB
|
|
|
|
return "a numerical value between 4 and 16 is expected"
|
|
if($arg !~ m/^\d+$/ || $arg < 4 || $arg > 16);
|
|
my $w = int($arg/4)*4;
|
|
my $v = sprintf("9%d",$arg/4-1);
|
|
my $msg = "Setting AGCCTRL0 (1D) to $v / $w dB";
|
|
CUL_SimpleWrite($hash, "W1F$v");
|
|
CUL_SimpleWrite($hash, $initstr);
|
|
return $msg;
|
|
|
|
|
|
} else {
|
|
|
|
return "Expecting a 0-padded hex number"
|
|
if((length($arg)&1) == 0 && $type ne "raw");
|
|
$initstr = "X$arg" if($type eq "verbose");
|
|
Log GetLogLevel($name,4), "set $name $type $arg";
|
|
CUL_SimpleWrite($hash, $sets{$type} . $arg);
|
|
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Get($@)
|
|
{
|
|
my ($hash, @a) = @_;
|
|
|
|
return "\"get CUL\" needs at least one parameter" if(@a < 2);
|
|
return "Unknown argument $a[1], choose one of " . join(" ", sort keys %gets)
|
|
if(!defined($gets{$a[1]}));
|
|
|
|
my $arg = ($a[2] ? $a[2] : "");
|
|
my $msg = "";
|
|
|
|
return "No $a[1] for dummies" if(IsDummy($hash->{NAME}));
|
|
|
|
if($a[1] eq "ccconf") {
|
|
|
|
my %r = ( "0D"=>1,"0E"=>1,"0F"=>1,"10"=>1,"1B"=>1,"1D"=>1 );
|
|
foreach my $a (sort keys %r) {
|
|
CUL_SimpleWrite($hash, "C$a");
|
|
my @answ = split(" ", CUL_ReadAnswer($hash, "C$a"));
|
|
$r{$a} = $answ[4];
|
|
}
|
|
$msg = sprintf("freq:%.3fMHz bWidth:%dKHz rAmpl:%ddB sens:%ddB",
|
|
26*(($r{"0D"}*256+$r{"0E"})*256+$r{"0F"})/65536, #Freq
|
|
26000/(8 * (4+(($r{"10"}>>4)&3)) * (1 << (($r{"10"}>>6)&3))), #Bw
|
|
$ampllist[$r{"1B"}],
|
|
4+4*($r{"1D"}&3) #Sens
|
|
);
|
|
|
|
} else {
|
|
|
|
CUL_SimpleWrite($hash, $gets{$a[1]} . $arg) if(!IsDummy($hash->{NAME}));
|
|
$msg = CUL_ReadAnswer($hash, $a[1]);
|
|
$msg = "No answer" if(!defined($msg));
|
|
$msg =~ s/[\r\n]//g;
|
|
|
|
}
|
|
|
|
$hash->{READINGS}{$a[1]}{VAL} = $msg;
|
|
$hash->{READINGS}{$a[1]}{TIME} = TimeNow();
|
|
|
|
return "$a[0] $a[1] => $msg";
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_SetState($$$$)
|
|
{
|
|
my ($hash, $tim, $vt, $val) = @_;
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_DoInit($)
|
|
{
|
|
my $hash = shift;
|
|
my $name = $hash->{NAME};
|
|
|
|
# Clear the pipe
|
|
$hash->{RA_Timeout} = 0.1;
|
|
for(;;) {
|
|
last if(CUL_ReadAnswer($hash, "Clear") =~ m/^Timeout/);
|
|
}
|
|
delete($hash->{RA_Timeout});
|
|
|
|
my ($ver, $try) = ("", 0);
|
|
while($try++ < 3 && $ver !~ m/^V/) {
|
|
$hash->{PortObj}->write("V\n");
|
|
$ver = CUL_ReadAnswer($hash, "Version");
|
|
}
|
|
|
|
if($ver !~ m/^V/) {
|
|
$attr{$name}{dummy} = 1;
|
|
$hash->{PortObj}->close();
|
|
my $msg = "Not an CUL device, receives for V: $ver";
|
|
Log 1, $msg;
|
|
return $msg;
|
|
}
|
|
|
|
CUL_SimpleWrite($hash, $initstr);
|
|
$hash->{STATE} = "Initialized";
|
|
|
|
# Reset the counter
|
|
delete($hash->{XMIT_TIME});
|
|
delete($hash->{NR_CMD_LAST_H});
|
|
return undef;
|
|
}
|
|
|
|
#####################################
|
|
# This is a direct read for commands like get
|
|
sub
|
|
CUL_ReadAnswer($$)
|
|
{
|
|
my ($hash,$arg) = @_;
|
|
|
|
return undef if(!$hash || !defined($hash->{FD}));
|
|
my ($mculdata, $rin) = ("", '');
|
|
my $nfound;
|
|
for(;;) {
|
|
if($^O eq 'MSWin32') {
|
|
$nfound=CUL_Ready($hash);
|
|
} else {
|
|
vec($rin, $hash->{FD}, 1) = 1;
|
|
my $to = 3; # 3 seconds timeout
|
|
$to = $hash->{RA_Timeout} if($hash->{RA_Timeout}); # ...or less
|
|
$nfound = select($rin, undef, undef, $to);
|
|
if($nfound < 0) {
|
|
next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
|
|
return "Select error $nfound / $!";
|
|
}
|
|
}
|
|
return "Timeout reading answer for get $arg" if($nfound == 0);
|
|
my $buf = $hash->{PortObj}->input();
|
|
|
|
Log 5, "CUL/RAW: $buf";
|
|
$mculdata .= $buf;
|
|
return $mculdata if($mculdata =~ m/\r\n/);
|
|
}
|
|
}
|
|
|
|
#####################################
|
|
# Check if the 1% limit is reached and trigger notifies
|
|
sub
|
|
CUL_XmitLimitCheck($$)
|
|
{
|
|
my ($hash,$fn) = @_;
|
|
my $now = time();
|
|
|
|
if(!$hash->{XMIT_TIME}) {
|
|
$hash->{XMIT_TIME}[0] = $now;
|
|
$hash->{NR_CMD_LAST_H} = 1;
|
|
return;
|
|
}
|
|
|
|
my $nowM1h = $now-3600;
|
|
my @b = grep { $_ > $nowM1h } @{$hash->{XMIT_TIME}};
|
|
|
|
if(@b > 163) { # Maximum nr of transmissions per hour (unconfirmed).
|
|
|
|
my $name = $hash->{NAME};
|
|
Log GetLogLevel($name,2), "CUL TRANSMIT LIMIT EXCEEDED";
|
|
DoTrigger($name, "TRANSMIT LIMIT EXCEEDED");
|
|
|
|
} else {
|
|
|
|
push(@b, $now);
|
|
|
|
}
|
|
$hash->{XMIT_TIME} = \@b;
|
|
$hash->{NR_CMD_LAST_H} = int(@b);
|
|
}
|
|
|
|
sub
|
|
CUL_SimpleWrite($$)
|
|
{
|
|
my ($hash, $msg) = @_;
|
|
return if(!$hash || !defined($hash->{PortObj}));
|
|
$hash->{PortObj}->write($msg . "\n");
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Write($$$)
|
|
{
|
|
my ($hash,$fn,$msg) = @_;
|
|
|
|
if(!$hash || !defined($hash->{PortObj})) {
|
|
Log 5, "CUL device is not active, cannot send";
|
|
return;
|
|
}
|
|
my $name = $hash->{NAME};
|
|
|
|
###################
|
|
# Rewrite message from FHZ -> CUL
|
|
if(length($fn) <= 1) { # CUL Native
|
|
} elsif($fn eq "04" && substr($msg,0,6) eq "010101") { # FS20
|
|
$fn = "F";
|
|
$msg = substr($msg,6);
|
|
} else {
|
|
Log GetLogLevel($name,2), "CUL cannot translate $fn $msg";
|
|
return;
|
|
}
|
|
|
|
Log 5, "CUL sending $fn$msg";
|
|
my $bstring = "$fn$msg\n";
|
|
|
|
if($fn eq "F") {
|
|
|
|
if(!$hash->{QUEUE}) {
|
|
|
|
CUL_XmitLimitCheck($hash,$bstring);
|
|
$hash->{QUEUE} = [ $bstring ];
|
|
$hash->{PortObj}->write($bstring);
|
|
|
|
##############
|
|
# 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, "CUL_HandleWriteQueue", $hash, 1);
|
|
|
|
} else {
|
|
push(@{$hash->{QUEUE}}, $bstring);
|
|
}
|
|
|
|
} else {
|
|
|
|
$hash->{PortObj}->write($bstring);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_HandleWriteQueue($)
|
|
{
|
|
my $hash = shift;
|
|
my $arr = $hash->{QUEUE};
|
|
|
|
if(defined($arr) && @{$arr} > 0) {
|
|
shift(@{$arr});
|
|
if(@{$arr} == 0) {
|
|
delete($hash->{QUEUE});
|
|
return;
|
|
}
|
|
my $bstring = $arr->[0];
|
|
CUL_XmitLimitCheck($hash,$bstring);
|
|
$hash->{PortObj}->write($bstring);
|
|
InternalTimer(gettimeofday()+0.25, "CUL_HandleWriteQueue", $hash, 1);
|
|
}
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Read($)
|
|
{
|
|
my ($hash) = @_;
|
|
|
|
my $buf = $hash->{PortObj}->input();
|
|
my $name = $hash->{NAME};
|
|
|
|
###########
|
|
# Lets' try again: Some drivers return len(0) on the first read...
|
|
if(defined($buf) && length($buf) == 0) {
|
|
$buf = $hash->{PortObj}->input();
|
|
}
|
|
|
|
if(!defined($buf) || length($buf) == 0) {
|
|
|
|
my $dev = $hash->{DeviceName};
|
|
Log 1, "USB device $dev disconnected, waiting to reappear";
|
|
$hash->{PortObj}->close();
|
|
|
|
if($hash->{MOBILE}) {
|
|
|
|
delete($hash->{PortObj});
|
|
delete($selectlist{"$name.$dev"});
|
|
$readyfnlist{"$name.$dev"} = $hash; # Start polling
|
|
$hash->{STATE} = "disconnected";
|
|
|
|
# Without the following sleep the open of the device causes a SIGSEGV,
|
|
# and following opens block infinitely. Only a reboot helps.
|
|
sleep(5);
|
|
|
|
return "";
|
|
|
|
} else {
|
|
|
|
for(;;) {
|
|
sleep(5);
|
|
if ($^O eq 'MSWin32') {
|
|
$hash->{PortObj} = new Win32::SerialPort($dev);
|
|
}else{
|
|
$hash->{PortObj} = new Device::SerialPort($dev);
|
|
}
|
|
|
|
if($hash->{PortObj}) {
|
|
Log 1, "USB device $dev reappeared";
|
|
$hash->{FD} = $hash->{PortObj}->FILENO if !($^O eq 'MSWin32');
|
|
CUL_DoInit($hash);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
my $culdata = $hash->{PARTIAL};
|
|
Log 5, "CUL/RAW: $culdata/$buf";
|
|
$culdata .= $buf;
|
|
|
|
while($culdata =~ m/\n/) {
|
|
|
|
my $dmsg;
|
|
($dmsg,$culdata) = split("\n", $culdata);
|
|
$dmsg =~ s/\r//;
|
|
goto NEXTMSG if($dmsg eq "");
|
|
|
|
# Debug message, X05
|
|
if($dmsg =~ m/p /) {
|
|
foreach my $m (split("p ", $dmsg)) {
|
|
Log GetLogLevel($name,4), "CUL: p $m";
|
|
}
|
|
goto NEXTMSG;
|
|
}
|
|
|
|
my $rssi;
|
|
if($initstr =~ m/X2/ && $dmsg =~ m/[FEHTK]([A-F0-9][A-F0-9])+$/) { # RSSI
|
|
my $l = length($dmsg);
|
|
$rssi = hex(substr($dmsg, $l-2, 2));
|
|
$dmsg = substr($dmsg, 0, $l-2);
|
|
$rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74));
|
|
Log GetLogLevel($name,4), "$name: $dmsg $rssi";
|
|
} else {
|
|
Log GetLogLevel($name,4), "$name: $dmsg";
|
|
}
|
|
|
|
###########################################
|
|
#Translate Message from CUL to FHZ
|
|
next if(!$dmsg || length($dmsg) < 1); # Bogus messages
|
|
my $fn = substr($dmsg,0,1);
|
|
my $len = length($dmsg);
|
|
|
|
if($fn eq "F") { # Reformat for 10_FS20.pm
|
|
|
|
if(defined($attr{$name}) && defined($attr{$name}{CUR_id_list})) {
|
|
my $id= substr($dmsg,1,4);
|
|
if($attr{$name}{CUR_id_list} =~ m/$id/) { # CUR Request
|
|
CUL_HandleCurRequest($hash,$dmsg);
|
|
goto NEXTMSG;
|
|
}
|
|
}
|
|
|
|
$dmsg = sprintf("81%02x04xx0101a001%s00%s",
|
|
$len/2+5, substr($dmsg,1,6), substr($dmsg,7));
|
|
$dmsg = lc($dmsg);
|
|
|
|
} elsif($fn eq "T") { # Reformat for 11_FHT.pm
|
|
|
|
$dmsg = sprintf("81%02x04xx0909a001%s00%s",
|
|
$len/2+5, substr($dmsg,1,6), substr($dmsg,7));
|
|
$dmsg = lc($dmsg);
|
|
|
|
} elsif($fn eq "K") {
|
|
|
|
if($len == 99) { # Reformat for 13_KS300.pm
|
|
my @a = split("", $dmsg);
|
|
$dmsg = sprintf("81%02x04xx4027a001", $len/2+6);
|
|
for(my $i = 0; $i < 14; $i+=2) { # Swap nibbles.
|
|
$dmsg .= $a[$i+2] . $a[$i+1];
|
|
}
|
|
}
|
|
# Other K... Messages ar sent to CUL_WS
|
|
|
|
} elsif($fn eq "E") { # CUL_EM / Native
|
|
;
|
|
} else {
|
|
#Log GetLogLevel($name,4), "CUL: unknown message $dmsg";
|
|
goto NEXTMSG;
|
|
}
|
|
|
|
my @found = Dispatch($hash, $dmsg);
|
|
if($rssi) {
|
|
foreach my $d (@found) {
|
|
next if(!$defs{$d});
|
|
$defs{$d}{RSSI} = $rssi;
|
|
}
|
|
}
|
|
|
|
NEXTMSG:
|
|
}
|
|
$hash->{PARTIAL} = $culdata;
|
|
}
|
|
|
|
#####################################
|
|
sub
|
|
CUL_Ready($) # Windows - only
|
|
{
|
|
my ($hash) = @_;
|
|
my $po=$hash->{PortObj};
|
|
|
|
if(!$po) { # Looking for the device
|
|
|
|
my $dev = $hash->{DeviceName};
|
|
my $name = $hash->{NAME};
|
|
|
|
$hash->{PARTIAL} = "";
|
|
if ($^O=~/Win/) {
|
|
$po = new Win32::SerialPort ($dev);
|
|
} else {
|
|
$po = new Device::SerialPort ($dev);
|
|
}
|
|
return undef if(!$po);
|
|
|
|
Log 1, "USB device $dev reappeared";
|
|
$hash->{PortObj} = $po;
|
|
if( $^O !~ /Win/ ) {
|
|
$hash->{FD} = $po->FILENO;
|
|
delete($readyfnlist{"$name.$dev"});
|
|
$selectlist{"$name.$dev"} = $hash;
|
|
} else {
|
|
$readyfnlist{"$name.$dev"} = $hash;
|
|
}
|
|
my $ret = CUL_DoInit($hash);
|
|
if($ret) {
|
|
delete($selectlist{"$name.$dev"});
|
|
delete($readyfnlist{"$name.$dev"});
|
|
}
|
|
return $ret;
|
|
|
|
}
|
|
|
|
# This is relevant for windows only
|
|
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags)=$po->status;
|
|
return ($InBytes>0);
|
|
}
|
|
|
|
sub
|
|
CUL_SendCurMsg($$$)
|
|
{
|
|
my ($hash,$id,$msg) = @_;
|
|
|
|
$msg = substr($msg, 0, 12) if(length($msg) > 12);
|
|
my $rmsg = "F" . $id . unpack('H*', $msg);
|
|
Log 1, "CUL_SendCurMsg: $id:$msg / $rmsg";
|
|
sleep(1); # Poor mans CSMA/CD
|
|
CUL_SimpleWrite($hash, $rmsg);
|
|
}
|
|
|
|
sub
|
|
CUL_HandleCurRequest($$)
|
|
{
|
|
my ($hash,$msg) = @_;
|
|
|
|
|
|
Log 1, "CUR Request: $msg";
|
|
my $l = length($msg);
|
|
return if($l < 9);
|
|
|
|
my $id = substr($msg,1,4);
|
|
my $cm = substr($msg,5,2);
|
|
my $a1 = substr($msg,7,2);
|
|
my $a2 = pack('H*', substr($msg,9)) if($l > 9);
|
|
|
|
if($cm eq "00") { # Get status
|
|
$msg = defined($defs{$a2}) ? $defs{$a2}{STATE} : "Undefined $a2";
|
|
$msg =~ s/: /:/g;
|
|
$msg =~ s/ / /g;
|
|
$msg =~ s/.*[a-z]-//g; # FHT desired-temp, but keep T:-1
|
|
$msg =~ s/\(.*//g; # FHT (Celsius)
|
|
$msg =~ s/.*5MIN:/5MIN:/g; # EM
|
|
$msg =~ s/\.$//;
|
|
$msg =~ s/ *//; # One letter seldom makes sense
|
|
CUL_SendCurMsg($hash,$id, "d" . $msg); # Display the message on the CUR
|
|
}
|
|
|
|
if($cm eq "01") { # Send time
|
|
my @a = localtime;
|
|
$msg = sprintf("c%02d%02d%02d", $a[2],$a[1],$a[0]);
|
|
CUL_SendCurMsg($hash,$id, $msg);
|
|
}
|
|
|
|
if($cm eq "02") { # FHT desired temp
|
|
$msg = sprintf("set %s desired-temp %.1f", $a2, $a1/2);
|
|
fhem( $msg );
|
|
}
|
|
|
|
}
|
|
|
|
1;
|