Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ce95150df | ||
|
|
59c63c4f5d | ||
|
|
f6216d23d4 | ||
|
|
6ec7ca22a8 | ||
|
|
b422b35553 | ||
|
|
6b5345e8bb | ||
|
|
1264675b48 | ||
|
|
64de3f28fe | ||
|
|
7b2248799c | ||
|
|
edd01ab85d | ||
|
|
fb5b1eece5 | ||
|
|
906ef34d77 | ||
|
|
20b5a1faa2 | ||
|
|
d7b24fc124 | ||
|
|
6690605865 | ||
|
|
56304e1e32 | ||
|
|
8aefaa4fbf | ||
|
|
5064c6ccd9 | ||
|
|
c3a2c11402 | ||
|
|
3d185a8eb0 | ||
|
|
aca7655c96 | ||
|
|
55a54847c1 | ||
|
|
f6c3222f62 | ||
|
|
98cf85c6eb | ||
|
|
4331f0808d | ||
|
|
f3eeb3feba | ||
|
|
e3d5ab4337 | ||
|
|
5608b10481 | ||
|
|
d1bf6bf66f |
6
CHANGED
6
CHANGED
@@ -5,6 +5,12 @@
|
||||
- feature: SVG: filled area support, some ls/lw fixes
|
||||
- feature: WOL (wake on lan) module added (by Matthias)
|
||||
- feature: additional groups from /etc/groups are applied (Christopher)
|
||||
- feature: updatefhem backup is using tar+gzip now
|
||||
- feature: EIB: introduce Get, interpret received values upon defined model
|
||||
(by datapoint types) (Maz)
|
||||
- feature: NetIO230B module by Andy
|
||||
- feature: Retaining configfile comments (not within a define statement)
|
||||
- feature: EnOcean PM101 by Ignaz
|
||||
|
||||
- 2011-12-31 (5.2)
|
||||
- bugfix: applying smallscreen attributes to firefox/opera
|
||||
|
||||
@@ -50,6 +50,7 @@ TCM_Initialize($)
|
||||
|
||||
# Normal devices
|
||||
$hash->{DefFn} = "TCM_Define";
|
||||
$hash->{UndefFn} = "TCM_Undef";
|
||||
$hash->{GetFn} = "TCM_Get";
|
||||
$hash->{SetFn} = "TCM_Set";
|
||||
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 loglevel:0,1,2,3,4,5,6";
|
||||
@@ -80,6 +81,17 @@ TCM_Define($$)
|
||||
$hash->{DeviceName} = $dev;
|
||||
$hash->{MODEL} = $model;
|
||||
my $ret = DevIo_OpenDev($hash, 0, undef);
|
||||
|
||||
if($hash->{STATE} eq "opened") {
|
||||
# Read Base-Id of Enocean-Module
|
||||
my $cnt=0;
|
||||
do { # this does not always work, so we try several times
|
||||
my $answer=TCM_Get($hash, ($name, "baseid") );
|
||||
my @fields=split(/[=,]/, $answer);
|
||||
$hash->{BASEID}=$fields[1];
|
||||
$cnt++;
|
||||
} while ($cnt<3 and $hash->{BASEID} eq "");
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -610,7 +622,7 @@ TCM_ReadAnswer($$)
|
||||
|
||||
if(defined($buf)) {
|
||||
$data .= uc(unpack('H*', $buf));
|
||||
Log 5, "TCM/RAW (ReadAnswer): $data";
|
||||
Log $ll5, "TCM/RAW (ReadAnswer): $data";
|
||||
|
||||
if($hash->{MODEL} eq "120") {
|
||||
if(length($data) >= 28) {
|
||||
@@ -645,7 +657,26 @@ TCM_ReadAnswer($$)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub
|
||||
TCM_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};
|
||||
}
|
||||
}
|
||||
DevIo_CloseDev($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -162,7 +162,7 @@ TUL_Get($@)
|
||||
my $rsp;
|
||||
my $name = $a[0];
|
||||
|
||||
return "No $a[1] for dummies" if(IsDummy($name));
|
||||
#return "No $a[1] for dummies" if(IsDummy($name));
|
||||
|
||||
TUL_SimpleWrite($hash, "B".$gets{$a[1]}[0] . $arg);
|
||||
$rsp = TUL_SimpleRead($hash);
|
||||
|
||||
@@ -194,11 +194,16 @@ CUL_FHTTK_Parse($$)
|
||||
}
|
||||
}
|
||||
|
||||
if (! defined($defs{$self}{READINGS}{"Previous"})) {
|
||||
$defs{$self}{READINGS}{"Previous"}{VAL} = "";
|
||||
$defs{$self}{READINGS}{"Previous"}{TIME} = "";
|
||||
}
|
||||
|
||||
if (defined($defs{$self}{PREV}{STATE}) && $defs{$self}{PREV}{STATE} != $state) {
|
||||
my $prevState = $defs{$self}{PREV}{STATE};
|
||||
if ($prevState != $state) {
|
||||
my ($windowReading,$windowState) = split(/:/, $fhttfk_codes{$prevState});
|
||||
$defs{$self}{READINGS}{"PreviousWindow"}{VAL} = $windowState;
|
||||
$defs{$self}{READINGS}{"PreviousWindow"}{TIME} = $def->{PREVTIMESTAMP};
|
||||
$defs{$self}{READINGS}{"Previous"}{VAL} = $windowState if defined($windowState) && $windowState ne "";
|
||||
$defs{$self}{READINGS}{"Previous"}{TIME} = TimeNow();
|
||||
}
|
||||
|
||||
$def->{PREVTIMESTAMP} = defined($defs{$self}{PREV}{TIMESTAMP})?$defs{$self}{PREV}{TIMESTAMP}:time();
|
||||
|
||||
@@ -427,6 +427,41 @@ CUL_HM_Parse($$)
|
||||
}
|
||||
push @event, $msg if($msg);
|
||||
}
|
||||
# 0402000000000501090000
|
||||
} elsif($cmd eq "A410" && $p =~ m/^0402000000000(.)(..)(..)/) {
|
||||
my ($plist, $o1, $v1) =
|
||||
(hex($1),hex($2),hex($3));
|
||||
my $msg;
|
||||
my @days = ("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri");
|
||||
if($plist == 5) {
|
||||
$msg = sprintf("param-change: offset=%s, value=%s", $o1, $v1);
|
||||
if($o1 == 1) { ### bitfield containing multiple values...
|
||||
$msg = "displayMode:temperature only" if ($v1 & 1) == 0;
|
||||
$msg = "displayMode:temperature and humidity" if ($v1 & 1) == 1;
|
||||
push @event, $msg if $msg;
|
||||
$msg = "displayTemp:actual" if ($v1 & 2) == 0;
|
||||
$msg = "displayTemp:setpoint" if ($v1 & 2) == 2;
|
||||
push @event, $msg if $msg;
|
||||
$msg = "displayTempUnit:celsius" if ($v1 & 4) == 0;
|
||||
$msg = "displayTempUnit:fahrenheit" if ($v1 & 4) == 4;
|
||||
push @event, $msg if $msg;
|
||||
$msg = "controlMode:manual" if ($v1 & 0x18) == 0;
|
||||
$msg = "controlMode:auto" if ($v1 & 0x18) == 8;
|
||||
$msg = "controlMode:central" if ($v1 & 0x18) == 0x10;
|
||||
$msg = "controlMode:party" if ($v1 & 0x18) == 0x18;
|
||||
push @event, $msg if $msg;
|
||||
my $day = $days[($v1 & 0xE0) - 0xD9 + 1];
|
||||
$msg = sprintf("decalcDay:%s", $day);
|
||||
|
||||
# remember state for subsequent set operations
|
||||
$shash->{helper}{state251} = $v1;
|
||||
} elsif($o1 == 2) {
|
||||
$msg = "tempValveMode:Auto" if ($v1 & 0xC0) == 0;
|
||||
$msg = "tempValveMode:Closed" if ($v1 & 0xC0) == 0x40;
|
||||
$msg = "tempValveMode:Open" if ($v1 & 0xC0) == 0x80;
|
||||
}
|
||||
}
|
||||
push @event, $msg if $msg;
|
||||
}
|
||||
|
||||
if($cmd eq "A001" && $p =~ m/^01080900(..)(..)/) {
|
||||
@@ -444,6 +479,10 @@ CUL_HM_Parse($$)
|
||||
push @event, "desired-temp:" .hex($1)/2;
|
||||
}
|
||||
|
||||
if($cmd eq "8002" && $p =~ m/^0102(..)(....)/) { # Ack for fhem-command
|
||||
push @event, "desired-temp-ack:" .hex($1)/2;
|
||||
}
|
||||
|
||||
CUL_HM_SendCmd($shash, "++8002$id${src}00",1,0) # Send Ack
|
||||
if($id eq $dst && $cmd ne "8002");
|
||||
|
||||
@@ -662,8 +701,9 @@ CUL_HM_Parse($$)
|
||||
|
||||
if($p =~ m/^(....)(..)$/) {
|
||||
my ($t, $h) = ($1, $2);
|
||||
$t = hex($t)/10;
|
||||
$t -= 3276.8 if($t > 1638.4);
|
||||
$t = hex($t);
|
||||
$t -= 32768 if($t > 16384);
|
||||
$t = sprintf("%0.1f", $t/10);
|
||||
$h = hex($h);
|
||||
push @event, "state:T: $t H: $h";
|
||||
push @event, "temperature:$t";
|
||||
@@ -716,6 +756,7 @@ CUL_HM_Parse($$)
|
||||
}
|
||||
|
||||
#push @event, "unknownMsg:$p" if(!@event);
|
||||
push @event, "unknownMsg:($cmd) $p" if(!@event);
|
||||
|
||||
my @changed;
|
||||
for(my $i = 0; $i < int(@event); $i++) {
|
||||
@@ -759,6 +800,23 @@ CUL_HM_Parse($$)
|
||||
return $name;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
CUL_HM_TC_missing($)
|
||||
{
|
||||
#
|
||||
# find out missing configuration parameters
|
||||
#
|
||||
my ($hash) = @_ ;
|
||||
my $missingSettings = "please complete settings for ";
|
||||
$missingSettings .= "displayTemp " unless($hash->{READINGS}{displayTemp}{VAL});
|
||||
$missingSettings .= "displayTempUnit " unless($hash->{READINGS}{displayTempUnit}{VAL});
|
||||
$missingSettings .= "displayMode " unless($hash->{READINGS}{displayMode}{VAL});
|
||||
$missingSettings .= "controlMode " unless($hash->{READINGS}{controlMode}{VAL});
|
||||
$missingSettings .= "decalcDay " unless($hash->{READINGS}{decalcDay}{VAL});
|
||||
return $missingSettings;
|
||||
}
|
||||
|
||||
my %culHmGlobalSets = (
|
||||
raw => "data ...",
|
||||
reset => "",
|
||||
@@ -796,6 +854,11 @@ my %culHmModelSets = (
|
||||
"tempListThu"=> "HH:MM temp ...",
|
||||
"tempListWed"=> "HH:MM temp ...",
|
||||
"tempListFri"=> "HH:MM temp ...",
|
||||
"displayMode" => "[temp-only|temp-hum]",
|
||||
"displayTemp" => "[actual|setpoint]",
|
||||
"displayTempUnit" => "[celsius|fahrenheit]",
|
||||
"controlMode" => "[manual|auto|central|party]",
|
||||
"decalcDay" => "day",
|
||||
},
|
||||
);
|
||||
|
||||
@@ -828,6 +891,10 @@ CUL_HM_Set($@)
|
||||
|
||||
my $isSender = (AttrVal($name,"hmClass","") eq "sender" || $md eq "HM-CC-TC");
|
||||
|
||||
# HM-CC-TC control mode bits for day encoding
|
||||
my %tc_day2bits = ( "Sat"=>"0", "Sun"=>"0x20", "Mon"=>"0x40",
|
||||
"Tue"=>"0x60", "Wed"=>"0x80", "Thu"=>"0xA0",
|
||||
"Fri"=>"0xC0");
|
||||
|
||||
if(!defined($h) && defined($culHmSubTypeSets{$st}{pct}) && $cmd =~ m/^\d+/) {
|
||||
$cmd = "pct";
|
||||
@@ -938,6 +1005,198 @@ CUL_HM_Set($@)
|
||||
|
||||
CUL_HM_pushConfig($hash, $id, $dst, $bn, 1, $l1);
|
||||
|
||||
} elsif($cmd =~ m/^displayMode$/) { ###############################
|
||||
my $tcnf;
|
||||
if($hash->{helper}{state251}) {
|
||||
$tcnf = $hash->{helper}{state251};
|
||||
if($a[2] eq "temp-only") {
|
||||
$tcnf &= 0xFE;
|
||||
} else {
|
||||
$tcnf |= 0x1;
|
||||
}
|
||||
} else {
|
||||
# look if index 1 subfields are complete, construct state251,
|
||||
# if incomplete, issue errormessage, set reading and wait for
|
||||
# completion of state251
|
||||
if($hash->{READINGS}{displayTemp}{VAL} &&
|
||||
$hash->{READINGS}{displayTempUnit}{VAL} &&
|
||||
$hash->{READINGS}{controlMode}{VAL} &&
|
||||
$hash->{READINGS}{decalcDay}{VAL}) {
|
||||
|
||||
$tcnf = 0;
|
||||
$tcnf |= 1 if($a[2] ne "temp-only"); # the parameter actually to be changed
|
||||
$tcnf |= 2 if($hash->{READINGS}{displayTemp}{VAL} eq "setpoint");
|
||||
$tcnf |= 4 if($hash->{READINGS}{displayTempUnit}{VAL} eq "fahrenheit");
|
||||
$tcnf |= 8 if($hash->{READINGS}{controlMode}{VAL} eq "auto");
|
||||
$tcnf |= 0x10 if($hash->{READINGS}{controlMode}{VAL} eq "central");
|
||||
$tcnf |= 0x18 if($hash->{READINGS}{controlMode}{VAL} eq "party");
|
||||
my $dbit = $tc_day2bits{$hash->{READINGS}{decalcDay}{VAL}};
|
||||
$tcnf |= $dbit;
|
||||
} else {
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return CUL_HM_TC_missing($hash);
|
||||
}
|
||||
}
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "01$tcnf");
|
||||
$hash->{helper}{state251} = $tcnf;
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return;
|
||||
|
||||
} elsif($cmd =~ m/^displayTemp$/) { ###############################
|
||||
my $tcnf;
|
||||
if($hash->{helper}{state251}) {
|
||||
$tcnf = $hash->{helper}{state251};
|
||||
if($a[2] eq "setpoint") {
|
||||
$tcnf &= 0xFD;
|
||||
} else {
|
||||
$tcnf |= 0x2;
|
||||
}
|
||||
} else {
|
||||
# look if index 1 subfields are complete, construct state251,
|
||||
# if incomplete, issue errormessage, set reading and wait for
|
||||
# completion of state251
|
||||
if($hash->{READINGS}{displayMode}{VAL} &&
|
||||
$hash->{READINGS}{displayTempUnit}{VAL} &&
|
||||
$hash->{READINGS}{controlMode}{VAL} &&
|
||||
$hash->{READINGS}{decalcDay}{VAL}) {
|
||||
|
||||
$tcnf = 0;
|
||||
$tcnf |= 1 if($hash->{READINGS}{displayMode}{VAL} ne "temp-only");
|
||||
$tcnf |= 2 if($a[2] ne "setpoint"); # the parameter actually to be changed
|
||||
$tcnf |= 4 if($hash->{READINGS}{displayTempUnit}{VAL} eq "fahrenheit");
|
||||
$tcnf |= 8 if($hash->{READINGS}{controlMode}{VAL} eq "auto");
|
||||
$tcnf |= 0x10 if($hash->{READINGS}{controlMode}{VAL} eq "central");
|
||||
$tcnf |= 0x18 if($hash->{READINGS}{controlMode}{VAL} eq "party");
|
||||
my $dbit = $tc_day2bits{$hash->{READINGS}{decalcDay}{VAL}};
|
||||
$tcnf |= $dbit;
|
||||
} else {
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return CUL_HM_TC_missing($hash);
|
||||
}
|
||||
}
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "01$tcnf");
|
||||
$hash->{helper}{state251} = $tcnf;
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return;
|
||||
|
||||
} elsif($cmd =~ m/^displayTempUnit$/) { ###############################
|
||||
my $tcnf;
|
||||
if($hash->{helper}{state251}) {
|
||||
$tcnf = $hash->{helper}{state251};
|
||||
if($a[2] eq "celsius") {
|
||||
$tcnf &= 0xFD;
|
||||
} else {
|
||||
$tcnf |= 0xFB;
|
||||
}
|
||||
} else {
|
||||
# look if index 1 subfields are complete, construct state251,
|
||||
# if incomplete, issue errormessage, set reading and wait for
|
||||
# completion of state251
|
||||
if($hash->{READINGS}{displayTemp}{VAL} &&
|
||||
$hash->{READINGS}{displayMode}{VAL} &&
|
||||
$hash->{READINGS}{controlMode}{VAL} &&
|
||||
$hash->{READINGS}{decalcDay}{VAL}) {
|
||||
|
||||
$tcnf = 0;
|
||||
$tcnf |= 1 if($hash->{READINGS}{displayMode}{VAL} ne "temp-only");
|
||||
$tcnf |= 2 if($hash->{READINGS}{displayTemp}{VAL} eq "setpoint");
|
||||
$tcnf |= 4 if($a[2] ne "fahrenheit"); # the parameter actually to be changed
|
||||
$tcnf |= 8 if($hash->{READINGS}{controlMode}{VAL} eq "auto");
|
||||
$tcnf |= 0x10 if($hash->{READINGS}{controlMode}{VAL} eq "central");
|
||||
$tcnf |= 0x18 if($hash->{READINGS}{controlMode}{VAL} eq "party");
|
||||
my $dbit = $tc_day2bits{$hash->{READINGS}{decalcDay}{VAL}};
|
||||
$tcnf |= $dbit;
|
||||
} else {
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return CUL_HM_TC_missing($hash);
|
||||
}
|
||||
}
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "01$tcnf");
|
||||
$hash->{helper}{state251} = $tcnf;
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return;
|
||||
|
||||
} elsif($cmd =~ m/^controlMode$/) { ###############################
|
||||
my $tcnf;
|
||||
if($hash->{helper}{state251}) {
|
||||
$tcnf = $hash->{helper}{state251};
|
||||
$tcnf &= 0xE7; # blank out the control mode bits (equals mode manual)
|
||||
$tcnf |= 0x08 if($a[2] eq "auto");
|
||||
$tcnf |= 0x10 if($a[2] eq "central");
|
||||
$tcnf |= 0x18 if($a[2] eq "party");
|
||||
} else {
|
||||
# look if index 1 subfields are complete, construct state251,
|
||||
# if incomplete, issue errormessage, set reading and wait for
|
||||
# completion of state251
|
||||
if($hash->{READINGS}{displayTemp}{VAL} &&
|
||||
$hash->{READINGS}{displayMode}{VAL} &&
|
||||
$hash->{READINGS}{displayTempUnit}{VAL} &&
|
||||
$hash->{READINGS}{decalcDay}{VAL}) {
|
||||
|
||||
$tcnf = 0;
|
||||
$tcnf |= 1 if($hash->{READINGS}{displayMode}{VAL} ne "temp-only");
|
||||
$tcnf |= 2 if($hash->{READINGS}{displayTemp}{VAL} eq "setpoint");
|
||||
$tcnf |= 4 if($hash->{READINGS}{displayTempUnit}{VAL} eq "fahrenheit");
|
||||
$tcnf |= 8 if($a[2] eq "auto"); # the parameter actually to be changed
|
||||
$tcnf |= 0x10 if($a[2] eq "central");
|
||||
$tcnf |= 0x18 if($a[2] eq "party");
|
||||
my $dbit = $tc_day2bits{$hash->{READINGS}{decalcDay}{VAL}};
|
||||
$tcnf |= $dbit;
|
||||
} else {
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return CUL_HM_TC_missing($hash);
|
||||
}
|
||||
}
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "01$tcnf");
|
||||
$hash->{helper}{state251} = $tcnf;
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return;
|
||||
|
||||
} elsif($cmd =~ m/^decalcDay$/) { ###############################
|
||||
my $tcnf;
|
||||
my $dbit = $tc_day2bits{$a[2]};
|
||||
|
||||
if($hash->{helper}{state251}) {
|
||||
$tcnf = $hash->{helper}{state251};
|
||||
$tcnf &= 0x1F; # blank out the decalc day bits (equals Sat)
|
||||
$tcnf |= $dbit;
|
||||
} else {
|
||||
# look if index 1 subfields are complete, construct state251,
|
||||
# if incomplete, issue errormessage, set reading and wait for
|
||||
# completion of state251
|
||||
if($hash->{READINGS}{displayTemp}{VAL} &&
|
||||
$hash->{READINGS}{displayMode}{VAL} &&
|
||||
$hash->{READINGS}{displayTempUnit}{VAL} &&
|
||||
$hash->{READINGS}{controlMode}{VAL}) {
|
||||
|
||||
$tcnf = 0;
|
||||
$tcnf |= 1 if($hash->{READINGS}{displayMode}{VAL} ne "temp-only");
|
||||
$tcnf |= 2 if($hash->{READINGS}{displayTemp}{VAL} eq "setpoint");
|
||||
$tcnf |= 4 if($hash->{READINGS}{displayTempUnit}{VAL} eq "fahrenheit");
|
||||
$tcnf |= 8 if($hash->{READINGS}{controlMode}{VAL} eq "auto");
|
||||
$tcnf |= 0x10 if($hash->{READINGS}{controlMode}{VAL} eq "central");
|
||||
$tcnf |= 0x18 if($hash->{READINGS}{controlMode}{VAL} eq "party");
|
||||
$tcnf |= $dbit; # the parameter actually to be changed
|
||||
} else {
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return CUL_HM_TC_missing($hash);
|
||||
}
|
||||
}
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "01$tcnf");
|
||||
$hash->{helper}{state251} = $tcnf;
|
||||
$hash->{READINGS}{$cmd}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{$cmd}{VAL} = sprintf("%s", $a[2]);
|
||||
return;
|
||||
|
||||
} elsif($cmd =~ m/^desired-temp$/) { ##################
|
||||
my $temp = CUL_HM_convTemp($a[2]);
|
||||
return $temp if(length($temp) > 2);
|
||||
|
||||
186
FHEM/10_EIB.pm
186
FHEM/10_EIB.pm
@@ -27,12 +27,47 @@ my $eib_simple ="off on value on-for-timer on-till";
|
||||
my %models = (
|
||||
);
|
||||
|
||||
my %eib_dpttypes = (
|
||||
|
||||
# 1-Octet unsigned value (handled as dpt7)
|
||||
"dpt5" => {"CODE"=>"dpt7", "UNIT"=>""},
|
||||
"percent" => {"CODE"=>"dpt7", "UNIT"=>"%"},
|
||||
|
||||
# 2-Octet unsigned Value (current, length, brightness)
|
||||
"dpt7" => {"CODE"=>"dpt7", "UNIT"=>""},
|
||||
"length-mm" => {"CODE"=>"dpt7", "UNIT"=>"mm"},
|
||||
"current-mA" => {"CODE"=>"dpt7", "UNIT"=>"mA"},
|
||||
"brightness" => {"CODE"=>"dpt7", "UNIT"=>"lux"},
|
||||
"timeperiod-ms" => {"CODE"=>"dpt7", "UNIT"=>"ms"},
|
||||
"timeperiod-min" => {"CODE"=>"dpt7", "UNIT"=>"min"},
|
||||
"timeperiod-h" => {"CODE"=>"dpt7", "UNIT"=>"h"},
|
||||
|
||||
# 2-Octet unsigned Value (Temp / Light)
|
||||
"dpt9" => {"CODE"=>"dpt9", "UNIT"=>""},
|
||||
"tempsensor" => {"CODE"=>"dpt9", "UNIT"=>"<22>C"},
|
||||
"lightsensor" => {"CODE"=>"dpt9", "UNIT"=>"Lux"},
|
||||
|
||||
# Time of Day
|
||||
"dpt10" => {"CODE"=>"dpt10", "UNIT"=>""},
|
||||
"time" => {"CODE"=>"dpt10", "UNIT"=>""},
|
||||
|
||||
# Date
|
||||
"dpt11" => {"CODE"=>"dpt11", "UNIT"=>""},
|
||||
"date" => {"CODE"=>"dpt11", "UNIT"=>""},
|
||||
|
||||
# 4-Octet unsigned value (handled as dpt7)
|
||||
"dpt12" => {"CODE"=>"dpt7", "UNIT"=>""},
|
||||
|
||||
);
|
||||
|
||||
|
||||
sub
|
||||
EIB_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{Match} = "^B.*";
|
||||
$hash->{GetFn} = "EIB_Get";
|
||||
$hash->{SetFn} = "EIB_Set";
|
||||
$hash->{StateFn} = "EIB_SetState";
|
||||
$hash->{DefFn} = "EIB_Define";
|
||||
@@ -50,7 +85,7 @@ EIB_Define($$)
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
my $u = "wrong syntax: define <name> EIB <group name>";
|
||||
my $u = "wrong syntax: define <name> EIB <group name> [<read group names>*]";
|
||||
|
||||
return $u if(int(@a) < 3);
|
||||
return "Define $a[0]: wrong group name format: specify as 0-15/0-15/0-255 or as hex"
|
||||
@@ -65,8 +100,15 @@ EIB_Define($$)
|
||||
my $name = $a[0];
|
||||
|
||||
$hash->{CODE}{$ncode++} = $code;
|
||||
# add read group names
|
||||
if(int(@a)>3)
|
||||
{
|
||||
for (my $count = 3; $count < int(@a); $count++)
|
||||
{
|
||||
$hash->{CODE}{$ncode++} = eib_name2hex(lc($a[$count]));;
|
||||
}
|
||||
}
|
||||
$modules{EIB}{defptr}{$code}{$name} = $hash;
|
||||
|
||||
AssignIoPort($hash);
|
||||
}
|
||||
|
||||
@@ -94,12 +136,22 @@ sub
|
||||
EIB_SetState($$$$)
|
||||
{
|
||||
my ($hash, $tim, $vt, $val) = @_;
|
||||
Log(5,"EIB setState tim: $tim vt: $vt val: $val");
|
||||
|
||||
$val = $1 if($val =~ m/^(.*) \d+$/);
|
||||
return "Undefined value $val" if(!defined($eib_c2b{$val}));
|
||||
return undef;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
EIB_Get($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
IOWrite($hash, "B", "r" . $hash->{GROUP});
|
||||
return "Current value for $hash->{NAME} ($hash->{GROUP}) requested.";
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
EIB_Set($@)
|
||||
@@ -110,6 +162,7 @@ EIB_Set($@)
|
||||
|
||||
return "no set value specified" if($na < 2 || $na > 3);
|
||||
return "Readonly value $a[1]" if(defined($readonly{$a[1]}));
|
||||
return "No $a[1] for dummies" if(IsDummy($hash->{NAME}));
|
||||
|
||||
my $c = $eib_c2b{$a[1]};
|
||||
if(!defined($c)) {
|
||||
@@ -221,22 +274,149 @@ EIB_Parse($$)
|
||||
|
||||
$lh->{CHANGED}[0] = $v;
|
||||
$lh->{STATE} = $v;
|
||||
$lh->{RAWSTATE} = $v;
|
||||
$lh->{LASTGROUP} = $dev;
|
||||
$lh->{READINGS}{state}{TIME} = TimeNow();
|
||||
$lh->{READINGS}{state}{VAL} = $v;
|
||||
Log GetLogLevel($n,2), "EIB $n $v";
|
||||
|
||||
# parse/translate by datapoint type
|
||||
EIB_ParseByDatapointType($lh,$n,$v);
|
||||
|
||||
push(@list, $n);
|
||||
}
|
||||
return @list;
|
||||
} else {
|
||||
|
||||
# check if the code is within the read groups
|
||||
my $found = 0;
|
||||
foreach my $mod (keys %{$modules{EIB}{defptr}})
|
||||
{
|
||||
my $def = $modules{EIB}{defptr}{"$mod"};
|
||||
if($def) {
|
||||
my @list;
|
||||
foreach my $n (keys %{ $def }) {
|
||||
my $lh = $def->{$n};
|
||||
foreach my $c (keys %{ $lh->{CODE} } )
|
||||
{
|
||||
$c = $lh->{CODE}{$c};
|
||||
if($c eq $dev)
|
||||
{
|
||||
$n = $lh->{NAME}; # It may be renamed
|
||||
|
||||
return "" if(IsIgnored($n)); # Little strange.
|
||||
|
||||
$lh->{CHANGED}[0] = $v;
|
||||
$lh->{STATE} = $v;
|
||||
$lh->{RAWSTATE} = $v;
|
||||
$lh->{LASTGROUP} = $dev;
|
||||
$lh->{READINGS}{state}{TIME} = TimeNow();
|
||||
$lh->{READINGS}{state}{VAL} = $v;
|
||||
Log GetLogLevel($n,2), "EIB $n $v";
|
||||
|
||||
# parse/translate by datapoint type
|
||||
EIB_ParseByDatapointType($lh,$n,$v);
|
||||
|
||||
push(@list, $n);
|
||||
$found = 1;
|
||||
}
|
||||
}}
|
||||
return @list if $found>0;
|
||||
}
|
||||
}
|
||||
|
||||
if($found==0)
|
||||
{
|
||||
my $dev_name = eib_hex2name($dev);
|
||||
Log(3, "EIB Unknown device $dev ($dev_name), Value $val, please define it");
|
||||
return "UNDEFINED EIB_$dev EIB $dev";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub
|
||||
EIB_ParseByDatapointType($$$)
|
||||
{
|
||||
my ($hash, $name, $value) = @_;
|
||||
my $model = $attr{$name}{"model"};
|
||||
|
||||
# nothing to do if no model is given
|
||||
return undef if(!defined($model));
|
||||
|
||||
my $dpt = $eib_dpttypes{"$model"};
|
||||
|
||||
Log(4,"EIB parse $value for $name model: $model dpt: $dpt");
|
||||
return undef if(!defined($dpt));
|
||||
|
||||
my $code = $eib_dpttypes{"$model"}{"CODE"};
|
||||
my $unit = $eib_dpttypes{"$model"}{"UNIT"};
|
||||
my $transval = undef;
|
||||
|
||||
Log(4,"EIB parse $value for $name model: $model dpt: $code unit: $unit");
|
||||
|
||||
if ($code eq "dpt7")
|
||||
{
|
||||
my $fullval = hex($value);
|
||||
$transval = $fullval;
|
||||
|
||||
Log(5,"EIB $code parse $value = $fullval translated: $transval");
|
||||
|
||||
} elsif($code eq "dpt9")
|
||||
{
|
||||
my $fullval = hex($value);
|
||||
my $sign = 1;
|
||||
$sign = -1 if(($fullval & 0x8000)>0);
|
||||
my $exp = ($fullval & 0x7800)>>11;
|
||||
my $mant = ($fullval & 0x07FF);
|
||||
|
||||
$transval = ($sign * $mant * (2**$exp))/100;
|
||||
|
||||
Log(5,"EIB $code parse $value = $fullval sign: $sign mant: $mant exp: $exp translated: $transval");
|
||||
|
||||
|
||||
} elsif ($code eq "dpt10")
|
||||
{
|
||||
# Time
|
||||
my $fullval = hex($value);
|
||||
my $hours = ($fullval & 0x1F0000)>>16;
|
||||
my $mins = ($fullval & 0x3F00)>>8;
|
||||
my $secs = ($fullval & 0x3F);
|
||||
$transval = sprintf("%02d:%02d:%02d",$hours,$mins,$secs);
|
||||
|
||||
Log(5,"EIB $code parse $value = $fullval hours: $hours mins: $mins secs: $secs translated: $transval");
|
||||
|
||||
} elsif ($code eq "dpt11")
|
||||
{
|
||||
# Date
|
||||
my $fullval = hex($value);
|
||||
my $day = ($fullval & 0x1F0000)>>16;
|
||||
my $month = ($fullval & 0x0F00)>>8;
|
||||
my $year = ($fullval & 0x7F);
|
||||
#translate year (21st cent if <90 / else 20th century)
|
||||
$year += 1900 if($year>=90);
|
||||
$year += 2000 if($year<90);
|
||||
$transval = sprintf("%02d.%02d.%04d",$day,$month,$year);
|
||||
|
||||
Log(5,"EIB $code parse $value = $fullval day: $day month: $month year: $year translated: $transval");
|
||||
|
||||
} elsif ($code eq "dptxx") {
|
||||
|
||||
}
|
||||
|
||||
# set state to translated value
|
||||
if(defined($transval))
|
||||
{
|
||||
Log(4,"EIB $name translated to $transval");
|
||||
$hash->{STATE} = "$transval $unit";
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(4,"EIB $name model $model could not be translated.");
|
||||
}
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
eib_hex2name($)
|
||||
@@ -262,7 +442,7 @@ eib_name2hex($)
|
||||
$r = sprintf("%01x%01x%02x",$1,$2,$3);
|
||||
}
|
||||
elsif($v =~ /^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,3})$/) {
|
||||
$r = sprintf("%01x%021%02x",$1,$2,$3);
|
||||
$r = sprintf("%01x%01x%02x",$1,$2,$3);
|
||||
}
|
||||
|
||||
return $r;
|
||||
|
||||
@@ -10,6 +10,7 @@ sub EnOcean_Initialize($);
|
||||
sub EnOcean_Parse($$);
|
||||
sub EnOcean_Set($@);
|
||||
sub EnOcean_MD15Cmd($$$);
|
||||
sub EnOcean_GetMyDeviceId($);
|
||||
|
||||
my %rorgname = ("F6"=>"switch", # RPS
|
||||
"D5"=>"contact", # 1BS
|
||||
@@ -64,10 +65,11 @@ EnOcean_Initialize($)
|
||||
$hash->{DefFn} = "EnOcean_Define";
|
||||
$hash->{ParseFn} = "EnOcean_Parse";
|
||||
$hash->{SetFn} = "EnOcean_Set";
|
||||
$hash->{AttrFn} = "EnOcean_Attr";
|
||||
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 " .
|
||||
"showtime:1,0 loglevel:0,1,2,3,4,5,6 model " .
|
||||
"subType:switch,contact,sensor,windowHandle,SR04,MD15," .
|
||||
"dimmer,dimmCtrl actualTemp";
|
||||
"eltakoDimmer,eltakoRoll subId actualTemp, dimTime";
|
||||
|
||||
for(my $i=0; $i<@ptm200btn;$i++) {
|
||||
$ptm200btn{$ptm200btn[$i]} = "$i:30";
|
||||
@@ -76,6 +78,27 @@ EnOcean_Initialize($)
|
||||
return undef;
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
|
||||
sub EnOcean_Attr($)
|
||||
{
|
||||
my @a = @_;
|
||||
|
||||
my $name= $a[1];
|
||||
my $ll2 = GetLogLevel($name, 2);
|
||||
|
||||
if($a[2] eq "subType") {
|
||||
Log $ll2, "EO_Attr subtype";
|
||||
|
||||
if($a[3] eq "eltakoDimmer") {
|
||||
Log $ll2, "EO_Attr subtype dimmer";
|
||||
$attr{$name}{eventMap}="B0:on B1:off"
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
EnOcean_Define($$)
|
||||
@@ -140,30 +163,80 @@ EnOcean_Set($@)
|
||||
$hash->{READINGS}{$cmd}{TIME} = $tn;
|
||||
$hash->{READINGS}{$cmd}{VAL} = $arg;
|
||||
|
||||
} elsif($st eq "dimmCtrl") { # Tested for Eltako-Dimmer
|
||||
} elsif($st eq "eltakoDimmer") {
|
||||
my $sendDimCmd=0;
|
||||
my $time=AttrVal($name, "time", 0);
|
||||
my $onoff=1;
|
||||
my $dimVal=100;
|
||||
$dimVal=$hash->{VALUE} if(defined $hash->{VALUE});
|
||||
|
||||
if($cmd eq "teach") {
|
||||
my $data=sprintf("A502000000%s00", $hash->{DEF});
|
||||
Log $ll2, "dimmCtrl.Teach: " . $data;
|
||||
my $idSrc=EnOcean_GetMyDeviceId($hash);
|
||||
my $data=sprintf("A502000000%s00", $idSrc);
|
||||
Log $ll2, "$st.Teach: " . $data;
|
||||
IOWrite($hash, "000A0001", $data); # len:000a optlen:00 pakettype:1(radio)
|
||||
|
||||
} elsif($cmd eq "dimm") {
|
||||
return "Usage: dimm percent [time 01-FF FF:slowest] [on/off]" if(@a<2);
|
||||
my $time=0;
|
||||
my $onoff=1;
|
||||
} elsif($cmd eq "dimto") {
|
||||
return "Usage: $cmd percent" if(@a<2 or $a[1]>100);
|
||||
# for eltako relative (0-100) (but not compliant to EEP because DB0.2 is 0)
|
||||
my $dimVal=$a[1];
|
||||
$dimVal=$a[1];
|
||||
shift(@a);
|
||||
if(defined($a[1])) { $time=$a[1]; shift(@a); }
|
||||
if(defined($a[1])) { $onoff=($a[1] eq "off") ? 0 : 1; shift(@a); }
|
||||
# EEP: A5/38/08 Central Command ->Typ 0x02: Dimming
|
||||
my $data=sprintf("A502%02X%02X%02X%s00", $dimVal, $time, $onoff|0x08, $hash->{DEF});
|
||||
IOWrite($hash, "000A0001", $data);
|
||||
Log $ll2, "dimmCtrl.dimm: " . $data;
|
||||
$sendDimCmd=1;
|
||||
|
||||
} elsif($cmd eq "dimup") {
|
||||
return "Usage: $cmd percent" if(@a<2 or $a[1]>100);
|
||||
$dimVal+=$a[1];
|
||||
Log $ll2, "$st.$cmd val:" . $hash->{VALUE} . " par:" . $a[1] . " val:" . $dimVal;
|
||||
shift(@a);
|
||||
$sendDimCmd=1;
|
||||
|
||||
} elsif($cmd eq "dimdown") {
|
||||
return "Usage: $cmd percent" if(@a<2 or $a[1]>100);
|
||||
$dimVal-=$a[1];
|
||||
shift(@a);
|
||||
$sendDimCmd=1;
|
||||
|
||||
} elsif($cmd eq "on" or $cmd eq "B0") {
|
||||
$sendDimCmd=1;
|
||||
|
||||
} elsif($cmd eq "off" or $cmd eq "B1") {
|
||||
$onoff=0;
|
||||
$sendDimCmd=1;
|
||||
|
||||
} else {
|
||||
return "Unknown argument $cmd, choose one of: teach, dimm"
|
||||
return "Unknown argument $cmd, choose one of: teach, dimto, dimup, dimdown, on ,off";
|
||||
|
||||
}
|
||||
if($sendDimCmd) {
|
||||
if($dimVal > 100) { $dimVal=100; }
|
||||
if($dimVal <= 0) { $dimVal=0; $onoff=0; }
|
||||
# EEP: A5/38/08 Central Command ->Typ 0x02: Dimming
|
||||
my $idSrc=EnOcean_GetMyDeviceId($hash);
|
||||
my $data=sprintf("A502%02X%02X%02X%s00", $dimVal, $time, $onoff|0x08, $idSrc);
|
||||
IOWrite($hash, "000A0001", $data);
|
||||
Log $ll2, "$st.$cmd: " . $data;
|
||||
}
|
||||
|
||||
###########################
|
||||
} elsif($st eq "eltakoRoll") {
|
||||
if($cmd eq "teach") {
|
||||
my $data=sprintf("A5FFF80D80%s00", $hash->{DEF});
|
||||
Log $ll2, "eltakoRollCtrl.Teach: " . $data;
|
||||
IOWrite($hash, "000A0001", $data); # len:000a optlen:00 pakettype:1(radio)
|
||||
} else {
|
||||
my %eltakoRollCtrlCommands = ( down=>0x02, up=>0x01, stop=>0x00 );
|
||||
my $usage = "Usage: (" . join("|", sort keys %eltakoRollCtrlCommands) . ")";
|
||||
my $rollcmd= $eltakoRollCtrlCommands{$cmd};
|
||||
return $usage if(!defined($rollcmd));
|
||||
my $time=AttrVal($name, "time", 0);
|
||||
# EEP: A5/3F/7F Universal ???
|
||||
my $idSrc=EnOcean_GetMyDeviceId($hash);
|
||||
my $data=sprintf("A5%02X%02X%02X%02X%s00", 0, $time, $rollcmd, 0x08, $idSrc);
|
||||
IOWrite($hash, "000A0001", $data);
|
||||
Log $ll2, "eltakoRoll.$cmd" . $data;
|
||||
}
|
||||
|
||||
###########################
|
||||
} else { # Simulate a PTM
|
||||
@@ -271,6 +344,8 @@ EnOcean_Parse($$)
|
||||
}
|
||||
|
||||
}
|
||||
# eltakoRoll: BI: unten / B0: oben / released:running/stopped
|
||||
|
||||
|
||||
# released events are disturbing when using a remote, since it overwrites
|
||||
# the "real" state immediately
|
||||
@@ -303,7 +378,7 @@ EnOcean_Parse($$)
|
||||
|
||||
if("$fn.$tp" eq "20.01" && $iohash->{pair}) { # MD15
|
||||
select(undef, undef, undef, 0.1); # max 10 Seconds
|
||||
EnOcean_A5Cmd($hash, "800800F0", "00000000");
|
||||
EnOcean_A5Cmd($hash, "800800F0", EnOcean_GetMyDeviceId($hash));
|
||||
select(undef, undef, undef, 0.5);
|
||||
EnOcean_MD15Cmd($hash, $name, 128); # 128 == 20 degree C
|
||||
}
|
||||
@@ -344,10 +419,18 @@ EnOcean_Parse($$)
|
||||
push @event, "3:measured-temp:". sprintf "%.1f", ($db_1*40/255);
|
||||
EnOcean_MD15Cmd($hash, $name, $db_1);
|
||||
|
||||
} elsif($st eq "PM101") {
|
||||
####################################
|
||||
# Ratio Presence Sensor Eagle PM101, code by aicgazi
|
||||
####################################
|
||||
my $lux = sprintf "%3d", $db_2;
|
||||
# content of $db_2 is the illuminance where max value 0xFF stands for 1000 lx
|
||||
$lux = sprintf "%04.2f", ( $lux * 1000 / 255 ) ;
|
||||
push @event, "3:brightness:$lux";
|
||||
push @event, "3:channel1:" . ($db_0 & 0x01 ? "off" : "on");
|
||||
push @event, "3:channel2:" . ($db_0 & 0x02 ? "off" : "on");
|
||||
|
||||
} elsif($st eq "dimmer") {
|
||||
# todo: create a more general solution for the central-command responses
|
||||
|
||||
} elsif($st eq "eltakoDimmer") {
|
||||
# response command from (Eltako-)Actor ( Central-Command:A5/38/08 )
|
||||
if($db_3 eq 0x01) { # switch
|
||||
push @event, "3:state:" . (($db_0 & 0x01) ? "on": "off");
|
||||
@@ -356,7 +439,7 @@ EnOcean_Parse($$)
|
||||
|
||||
} elsif($db_3 eq 0x02) { # dimm
|
||||
push @event, "3:state:" . (($db_0 & 0x01) ? "on": "off");
|
||||
push @event, "3:dimmValue:$db_2";
|
||||
push @event, "3:value:$db_2";
|
||||
|
||||
} elsif($db_3 eq 0x03) { # setpoint-switch, todo
|
||||
} elsif($db_3 eq 0x04) { # basic setpoint, todo
|
||||
@@ -390,10 +473,11 @@ EnOcean_Parse($$)
|
||||
if($vn eq "state") {
|
||||
$hash->{STATE} = $vv;
|
||||
push @changed, $vv;
|
||||
|
||||
} elsif($vn eq "value") {
|
||||
$hash->{VALUE} = $vv if($vv>0);
|
||||
push @changed, "$vn: $vv";
|
||||
} else {
|
||||
push @changed, "$vn: $vv";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +514,7 @@ EnOcean_MD15Cmd($$$)
|
||||
|
||||
if($msg) {
|
||||
select(undef, undef, undef, 0.2);
|
||||
EnOcean_A5Cmd($hash, $msg, "00000000");
|
||||
EnOcean_A5Cmd($hash, $msg, EnOcean_GetMyDeviceId($hash));
|
||||
if($cmd eq "initialize") {
|
||||
delete($defs{$name}{READINGS}{CMD});
|
||||
delete($defs{$name}{READINGS}{$cmd});
|
||||
@@ -448,4 +532,24 @@ EnOcean_A5Cmd($$$)
|
||||
# type=A5 msg:4 senderId:4 status=00 subTelNum=01 destId:4 dBm=FF Security=00
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
|
||||
#
|
||||
# compose the Src-Id for a command to send
|
||||
#
|
||||
sub EnOcean_GetMyDeviceId($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $myId=0; # default: use Device-ID of EUL
|
||||
my $name = $hash->{NAME};
|
||||
my $baseId=hex($hash->{IODev}{BASEID});
|
||||
my $subId = AttrVal($name, "subId", "");
|
||||
if(defined $baseId and defined $subId) {
|
||||
# if there is a base-id an a subid -> use this combination
|
||||
$myId=sprintf("%08X", $baseId | $subId);
|
||||
}
|
||||
return $myId;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
290
FHEM/24_NetIO230B.pm
Normal file
290
FHEM/24_NetIO230B.pm
Normal file
@@ -0,0 +1,290 @@
|
||||
################################################################
|
||||
# fhem-module for NetIO 230B Power Distribution Unit
|
||||
#
|
||||
# http://www.koukaam.se/showproduct.php?article_id=1502
|
||||
#
|
||||
# Usage:
|
||||
# There are 2 modes:
|
||||
#
|
||||
# (1) - define the IP, user and password directly in fhem's fhem.cfg (or UI).
|
||||
#
|
||||
# define NetIO1 <IP address> <socket number> [<user> <password>];
|
||||
# e.g. define NetIO1 192.168.178.2 1 admin admin;
|
||||
#
|
||||
# if you omit the user credentials, the module will look for a configuration file,
|
||||
# if no configuration file is found, it tries with 'admin', 'admin'
|
||||
#
|
||||
#
|
||||
# (2) - define your credentials using a config file.
|
||||
#
|
||||
# define NetIO1 <IP address> <socket number> [<path_to_configuration_file>];
|
||||
# define NetIO1 192.168.178.2 1 /var/log/fhem/netio.conf);
|
||||
#
|
||||
# if you omit the configuration parameter, the module will look for a configuration
|
||||
# file at: /var/log/fhem/netio.conf
|
||||
#
|
||||
# NetIO230B Configuration file format:
|
||||
#
|
||||
# %config= (
|
||||
# host => "192.168.xx.xx",
|
||||
# user => "anyusername_without_spaces",
|
||||
# password => "anypassword_without_spaces"
|
||||
# );
|
||||
#
|
||||
################################################################
|
||||
# created 2012 by Andy Fuchs
|
||||
#---------
|
||||
# Changes:
|
||||
#---------
|
||||
# 2012-02-03 0.1 initial realease
|
||||
#
|
||||
|
||||
################################################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Data::Dumper;
|
||||
use LWP::UserAgent;
|
||||
use HTTP::Request;
|
||||
|
||||
use constant PARAM_NAME => 1;
|
||||
use constant PARAM_HOST => 2;
|
||||
use constant PARAM_SOCK => 3;
|
||||
use constant PARAM_USER => 4;
|
||||
use constant PARAM_PASS => 5;
|
||||
use constant PARAM_FILE => 4;
|
||||
|
||||
use constant DEBUG => 1;
|
||||
|
||||
sub
|
||||
NetIO230B_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{SetFn} = "NetIO230B_Set";
|
||||
$hash->{GetFn} = "NetIO230B_Get";
|
||||
$hash->{DefFn} = "NetIO230B_Define";
|
||||
$hash->{AttrList} = "loglevel:0,1,2,3,4,5,6";
|
||||
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
NetIO230B_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "no set value specified" if(int(@a) != 2);
|
||||
return "Unknown argument $a[1], choose one of on off " if($a[1] eq "?");
|
||||
|
||||
my $state = $a[1]; #initialize state to the passed parameter
|
||||
my $result = "command was not executed - $state is not 'on' or 'off'";
|
||||
if ($state eq "on" || $state eq "off")
|
||||
{
|
||||
$hash->{STATE} = $state;
|
||||
$state = int($state eq "on");
|
||||
#prepare the sockets default parameters; 'u' means: don't touch
|
||||
my @values=("u","u","u","u");
|
||||
my @sockets = @{$hash->{SOCKETS}};
|
||||
|
||||
foreach (@sockets) {
|
||||
$values[$_-1] = $state;
|
||||
$hash->{READINGS}{"socket$_"}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{"socket$_"}{VAL} = $state;
|
||||
}
|
||||
|
||||
$result = NetIO230B_Request($hash, "set", join("",@values));
|
||||
}
|
||||
|
||||
Log 3, "NetIO230B set @a => $result";
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
NetIO230B_Get($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $result = NetIO230B_Request($hash, "get");
|
||||
Log 3, "NetIO230B get @a => $result";
|
||||
|
||||
return $hash->{STATE};
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
NetIO230B_Request($@)
|
||||
{
|
||||
my ($hash, $cmd, $list) = @_;
|
||||
my $URL='';
|
||||
my $log='';
|
||||
|
||||
$URL="http://".$hash->{HOST}."/tgi/control.tgi?l=p:". $hash->{USER}.":".$hash->{PASS}."&p=";
|
||||
if($cmd eq "set") {
|
||||
$URL .= "$list";
|
||||
} else {
|
||||
$URL .= "l";
|
||||
}
|
||||
|
||||
my $agent = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1, timeout => 30);
|
||||
my $header = HTTP::Request->new(GET => $URL);
|
||||
my $request = HTTP::Request->new('GET', $URL, $header);
|
||||
my $response = $agent->request($request);
|
||||
|
||||
$log.= "Can't get $URL -- ".$response->status_line
|
||||
unless $response->is_success;
|
||||
|
||||
if($log ne "")
|
||||
{
|
||||
Log 3, "NetIO230B_Request: ".$log;
|
||||
return("");
|
||||
}
|
||||
|
||||
# read decoded content
|
||||
my $buffer = $response->decoded_content;
|
||||
|
||||
# strip html tags
|
||||
$buffer =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gs;
|
||||
|
||||
# strip leading whitespace
|
||||
$buffer =~ s/^\s+//;
|
||||
|
||||
#strip trailing whitespace
|
||||
$buffer =~ s/\s+$//;
|
||||
|
||||
return $buffer if ($cmd eq "set");
|
||||
|
||||
#+++todo
|
||||
#555 FORBIDDEN
|
||||
|
||||
#split the result into an array
|
||||
my @values=split(/ */,$buffer);
|
||||
|
||||
#save the values to the readings hash
|
||||
my $state = "???";
|
||||
my @sockets = @{$hash->{SOCKETS}};
|
||||
foreach (@sockets) {
|
||||
$hash->{READINGS}{"socket$_"}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{"socket$_"}{VAL} = $values[$_-1];
|
||||
if ($state == "???") { #initialize state
|
||||
$state = $values[$_-1];
|
||||
} else {
|
||||
$state = "???" if ($values[$_-1] != $state); #if states are mixed show ???
|
||||
}
|
||||
}
|
||||
|
||||
$hash->{STATE} = $state;
|
||||
|
||||
# debug output
|
||||
#my %k = %{$hash->{READINGS}};
|
||||
#foreach my $r (sort keys %k) {
|
||||
# Log 1, "$r S: $k{$r}{VAL} T: $k{$r}{TIME}";
|
||||
#}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
### _Define routing is called when fhem starts -> 'reloading' of the module does not call the define routine!!
|
||||
###
|
||||
sub
|
||||
NetIO230B_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
my $paramCount = int(@a);
|
||||
|
||||
Log 3, "Wrong syntax: use 'define <name> NetIO230B <ip-address> [<socket_number> <username> <password>]' or 'define <name> NetIO230B <ip-address> [<socket_number> <configfilename>]'" if(int(@a) < 4); #5 = mit user/pass #4 = mit config
|
||||
|
||||
#provide some default settings
|
||||
$hash->{CONFIGFILEPATH} = "/var/log/fhem/netio.conf"; #default file path is /var/log/fhem/netio.conf
|
||||
$hash->{USER} = "admin";
|
||||
$hash->{PASS} = "admin";
|
||||
@{$hash->{SOCKETS}} = (1,2,3,4); #default to all sockets
|
||||
|
||||
#mandatory parameter: 'HOST'
|
||||
$hash->{HOST} = $a[PARAM_HOST]; #can be overridden, if using a config file
|
||||
|
||||
#mandatory parameter: 'SOCKET #'; negative numbers are ignored
|
||||
my $buf = $a[PARAM_SOCK];
|
||||
if (($buf =~ m/^\d+$/) && ($buf >=0)) { #make sure input value is a positive number
|
||||
|
||||
if ($buf == 0) #input socket '0' is used as 'all'
|
||||
{
|
||||
@{$hash->{SOCKETS}} = (1,2,3,4); #use this number as 'array of socket-numbers' to operate on
|
||||
|
||||
} elsif ($buf <= 4) #input socket is a single number <=4
|
||||
{
|
||||
@{$hash->{SOCKETS}} = ($buf); #use this number as 'array of socket-numbers' to operate on
|
||||
|
||||
} else {
|
||||
|
||||
@{$hash->{SOCKETS}} = split("",$buf); #convert the input values to an array of sockets to operate on
|
||||
}
|
||||
}
|
||||
|
||||
#optional parameter: 'CONFIGFILE_NAME_or_PATH'
|
||||
if ($paramCount == 5) #seems a config file is passed
|
||||
{
|
||||
$hash->{CONFIGFILEPATH} = $a[PARAM_FILE] if defined($a[PARAM_FILE]);;
|
||||
}
|
||||
|
||||
#optional parameters: 'USER and PASS'
|
||||
if ($paramCount != 6)
|
||||
{
|
||||
my %config = NetIO230B_GetConfiguration($hash);
|
||||
if (%config) {
|
||||
$hash->{HOST} = $config{host} if (defined($config{host}));
|
||||
$hash->{USER} = $config{user} if (defined($config{user}));
|
||||
$hash->{PASS} = $config{password} if (defined($config{password}));
|
||||
} else {
|
||||
|
||||
Log 3, "NetIO230B: Configuration could not be read. Trying default values...\n";
|
||||
}
|
||||
|
||||
} else {
|
||||
#in any other case
|
||||
$hash->{USER} = $a[PARAM_USER] if defined($a[PARAM_USER]);
|
||||
$hash->{PASS} = $a[PARAM_PASS] if defined($a[PARAM_PASS]);
|
||||
}
|
||||
|
||||
Log 3, "NetIO230B: device opened at host: $hash->{HOST} => @a\n";
|
||||
|
||||
return undef;
|
||||
}
|
||||
##########################################
|
||||
#
|
||||
# NetIO230B Configuration-Format:
|
||||
#
|
||||
# %config= (
|
||||
# host => "192.168.xx.xx",
|
||||
# user => "anyusername_without_spaces",
|
||||
# password => "anypassword_without_spaces"
|
||||
# );
|
||||
#
|
||||
#
|
||||
##########################################
|
||||
|
||||
### _GetConfiguration reads a plain text file containing arbitrary information
|
||||
sub
|
||||
NetIO230B_GetConfiguration($)
|
||||
{
|
||||
my ($hash)= @_;
|
||||
my $configfilename = $hash->{CONFIGFILEPATH};
|
||||
|
||||
if(!open(CONFIGFILE, $configfilename))
|
||||
{
|
||||
Log 3, "NetIO230B: Cannot open settings file '$configfilename'.";
|
||||
return ();
|
||||
}
|
||||
my @configfile=<CONFIGFILE>;
|
||||
close(CONFIGFILE);
|
||||
|
||||
my %config;
|
||||
eval join("", @configfile);
|
||||
|
||||
return %config;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -119,6 +119,10 @@ watchdog_Trigger($)
|
||||
Log(3, "Watchdog $ntfy->{NAME} triggered");
|
||||
my $exec = SemicolonEscape($ntfy->{CMD});;
|
||||
$ntfy->{STATE} = "triggered";
|
||||
|
||||
$ntfy->{READINGS}{Triggered}{TIME} = TimeNow();
|
||||
$ntfy->{READINGS}{Triggered}{VAL} = $ntfy->{STATE};
|
||||
|
||||
my $ret = AnalyzeCommandChain(undef, $exec);
|
||||
Log 3, $ret if($ret);
|
||||
}
|
||||
|
||||
@@ -292,10 +292,10 @@ my @usbtable = (
|
||||
define => "CUL_PARAM CUL DEVICE\@9600 1PARAM34", },
|
||||
|
||||
{ NAME => "TCM310",
|
||||
matchList => ["cu.usbserial(.*)", "cu.usbmodem(.*)", "ttyUSB(.*)"],
|
||||
matchList => ["cu.usbserial(.*)", "cu.usbmodem(.*)", "ttyUSB(.*)", "ttyACM(.*)"],
|
||||
DeviceName=> "DEVICE\@57600",
|
||||
request => pack("H*", "5500010005700838"), # get idbase
|
||||
response => "^\x00\xFF....",
|
||||
response => "^\x55\x00\x05\x01",
|
||||
define => "TCM310_PARAM TCM 310 DEVICE\@57600", },
|
||||
|
||||
{ NAME => "TCM120",
|
||||
@@ -336,6 +336,7 @@ CommandUsb($$)
|
||||
if($^O eq "linux") {
|
||||
# One device at a time to avoid endless loop
|
||||
my $lsusb = `lsusb`;
|
||||
if($lsusb) {
|
||||
my $culType;
|
||||
$culType = "CUL_V4" if($lsusb =~ m/VID=03eb.PID=2ff0/s); # FritzBox
|
||||
$culType = "CUL_V3" if($lsusb =~ m/VID=03eb.PID=2ff4/s); # FritzBox
|
||||
@@ -352,6 +353,7 @@ CommandUsb($$)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################
|
||||
# Now the /dev scan
|
||||
@@ -362,7 +364,7 @@ CommandUsb($$)
|
||||
my $PARAM = $1;
|
||||
$PARAM =~ s/[^A-Za-z0-9]//g;
|
||||
my $name = $thash->{NAME};
|
||||
$msg = "$dev: checking if it is a $name";
|
||||
$msg = "### $dev: checking if it is a $name";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
|
||||
# Check if it already used
|
||||
@@ -370,7 +372,7 @@ CommandUsb($$)
|
||||
if($defs{$d}{DeviceName} &&
|
||||
$defs{$d}{DeviceName} =~ m/$dev/ &&
|
||||
$defs{$d}{FD}) {
|
||||
$msg = "$dev: already used by the $d fhem device";
|
||||
$msg = "already used by the $d fhem device";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
goto NEXTDEVICE;
|
||||
}
|
||||
@@ -382,22 +384,21 @@ CommandUsb($$)
|
||||
my $hash = { NAME=>$name, DeviceName=>$dname };
|
||||
DevIo_OpenDev($hash, 0, 0);
|
||||
if(!defined($hash->{USBDev})) {
|
||||
$msg = "$dev: Cannot open the device, check the log";
|
||||
DevIo_CloseDev($hash); # remove the ReadyFn loop
|
||||
$msg = "cannot open the device";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
goto NEXTDEVICE;
|
||||
}
|
||||
|
||||
# Clear the USB buffer
|
||||
if($thash->{flush}) {
|
||||
DevIo_SimpleWrite($hash, $thash->{flush}, 0);
|
||||
DevIo_SimpleWrite($hash, $thash->{flush}, 0) if($thash->{flush});
|
||||
DevIo_TimeoutRead($hash, 0.1);
|
||||
}
|
||||
DevIo_SimpleWrite($hash, $thash->{request}, 0);
|
||||
my $answer = DevIo_TimeoutRead($hash, 0.1);
|
||||
DevIo_CloseDev($hash);
|
||||
|
||||
if($answer !~ m/$thash->{response}/) {
|
||||
$msg = "$dev: got wrong answer for a $name";
|
||||
$msg = "got wrong answer for a $name";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
next;
|
||||
}
|
||||
@@ -405,7 +406,7 @@ CommandUsb($$)
|
||||
my $define = $thash->{define};
|
||||
$define =~ s/PARAM/$PARAM/g;
|
||||
$define =~ s,DEVICE,$dir/$dev,g;
|
||||
$msg = "$dev: create as a fhem device with: define $define";
|
||||
$msg = "create as a fhem device with: define $define";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
|
||||
if(!$scan) {
|
||||
|
||||
@@ -27,7 +27,6 @@ updatefhem_Initialize($$)
|
||||
$cmds{CULflash} = \%chash;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
CommandUpdatefhem($$)
|
||||
@@ -37,42 +36,18 @@ CommandUpdatefhem($$)
|
||||
my $ret = "";
|
||||
my $moddir = (-d "FHEM.X" ? "FHEM.X" : "$attr{global}{modpath}/FHEM");
|
||||
|
||||
## backup by RueBe
|
||||
my @commandchain = split(/ +/,$param);
|
||||
## backup by RueBe, simplified by rudi
|
||||
my @args = split(/ +/,$param);
|
||||
|
||||
# Check if the first parameter is "backup"
|
||||
if($param) {
|
||||
my @commandchain = split(/ +/,$param);
|
||||
if(uc($commandchain[0]) eq "BACKUP") {
|
||||
my $backupdir = AttrVal("global", "backupdir", "/tmp/FHEM_Backup");
|
||||
# create the backupfolder
|
||||
if(!-d $backupdir) {
|
||||
if(!mkdir($backupdir)) {
|
||||
return "Can't create backup folder $!";
|
||||
}
|
||||
}
|
||||
# full backup, for compatibility, we use the native copy unction, not
|
||||
# dircopy from File::Copy::Recursive
|
||||
if($commandchain[1] eq "") {
|
||||
opendir(IMD, $moddir) || return "Cannot open fhem-module directory";
|
||||
my @files= readdir(IMD);
|
||||
closedir(IMD);
|
||||
my $f;
|
||||
|
||||
foreach $f (@files) {
|
||||
unless ( ($f eq ".") || ($f eq "..") ) {
|
||||
my $ret = copy("$moddir/$f", "$backupdir/$f");
|
||||
}
|
||||
}
|
||||
$param = "";
|
||||
|
||||
} else {
|
||||
# one file backup
|
||||
copy("$moddir/$commandchain[1]", "$backupdir/$commandchain[1]");
|
||||
# recreate $param for further use
|
||||
$param = $commandchain[1];
|
||||
}
|
||||
}
|
||||
if(@args && uc($args[0]) eq "BACKUP") {
|
||||
my $bdir = AttrVal("global", "backupdir", "$moddir.backup");
|
||||
my $dateTime = TimeNow();
|
||||
$dateTime =~ s/ /_/g;
|
||||
my $ret = `(mkdir -p $bdir && tar cf - $moddir | gzip > $bdir/FHEM.$dateTime.tgz) 2>&1`;
|
||||
return $ret if($ret);
|
||||
shift @args;
|
||||
$param = join("", @args);
|
||||
}
|
||||
|
||||
# Read in the OLD filetimes.txt
|
||||
@@ -226,6 +201,8 @@ GetHttpFile($$)
|
||||
my $req = "GET $filename HTTP/1.0\r\nHost: $host\r\n\r\n\r\n";
|
||||
syswrite $conn, $req;
|
||||
my ($buf, $ret);
|
||||
|
||||
# Note: should add a timeout
|
||||
while(sysread($conn,$buf,65536) > 0) {
|
||||
$ret .= $buf;
|
||||
}
|
||||
|
||||
5
HISTORY
5
HISTORY
@@ -485,3 +485,8 @@
|
||||
- Thu Dec 01 2011 (Martin F.)
|
||||
- Move JsonList from contrib to main modules. Jsonlist output is optimized
|
||||
and now be more structured.
|
||||
|
||||
- Sun Jan 29 2012 (Maz Rashid)
|
||||
- Improving 10_EIB.pm by introducing Get and interpreting received value according
|
||||
to the selected model (based on datapoint types.)
|
||||
- Introduced documentation for TUL / EIB modules.
|
||||
@@ -29,7 +29,7 @@
|
||||
#
|
||||
# where <key> is one of currentPower, totalEnergy, totalEnergyDay,
|
||||
# totalEnergyMonth, totalEnergyYear, UDC, IDC, UDCB, IDCB, UDCC, IDCC,
|
||||
# gridVoltage, gridPower and temperature.
|
||||
# gridVoltage, gridCurrent and temperature.
|
||||
#
|
||||
##############################################################################
|
||||
#
|
||||
@@ -68,8 +68,8 @@ my @gets = ('totalEnergyDay', # kWh
|
||||
'currentPower', # W
|
||||
'UDC', 'IDC', 'UDCB', # V, A, V
|
||||
'IDCB', 'UDCC', 'IDCC', # A, V, A
|
||||
'gridVoltage', 'gridPower', # V, A
|
||||
'temperature'); # <EFBFBD>C
|
||||
'gridVoltage', 'gridCurrent', # V, A
|
||||
'temperature'); # oC
|
||||
|
||||
sub
|
||||
SolarView_Initialize($)
|
||||
@@ -100,10 +100,17 @@ SolarView_Define($$)
|
||||
$hash->{Interval} = (@args>=5) ? int($args[4]) : 300;
|
||||
$hash->{Timeout} = (@args>=6) ? int($args[5]) : 4;
|
||||
|
||||
$hash->{Invalid} = -1;
|
||||
$hash->{NightOff} = 1;
|
||||
$hash->{Debounce} = 50;
|
||||
$hash->{Sleep} = 0;
|
||||
# config variables
|
||||
$hash->{Invalid} = -1; # default value for invalid readings
|
||||
$hash->{Sleep} = 0; # seconds to sleep before connect
|
||||
$hash->{Debounce} = 50; # minimum level for enabling debounce (0 to disable)
|
||||
$hash->{Rereads} = 2; # number of retries when reading curPwr of 0
|
||||
$hash->{NightOff} = 'yes'; # skip connection to SV at night?
|
||||
$hash->{UseSVNight} = 'yes'; # use the on/off timings from SV (else: SUNRISE_EL)
|
||||
$hash->{UseSVTime} = ''; # use the SV time as timestamp (else: TimeNow())
|
||||
|
||||
# internal variables
|
||||
$hash->{Debounced} = 0;
|
||||
|
||||
$hash->{STATE} = 'Initializing';
|
||||
|
||||
@@ -135,22 +142,29 @@ SolarView_Update($)
|
||||
# if NightOff is set and there has been a successful
|
||||
# reading before, then skip this update "at night"
|
||||
#
|
||||
if ($hash->{NightOff} and $hash->{READINGS}{currentPower}{VAL} != $hash->{Invalid})
|
||||
if ($hash->{NightOff} and SolarView_IsNight($hash) and
|
||||
$hash->{READINGS}{currentPower}{VAL} != $hash->{Invalid})
|
||||
{
|
||||
my ($sec,$min,$hour) = localtime(time);
|
||||
return undef if ($hour < 6 or $hour > 22);
|
||||
$hash->{STATE} = 'Pausing';
|
||||
return undef;
|
||||
}
|
||||
|
||||
sleep($hash->{Sleep}) if $hash->{Sleep};
|
||||
|
||||
Log 4, "$hash->{NAME} tries to connect solarview at $hash->{Host}:$hash->{Port}";
|
||||
Log 4, "$hash->{NAME} tries to contact solarview at $hash->{Host}:$hash->{Port}";
|
||||
|
||||
# save the previous value of currentPower for debouncing
|
||||
my $cpVal = $hash->{READINGS}{currentPower}{VAL};
|
||||
my $cpTime = $hash->{READINGS}{currentPower}{TIME};
|
||||
|
||||
my $success = 0;
|
||||
my $rereads = $hash->{Rereads};
|
||||
|
||||
eval {
|
||||
local $SIG{ALRM} = sub { die 'timeout'; };
|
||||
alarm $hash->{Timeout};
|
||||
|
||||
READ_SV:
|
||||
my $socket = IO::Socket::INET->new(PeerAddr => $hash->{Host},
|
||||
PeerPort => $hash->{Port},
|
||||
Timeout => $hash->{Timeout});
|
||||
@@ -162,17 +176,17 @@ SolarView_Update($)
|
||||
my $res = <$socket>;
|
||||
close($socket);
|
||||
|
||||
alarm 0;
|
||||
|
||||
if ($res and $res =~ /^\{(00,[\d\.,]+)\},/)
|
||||
{
|
||||
my @vals = split(/,/, $1);
|
||||
|
||||
my $tn = sprintf("%04d-%02d-%02d %02d:%02d:00",
|
||||
$vals[3], $vals[2], $vals[1], $vals[4], $vals[5]);
|
||||
my $timenow = TimeNow();
|
||||
|
||||
my $cpVal = $hash->{READINGS}{currentPower}{VAL};
|
||||
my $cpTime = $hash->{READINGS}{currentPower}{TIME};
|
||||
if ($hash->{UseSVTime})
|
||||
{
|
||||
$timenow = sprintf("%04d-%02d-%02d %02d:%02d:00",
|
||||
$vals[3], $vals[2], $vals[1], $vals[4], $vals[5]);
|
||||
}
|
||||
|
||||
for my $i (6..19)
|
||||
{
|
||||
@@ -181,19 +195,33 @@ SolarView_Update($)
|
||||
if (defined($vals[$i]))
|
||||
{
|
||||
$hash->{READINGS}{$gets[$getIdx]}{VAL} = 0 + $vals[$i];
|
||||
$hash->{READINGS}{$gets[$getIdx]}{TIME} = $tn;
|
||||
$hash->{READINGS}{$gets[$getIdx]}{TIME} = $timenow;
|
||||
}
|
||||
}
|
||||
|
||||
# if Debounce is enabled, then skip one drop of
|
||||
if ($rereads and $hash->{READINGS}{currentPower}{VAL} == 0)
|
||||
{
|
||||
sleep(1);
|
||||
$rereads = $rereads - 1;
|
||||
goto READ_SV;
|
||||
}
|
||||
|
||||
alarm 0;
|
||||
|
||||
# if Debounce is enabled (>0), then skip one! drop of
|
||||
# currentPower from 'greater than Debounce' to 'Zero'
|
||||
#
|
||||
if ($hash->{Debounce} > 0 and
|
||||
$hash->{Debounce} < $cpVal and
|
||||
$hash->{READINGS}{currentPower}{VAL} == 0)
|
||||
$hash->{READINGS}{currentPower}{VAL} == 0 and
|
||||
not $hash->{Debounced})
|
||||
{
|
||||
$hash->{READINGS}{currentPower}{VAL} = $cpVal;
|
||||
$hash->{READINGS}{currentPower}{TIME} = $cpTime;
|
||||
|
||||
$hash->{Debounced} = 1;
|
||||
} else {
|
||||
$hash->{Debounced} = 0;
|
||||
}
|
||||
|
||||
$success = 1;
|
||||
@@ -248,5 +276,70 @@ SolarView_Undef($$)
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
SolarView_IsNight($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $isNight = 0;
|
||||
my ($sec,$min,$hour,$mday,$mon) = localtime(time);
|
||||
|
||||
# reset totalEnergyX if needed
|
||||
if ($hour == 0)
|
||||
{
|
||||
my $timenow = TimeNow();
|
||||
|
||||
$hash->{READINGS}{totalEnergyDay}{VAL} = 0;
|
||||
$hash->{READINGS}{totalEnergyDay}{TIME} = $timenow;
|
||||
#
|
||||
if ($mday == 1)
|
||||
{
|
||||
$hash->{READINGS}{totalEnergyMonth}{VAL} = 0;
|
||||
$hash->{READINGS}{totalEnergyMonth}{TIME} = $timenow;
|
||||
#
|
||||
if ($mon == 0)
|
||||
{
|
||||
$hash->{READINGS}{totalEnergyYear}{VAL} = 0;
|
||||
$hash->{READINGS}{totalEnergyYear}{TIME} = $timenow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hash->{UseSVNight})
|
||||
{
|
||||
# These are the on/off timings from Solarview, see
|
||||
# http://www.amhamberg.de/solarview-fb_Installieren.pdf
|
||||
#
|
||||
if ($mon == 0) { # Jan
|
||||
$isNight = ($hour < 7 or $hour > 17);
|
||||
} elsif ($mon == 1) { # Feb
|
||||
$isNight = ($hour < 7 or $hour > 18);
|
||||
} elsif ($mon == 2) { # Mar
|
||||
$isNight = ($hour < 6 or $hour > 19);
|
||||
} elsif ($mon == 3) { # Apr
|
||||
$isNight = ($hour < 5 or $hour > 20);
|
||||
} elsif ($mon == 4) { # May
|
||||
$isNight = ($hour < 5 or $hour > 21);
|
||||
} elsif ($mon == 5) { # Jun
|
||||
$isNight = ($hour < 5 or $hour > 21);
|
||||
} elsif ($mon == 6) { # Jul
|
||||
$isNight = ($hour < 5 or $hour > 21);
|
||||
} elsif ($mon == 7) { # Aug
|
||||
$isNight = ($hour < 5 or $hour > 21);
|
||||
} elsif ($mon == 8) { # Sep
|
||||
$isNight = ($hour < 6 or $hour > 20);
|
||||
} elsif ($mon == 9) { # Oct
|
||||
$isNight = ($hour < 7 or $hour > 19);
|
||||
} elsif ($mon == 10) { # Nov
|
||||
$isNight = ($hour < 7 or $hour > 17);
|
||||
} elsif ($mon == 11) { # Dec
|
||||
$isNight = ($hour < 8 or $hour > 16);
|
||||
}
|
||||
} else { # we use SUNRISE_EL
|
||||
$isNight = not isday();
|
||||
}
|
||||
|
||||
return $isNight;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
<a href="#ECMD">ECMD</a>
|
||||
<a href="#ECMDDevice">ECMDDevice</a>
|
||||
<a href="#DS18S20">DS18S20</a>
|
||||
<a href="#EIB">EIB</a>
|
||||
<a href="#EnOcean">EnOcean</a>
|
||||
<a href="#EM">EM</a>
|
||||
<a href="#EMEM">EMEM</a>
|
||||
@@ -104,6 +105,7 @@
|
||||
<a href="#M232">M232</a>
|
||||
<a href="#M232Counter">M232Counter</a>
|
||||
<a href="#M232Voltage">M232Voltage</a>
|
||||
<a href="#NetIO230B">NetIO230B</a>
|
||||
<a href="#OREGON">OREGON</a>
|
||||
<a href="#OWFS">OWFS</a>
|
||||
<a href="#OWTEMP">OWTEMP</a>
|
||||
@@ -115,6 +117,7 @@
|
||||
<a href="#SIS_PMS">SIS_PMS</a>
|
||||
<a href="#TCM">TCM</a>
|
||||
<a href="#TellStick">TellStick</a>
|
||||
<a href="#TUL">TUL</a>
|
||||
<a href="#USF1000">USF1000</a>
|
||||
<a href="#USBWX">USBWX</a>
|
||||
<a href="#VantagePro2">VantagePro2</a>
|
||||
@@ -709,24 +712,23 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
will be necessary to apply the changes.
|
||||
<br>
|
||||
|
||||
If backup is specified, then the old files are saved before overwriting
|
||||
them. They are copied to the folder given in global <backupdir> or as
|
||||
default to /tmp/FHEM_Backup. Please check if the fhem user has the rights
|
||||
to create a folder for backup.
|
||||
If backup is specified, then the complete FHEM directory (containing the
|
||||
modules and .gplot files) will be saved into a .tar.gz file with a
|
||||
timestamp in the <a href="#modpath">modpath</a>/FHEM.backup directory or to
|
||||
a directory specified by the <a href="#backupdir">backupdir</a> global
|
||||
attribute. Note: tar and gzip must be installed to use this feature.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<b>Attributes</b> <ul>
|
||||
<a name="backupdir"></a>
|
||||
<li>backupdir<br>
|
||||
A folder where updatefhem can store all files from <a
|
||||
href="#modpath">modpath</a> before executing the update. Please check if
|
||||
the fhem user has the rights to create this folder.
|
||||
</li><br>
|
||||
Note: this is a global attribute, e.g.<br>
|
||||
A folder where updatefhem will store the compressed backup file.
|
||||
Note: this is a global attribute.<br>Example:
|
||||
<ul>
|
||||
attr global backup /Volumes/BigHD<br>
|
||||
attr global backupdir /Volumes/BigHD<br>
|
||||
</ul>
|
||||
</li><br>
|
||||
</ul><br>
|
||||
|
||||
</ul>
|
||||
@@ -2947,7 +2949,7 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
party-temp <tmp><br>
|
||||
desired-temp <tmp><br>
|
||||
Set different temperatures. Temp must be between 6 and 30
|
||||
Celsius, and precision is half a degree.
|
||||
Celsius, and precision is half a degree.</li>
|
||||
<li>tempListSat HH:MM temp ... 24:00 temp<br>
|
||||
tempListSun HH:MM temp ... 24:00 temp<br>
|
||||
tempListMon HH:MM temp ... 24:00 temp<br>
|
||||
@@ -2959,6 +2961,13 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
specified for each week day, the resolution is 10 Minutes. The
|
||||
last time spec must always be 24:00.<br>
|
||||
Example: set th tempListSat 06:00 19 23:00 22.5 24:00 19<br>
|
||||
Meaning: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be
|
||||
22.5, thereafter until midnight, 19 degrees celsius is desired.</li>
|
||||
<li>displayMode [temp-only|temp-hum]<br>
|
||||
displayTemp [actual|setpoint]<br>
|
||||
displayTempUnit [celsius|fahrenheit]<br>
|
||||
controlMode [manual|auto|central|party]<br>
|
||||
decalcDay <day></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
<br>
|
||||
@@ -3210,12 +3219,124 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="EIB"></a>
|
||||
<h3>EIB / KNX</h3>
|
||||
<ul>
|
||||
EIB/KNX is a standard for building automation / home automation.
|
||||
It is mainly based on a twisted pair wiring, but also other mediums (ip, wireless) are specified.
|
||||
|
||||
While the module <a href="#TUL">TUL</a> represents the connection to the EIB network,
|
||||
the EIB modules represent individual EIB devices. This module provides a basic set of operations (on, off, on-till, etc.)
|
||||
to switch on/off EIB devices. Sophisticated setups can be achieved by combining a number of
|
||||
EIB module instances or by sending raw hex values to the network (set <devname> raw <hexval>).
|
||||
|
||||
EIB/KNX defines a series of Datapoint Type as standard data types used
|
||||
to allow general interpretation of values of devices manufactured by diferent companies.
|
||||
This datatypes are used to interpret the status of a device, so the state in FHEM will then
|
||||
show the correct value.
|
||||
|
||||
<br><br>
|
||||
<a name="EIBdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> EIB <main group> [<additional group> ..]</code>
|
||||
<br><br>
|
||||
|
||||
Define an EIB device, connected via a <a href="#TUL">TUL</a>. The
|
||||
<group> parameters are either a group name notation (0-15/0-15/0-255) or the hex representation of the value (0-f0-f0-ff).
|
||||
The <main group> is used for sending of commands to the EIB network.
|
||||
The state of the instance will be updated when a new state is received from the network for any of the given groups.
|
||||
This is usefull for example for toggle switches where a on command is send to one group and the real state (on or off) is
|
||||
responded back on a second group.
|
||||
|
||||
For actors and sensors the
|
||||
<a href="#autocreate">autocreate</a> module may help.<br>
|
||||
|
||||
Example:
|
||||
<ul>
|
||||
<code>define lamp1 EIB 0/10/12</code><br>
|
||||
<code>define lamp1 EIB 0/10/12 0/0/5</code><br>
|
||||
<code>define lamp1 EIB 0A0C</code><br>
|
||||
</ul>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="EIBset"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<code>set <name> <value> [<time>]</code><br>
|
||||
where value one of:
|
||||
<li><b>on</b> switch on device
|
||||
<li><b>off</b> switch off device
|
||||
<li><b>on-for-timer</b> <secs> switch on the device for the given time. After the specified seconds a switch off command is sent.
|
||||
<li><b>on-till</b> <time spec> switches the device on. The device will be switched off at the given time.
|
||||
<li><b>value</b> <hexvalue> sends the given value as raw data to the device.
|
||||
|
||||
<br>Example:
|
||||
<ul><code>
|
||||
set lamp1 on<br>
|
||||
set lamp1 off<br>
|
||||
set lamp1 on-for-timer 10<br>
|
||||
set lamp1 on-till 13:15:00<br>
|
||||
set lamp1 value 234578<br>
|
||||
</code></ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="EIBattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a href="#eventMap">eventMap</a></li>
|
||||
<li><a href="#webCmd">webCmd</a></li>
|
||||
<li><a href="#IODev">IODev</a></li>
|
||||
<li><a href="#loglevel">loglevel</a></li>
|
||||
<li><a href="#do_not_notify">do_not_notify</a></li>
|
||||
<li><a href="#ignore">ignore</a></li>
|
||||
<li><a href="#dummy">dummy</a></li>
|
||||
<li><a href="#showtime">showtime</a></li>
|
||||
<li><a href="#model">model</a>
|
||||
set the model according to the datapoint types defined by the (<a href="http://www.sti.uniurb.it/romanell/110504-Lez10a-KNX-Datapoint%20Types%20v1.5.00%20AS.pdf" target="_blank">EIB / KNX specifications</a>).<br>
|
||||
The device state in FHEM is interpreted and shown according to the specification.
|
||||
<ul>
|
||||
<li>dpt5</li>
|
||||
<li>percent</li>
|
||||
<li>dpt7</li>
|
||||
<li>length-mm</li>
|
||||
<li>current-mA</li>
|
||||
<li>brightness</li>
|
||||
<li>timeperiod-ms</li>
|
||||
<li>timeperiod-min</li>
|
||||
<li>timeperiod-h</li>
|
||||
<li>dpt9</li>
|
||||
<li>tempsensor</li>
|
||||
<li>lightsensor</li>
|
||||
<li>dpt10</li>
|
||||
<li>time</li>
|
||||
<li>dpt11</li>
|
||||
<li>date</li>
|
||||
<li>dpt12</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
|
||||
<a name="EnOcean"></a>
|
||||
<h3>EnOcean</h3>
|
||||
<ul>
|
||||
Devices sold by numerous hardware verndors (e.g. Eltako, Peha, etc), using
|
||||
Devices sold by numerous hardware vendors (e.g. Eltako, Peha, etc), using
|
||||
the RF Protocol provided by the EnOcean Alliance.
|
||||
<br>
|
||||
To send commands to an Enocean-actuator the <a href="#TCM">TCM</a> can either use the
|
||||
unique Chip-ID (then you can control only one actuator, but nobody else
|
||||
can send commands to your device from a normal enocean-module). Or you can set/use the
|
||||
Base-ID and define a Sub-ID for every device to control (then up to 128 devices
|
||||
can be controlled, but anyone take over your devices simply by sniffing and copying the base-id).
|
||||
<br>
|
||||
To use Sub-IDs set the attribute <code>subId</code> to different values (0-127) for all your devices.
|
||||
<br><br>
|
||||
<a name="EnOceandefine"></a>
|
||||
<b>Define</b>
|
||||
@@ -3250,6 +3371,31 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
Do not regulate the MD15.</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Eltako-Dimmer-Switch (FUD12NPN, FUD61NP, FUD61NPN). Set subtype: <code>attr mydimmer subType eltakoDimmer</code>
|
||||
<br>
|
||||
Commands:
|
||||
<ul>
|
||||
<li>dimto <value></li>
|
||||
<li>dimup <value></li>
|
||||
<li>dimdown <value></li>
|
||||
<li>on</li>
|
||||
<li>off</li>
|
||||
<li>teach</li>
|
||||
</ul>
|
||||
Attributes:
|
||||
<ul>
|
||||
<li>dimTime <value><br>. Set Dimm-velocity (0-255) 0: fast</li>
|
||||
</ul></li>
|
||||
|
||||
<li>Eltako-Roller-Shutter (FSB12, FSB61). Set subtype: <code>attr myroll subType eltakoRoll</code>
|
||||
<br>
|
||||
Commands:
|
||||
<ul>
|
||||
<li>up</li>
|
||||
<li>down</li>
|
||||
<li>stop</li>
|
||||
<li>teach</li>
|
||||
</ul></li>
|
||||
|
||||
<li>all other:
|
||||
<ul>
|
||||
@@ -3369,7 +3515,12 @@ A line ending with \ will be concatenated with the next one, so long lines
|
||||
<li>actuator: [ok|obstructed]
|
||||
<li>temperature: $tmp
|
||||
</ul>
|
||||
|
||||
<li>Ratio Presence Sensor Eagle PM101 (set subType to PM101)<br>
|
||||
<ul>
|
||||
<li>brightness: $lux
|
||||
<li>channel1: [on|off]
|
||||
<li>channel2: [on|off]
|
||||
</ul>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@@ -6387,6 +6538,103 @@ Terminating
|
||||
<li><a href="#loglevel">loglevel</a></li><br>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
|
||||
<a name="NetIO230B"></a>
|
||||
<h3>NetIO230B</h3>
|
||||
<ul>
|
||||
<p>
|
||||
fhem-module for NetIO 230B Power Distribution Unit (see: <a
|
||||
href="http://www.koukaam.se/showproduct.php?article_id=1502">NetIO 230B
|
||||
(koukaam.se)</a>)
|
||||
</p>
|
||||
Note: this module needs the HTTP::Request and LWP::UserAgent perl modules.
|
||||
<br />
|
||||
Please also note: the PDU must use firmware 3.1 or later and set to unencrypted mode.
|
||||
<br /><br />
|
||||
<a name="NETIO230Bdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
|
||||
<li><code>define <name> NetIO230B <ip-address> <socket number(s)
|
||||
> [<user name> <password>]</code></li>
|
||||
|
||||
<li><code>define <name> NetIO230B <ip-address> <socket number(s)
|
||||
> [<config file path>]</code></li>
|
||||
|
||||
<p>
|
||||
Defines a switching device, where sockets can be switched
|
||||
</p>
|
||||
<ul>
|
||||
<li>separately (just use 0-4 as socket number)</li>
|
||||
<li>all together (use 1234 as socket number)</li>
|
||||
<li>in arbitrary groups (e.g 13 switches socket 1 and 3, 42
|
||||
switches socket 2 and 4, etc...), invalid numbers are
|
||||
ignored</li>
|
||||
</ul>
|
||||
<p>
|
||||
User name and password are optional. When no user name or
|
||||
password is passed, the module looks for a configfile at
|
||||
'/var/log/fhem/netio.conf'. If no config file is found, it
|
||||
uses 'admin/admin' as user/pass, since this is the default
|
||||
configuration for the device.
|
||||
<p>
|
||||
Alternatively you can pass a path to a configfile instead of
|
||||
the user/pass combo. (e.g. /var/tmp/tmp.conf)
|
||||
Configfile-Format:<br />
|
||||
<ul>
|
||||
<code>
|
||||
%config= (<br />
|
||||
host => "192.168.61.40",<br />
|
||||
user => "admin",<br />
|
||||
password => "admin"<br />
|
||||
);</code>
|
||||
<br /><br /><small>(All settings optional)</small>
|
||||
</ul>
|
||||
</p>
|
||||
<p>Examples:</p>
|
||||
<ul>
|
||||
<li><code>define Socket3 NetIO230B 192.168.178.10 3</code></li>
|
||||
<li><code>define Socket1_and_4 NetIO230B 192.168.178.10 14</code></li>
|
||||
<li><code>define coffeemaker NetIO230B 192.168.178.10 1 username secretpassword</code></li>
|
||||
<li><code>define coffeemaker_and_light NetIO230B 192.168.178.10 23 /var/log/kitchen.conf</code></li>
|
||||
</ul>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="NETIO230Bget"></a>
|
||||
<b>Get </b>
|
||||
<ul>
|
||||
<code>get <name> state</code>
|
||||
<br><br>
|
||||
returns the state of the socket(s)<br>
|
||||
|
||||
Example:
|
||||
<ul>
|
||||
<code>get coffeemaker_and_light</code> => <code>on or off</code><br>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
<a name="NETIO230Bset"></a>
|
||||
<b>Set </b>
|
||||
<ul>
|
||||
<code>set <name> <value></code>
|
||||
<br><br>
|
||||
where <code>value</code> is one of:<br>
|
||||
<pre>
|
||||
on
|
||||
off
|
||||
</pre>
|
||||
Examples:
|
||||
<ul>
|
||||
<code>set coffeemaker_and_light on</code><br>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
|
||||
<a name="TellStick"></a>
|
||||
<h3>TellStick</h3>
|
||||
<ul>
|
||||
@@ -6690,6 +6938,92 @@ href="http://www.elv.de/output/controller.aspx?cid=74&detail=10&detail2=29870">U
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
<a name="TUL"></a>
|
||||
<h3>TUL</h3>
|
||||
<ul>
|
||||
|
||||
<table>
|
||||
<tr><td>
|
||||
The TUL module is the representation of a EIB / KNX connector in FHEM.
|
||||
<a href="#EIB">EIB</a> instances represent the EIB / KNX devices and will need a TUL as IODev to communicate with the EIB / KNX network.<br>
|
||||
The TUL module is designed to connect to EIB network either using EIBD or the <a href="http://busware.de/tiki-index.php?page=TUL" target="_blank">TUL usb stick</a> created by busware.de
|
||||
|
||||
Note: this module may require the Device::SerialPort or Win32::SerialPort
|
||||
module if you attach the device via USB and the OS sets strange default
|
||||
parameters for serial devices.
|
||||
|
||||
</td><td>
|
||||
<img src="http://busware.de/show_image.php?id=269" width="100%" height="100%"/>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<a name="TULdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> TUL <device> <physical address></code> <br>
|
||||
<br>
|
||||
TUL usb stick / TPUART serial devices:<br><ul>
|
||||
<device> specifies the serial port to communicate with the TUL.
|
||||
The name of the serial-device depends on your distribution, under
|
||||
linux the cdc_acm kernel module is responsible, and usually a
|
||||
/dev/ttyACM0 device will be created. If your distribution does not have a
|
||||
cdc_acm module, you can force usbserial to handle the TUL by the
|
||||
following command:<ul>modprobe usbserial vendor=0x03eb
|
||||
product=0x204b</ul>In this case the device is most probably
|
||||
/dev/ttyUSB0.<br><br>
|
||||
|
||||
You can also specify a baudrate if the device name contains the @
|
||||
character, e.g.: /dev/ttyACM0@19200<br><br>
|
||||
Note: For TUL usb stick the baudrate 19200 is needed and this is the default
|
||||
when no baudrate is given.
|
||||
<br><br>
|
||||
|
||||
Example:<br>
|
||||
<code>define tul TUL tul:/dev/ttyACM0 1.1.249</code>
|
||||
</ul>
|
||||
EIBD:<br><ul>
|
||||
<device> specifies the host:port of the eibd device. E.g.
|
||||
eibd:192.168.0.244:2323. When using the standard port, the port can be omitted.
|
||||
<br><br>
|
||||
|
||||
Example:<br>
|
||||
<code>define tul TUL eibd:localhost 1.1.249</code>
|
||||
</ul>
|
||||
<br>
|
||||
If the device is called none, then no device will be opened, so you
|
||||
can experiment without hardware attached.<br>
|
||||
|
||||
The physical address is used as the source address of telegrams sent to EIB network.
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="TULset"></a>
|
||||
<b>Set </b>
|
||||
<ul>
|
||||
<li>raw<br>
|
||||
Issue a TUL raw telegram message
|
||||
</li><br>
|
||||
</ul>
|
||||
|
||||
<a name="TULget"></a>
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<li>raw<br>
|
||||
sends a read telegram
|
||||
</li><br>
|
||||
</ul>
|
||||
|
||||
<a name="TULattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a href="#do_not_notify">do_not_notify</a></li><br>
|
||||
<li><a href="#attrdummy">dummy</a></li><br>
|
||||
<li><a href="#showtime">showtime</a></li><br>
|
||||
<li><a href="#loglevel">loglevel</a></li><br>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
|
||||
<a name="weblink"></a>
|
||||
<h3>weblink</h3>
|
||||
|
||||
75
fhem.pl
75
fhem.pl
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2005-20011
|
||||
# (c) 2005-20012
|
||||
# Copyright: Rudolf Koenig (r dot koenig at koeniglich dot de)
|
||||
# All rights reserved
|
||||
#
|
||||
@@ -157,8 +157,10 @@ use vars qw($reread_active);
|
||||
my $AttrList = "room comment alias";
|
||||
|
||||
my $server; # Server socket
|
||||
my %comments; # Comments from the include files
|
||||
my $ipv6; # Using IPV6
|
||||
my $currlogfile; # logfile, without wildcards
|
||||
my $currcfgfile=""; # current config/include file
|
||||
my $logopened = 0; # logfile opened or using stdout
|
||||
my %client; # Client array
|
||||
my $rcvdquit; # Used for quit handling in init files
|
||||
@@ -306,7 +308,7 @@ setGlobalAttrBeforeFork();
|
||||
|
||||
if($^O =~ m/Win/ && !$attr{global}{nofork}) {
|
||||
Log 1, "Forcing 'attr global nofork' on WINDOWS";
|
||||
Log 1, "set it in the config file to avoud this message";
|
||||
Log 1, "set it in the config file to avoid this message";
|
||||
$attr{global}{nofork}=1;
|
||||
}
|
||||
|
||||
@@ -658,7 +660,20 @@ AnalyzeCommandChain($$)
|
||||
my ($c, $cmd) = @_;
|
||||
my @ret;
|
||||
|
||||
if($cmd =~ m/^[ \t]*(#.*)?$/) { # Save comments
|
||||
if(!$init_done) {
|
||||
if($currcfgfile ne AttrVal("global", "statefile", "")) {
|
||||
my $nr = $devcount++;
|
||||
$comments{$nr}{TEXT} = $cmd;
|
||||
$comments{$nr}{CFGFN} = $currcfgfile
|
||||
if($currcfgfile ne AttrVal("global", "configfile", ""));
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
$cmd =~ s/#.*$//s;
|
||||
|
||||
$cmd =~ s/;;/SeMiCoLoN/g;
|
||||
foreach my $subcmd (split(";", $cmd)) {
|
||||
$subcmd =~ s/SeMiCoLoN/;/g;
|
||||
@@ -834,11 +849,23 @@ CommandInclude($$)
|
||||
my ($cl, $arg) = @_;
|
||||
my $fh;
|
||||
my @ret;
|
||||
my $oldcfgfile;
|
||||
|
||||
if(!open($fh, $arg)) {
|
||||
return "Can't open $arg: $!";
|
||||
}
|
||||
|
||||
if(!$init_done &&
|
||||
$arg ne AttrVal("global", "statefile", "") &&
|
||||
$arg ne AttrVal("global", "configfile", "")) {
|
||||
my $nr = $devcount++;
|
||||
$comments{$nr}{TEXT} = "include $arg";
|
||||
$comments{$nr}{CFGFN} = $currcfgfile
|
||||
if($currcfgfile ne AttrVal("global", "configfile", ""));
|
||||
}
|
||||
$oldcfgfile = $currcfgfile;
|
||||
$currcfgfile = $arg;
|
||||
|
||||
my $bigcmd = "";
|
||||
$rcvdquit = 0;
|
||||
while(my $l = <$fh>) {
|
||||
@@ -854,6 +881,7 @@ CommandInclude($$)
|
||||
last if($rcvdquit);
|
||||
|
||||
}
|
||||
$currcfgfile = $oldcfgfile;
|
||||
close($fh);
|
||||
return join("\n", @ret) if(@ret);
|
||||
return undef;
|
||||
@@ -1012,31 +1040,58 @@ CommandSave($$)
|
||||
if(!open(SFH, ">$param")) {
|
||||
return "Cannot open $param: $!";
|
||||
}
|
||||
my %fh = ("configfile" => *SFH);
|
||||
|
||||
foreach my $d (sort { $defs{$a}{NR} <=> $defs{$b}{NR} } keys %defs) {
|
||||
next if($defs{$d}{TEMPORARY} || # e.g. WEBPGM connections
|
||||
my %devByNr;
|
||||
map { $devByNr{$defs{$_}{NR}} = $_ } keys %defs;
|
||||
|
||||
for(my $i = 0; $i < $devcount; $i++) {
|
||||
|
||||
my ($h, $d);
|
||||
if($comments{$i}) {
|
||||
$h = $comments{$i};
|
||||
|
||||
} else {
|
||||
$d = $devByNr{$i};
|
||||
next if(!defined($d) ||
|
||||
$defs{$d}{TEMPORARY} || # e.g. WEBPGM connections
|
||||
$defs{$d}{VOLATILE}); # e.g at, will be saved to the statefile
|
||||
$h = $defs{$d};
|
||||
}
|
||||
|
||||
my $cfgfile = $h->{CFGFN} ? $h->{CFGFN} : "configfile";
|
||||
my $fh = $fh{$cfgfile};
|
||||
if(!$fh) {
|
||||
return "Can't open $cfgfile: $!" if(!(open($fh, ">$cfgfile")));
|
||||
$fh{$cfgfile} = $fh;
|
||||
}
|
||||
|
||||
if(!defined($d)) {
|
||||
print $fh $h->{TEXT},"\n";
|
||||
next;
|
||||
}
|
||||
|
||||
if($d ne "global") {
|
||||
if($defs{$d}{DEF}) {
|
||||
my $def = $defs{$d}{DEF};
|
||||
$def =~ s/;/;;/g;
|
||||
print SFH "\ndefine $d $defs{$d}{TYPE} $def\n";
|
||||
print $fh "define $d $defs{$d}{TYPE} $def\n";
|
||||
} else {
|
||||
print SFH "\ndefine $d $defs{$d}{TYPE}\n";
|
||||
print $fh "define $d $defs{$d}{TYPE}\n";
|
||||
}
|
||||
}
|
||||
foreach my $a (sort keys %{$attr{$d}}) {
|
||||
next if($d eq "global" &&
|
||||
($a eq "configfile" || $a eq "version"));
|
||||
print SFH "attr $d $a $attr{$d}{$a}\n";
|
||||
print $fh "attr $d $a $attr{$d}{$a}\n";
|
||||
}
|
||||
}
|
||||
print SFH "include $attr{global}{lastinclude}\n"
|
||||
if($attr{global}{lastinclude});
|
||||
|
||||
|
||||
close(SFH);
|
||||
foreach my $fh (values %fh) {
|
||||
close($fh);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
@@ -1193,6 +1248,8 @@ CommandDefine($$)
|
||||
$hash{STATE} = "???";
|
||||
$hash{DEF} = $a[2] if(int(@a) > 2);
|
||||
$hash{NR} = $devcount++;
|
||||
$hash{CFGFN} = $currcfgfile
|
||||
if($currcfgfile ne AttrVal("global", "configfile", ""));
|
||||
|
||||
# If the device wants to issue initialization gets/sets, then it needs to be
|
||||
# in the global hash.
|
||||
|
||||
23
test/fhem-eib.cfg
Normal file
23
test/fhem-eib.cfg
Normal file
@@ -0,0 +1,23 @@
|
||||
attr global logfile -
|
||||
attr global modpath .
|
||||
attr global port 7072 global
|
||||
attr global statefile test/fhem-eib.save
|
||||
attr global verbose 5
|
||||
|
||||
define web FHEMWEB 8083 global
|
||||
attr web fwmodpath webfrontend/pgm2
|
||||
|
||||
define tul TUL eibd:marvin 1.1.249
|
||||
|
||||
define Lampe1 EIB 0/0/1
|
||||
define Lampe2 EIB 0/0/2 0/0/3
|
||||
|
||||
define LightSens EIB 0/0/250
|
||||
attr LightSens dummy 1
|
||||
attr LightSens model lightsensor
|
||||
#attr LightSens model date
|
||||
|
||||
attr Lampe1 webCmd on:off:on-for-timer 10
|
||||
|
||||
get Lampe1 value
|
||||
get Lampe2 value
|
||||
@@ -417,6 +417,7 @@ FW_AnswerCall($)
|
||||
|
||||
if($FW_inform) { # Longpoll header
|
||||
$defs{$FW_cname}{inform} = $FW_room;
|
||||
# NTFY_ORDER is larger than the normal order (50-)
|
||||
$defs{$FW_cname}{NTFY_ORDER} = $FW_cname; # else notifyfn won't be called
|
||||
my $c = $defs{$FW_cname}{CD};
|
||||
print $c "HTTP/1.1 200 OK\r\n",
|
||||
@@ -1524,8 +1525,9 @@ FW_style($$)
|
||||
binmode (FH);
|
||||
print FH $FW_data;
|
||||
close(FH);
|
||||
FW_style("style list", "Saved the file $fName");
|
||||
FW_fC("rereadcfg") if($fName eq $attr{global}{configfile});
|
||||
my $ret = FW_fC("rereadcfg") if($fName eq $attr{global}{configfile});
|
||||
$ret = ($ret ? "<h3>ERROR:</h3><b>$ret</b>" : "Saved the file $fName");
|
||||
FW_style("style list", $ret);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1814,6 +1816,7 @@ FW_Notify($$)
|
||||
my ($allSet, $cmdlist, $txt) = FW_devState($dn, "");
|
||||
($FW_wname, $FW_ME, $FW_longpoll, $FW_ss, $FW_tp) = @old;
|
||||
|
||||
# Collect multiple changes (e.g. from noties) into one message
|
||||
$ntfy->{INFORMBUF} = "" if(!defined($ntfy->{INFORMBUF}));
|
||||
$ntfy->{INFORMBUF} .= "$dn;$dev->{STATE};$txt\n";
|
||||
RemoveInternalTimer($ln);
|
||||
@@ -1827,6 +1830,7 @@ FW_FlushInform($)
|
||||
{
|
||||
my ($name) = @_;
|
||||
my $hash = $defs{$name};
|
||||
return if(!$hash);
|
||||
my $c = $hash->{CD};
|
||||
print $c $hash->{INFORMBUF};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user