29 Commits

Author SHA1 Message Date
Marc Hoppe
5ce95150df ... 2012-02-10 19:02:31 +01:00
Marc Hoppe
59c63c4f5d ... 2012-02-10 19:02:31 +01:00
Marc Hoppe
f6216d23d4 Enocean: eltakoDimmer: new Commands 'on'/'off' new attr 'dimTime' 2012-02-10 19:02:31 +01:00
Marc Hoppe
6ec7ca22a8 Enocean: eltakoDimmer: new commands 'dimup' and 'dimdown' 2012-02-10 19:01:58 +01:00
Marc Hoppe
b422b35553 EnOcean: if there is an 'attr' 'subId' use it for sending data to an Actuator
There is no subType 'dimmer' and 'dimmctrl' anymore these are replaced by 'eltakoDimmer'.
 The commands supported are 'dimto' and 'teach'
 The readings which will be set are 'state' and 'value'
2012-02-10 19:01:58 +01:00
Marc Hoppe
6b5345e8bb 00_TCM: Read Base-Id of Tcm on start 2012-02-10 19:00:12 +01:00
rudolfkoenig
1264675b48 PM101 by Ignaz
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1245 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-09 08:32:12 +00:00
rudolfkoenig
64de3f28fe Saving comments
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1244 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-08 12:41:00 +00:00
rudolfkoenig
7b2248799c desired-temp ack event name changed
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1243 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-08 08:00:46 +00:00
rudolfkoenig
edd01ab85d HM-CC-TC changes by Oskar
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1242 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-08 07:54:44 +00:00
laforce
fb5b1eece5 1. added a minor string to NetIO230B description
2. just testing, if committing works

git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1241 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-07 12:38:24 +00:00
rudolfkoenig
906ef34d77 NetIO230B by Andy
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1240 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:48:39 +00:00
rudolfkoenig
20b5a1faa2 update backup changed
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1239 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:38:15 +00:00
rudolfkoenig
d7b24fc124 Display the error message when editing the fhem.cfg
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1238 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:28:20 +00:00
rudolfkoenig
6690605865 backup fixes
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1237 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:07:27 +00:00
rudolfkoenig
56304e1e32 Undef added
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1236 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:05:59 +00:00
rudolfkoenig
8aefaa4fbf FB fixes
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1235 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-06 16:04:34 +00:00
klassm
5064c6ccd9 CUL_FHTTK fix
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1234 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-05 15:46:05 +00:00
rudolfkoenig
c3a2c11402 HM-WDS10-TH-O negative temp fix by fhem-hm-knecht
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1233 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-04 09:23:17 +00:00
to-be
3d185a8eb0 SolarView: Fixes for NightOff and Debounce
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1232 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-03 20:18:30 +00:00
klassm
aca7655c96 add triggered reading to watchdog
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1231 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-02-02 09:33:08 +00:00
rudolfkoenig
55a54847c1 logpoll fix for multiple devices
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1230 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-31 18:00:34 +00:00
hotmaz
f6c3222f62 - interpret datapoint types to show real values
- get method implemented
- send on one group, receive from several groups.


git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1229 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 23:40:24 +00:00
hotmaz
98cf85c6eb added description for TUL / EIB
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1228 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 23:39:59 +00:00
hotmaz
4331f0808d added description for TUL / EIB
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1227 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 23:36:59 +00:00
hotmaz
f3eeb3feba added description for TUL / EIB
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1226 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 23:33:25 +00:00
hotmaz
e3d5ab4337 - interpret datapoint types to show real values
- get method implemented
- send on one group, receive from several groups.


git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1225 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 21:51:38 +00:00
klassm
5608b10481 bugfix for saving the previous state of CUL_FHTTK devices
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1224 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-29 10:38:52 +00:00
klassm
d1bf6bf66f bugfix: rename previous window reading attribute, as camelcase does not work
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1223 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-28 16:37:41 +00:00
17 changed files with 1582 additions and 209 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -194,11 +194,16 @@ CUL_FHTTK_Parse($$)
}
}
my $prevState = $defs{$self}{PREV}{STATE};
if ($prevState != $state) {
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};
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();

View File

@@ -367,9 +367,9 @@ CUL_HM_Parse($$)
my $msg;
if($plist == 5) {
if($o1 eq "05") {
$msg = sprintf("windowopen-temp-%d: %.1f (sensor:%s)",
$msg = sprintf("windowopen-temp-%d: %.1f (sensor:%s)",
$tchan, $v1/2, $tdev);
}
}
}
push @event, $msg if $msg;
}
@@ -396,7 +396,7 @@ CUL_HM_Parse($$)
my $dayidx = int($idx/48);
if($idx % 4 == 0 && $dayidx < $maxdays) {
$idx -= 48*$dayidx;
$idx /= 2;
$idx /= 2;
my $ptr = $shash->{TEMPLIST}{$days[$dayidx+$dayoff]};
$ptr->{$idx}{HOUR} = int($v1/6);
$ptr->{$idx}{MINUTE} = ($v1 - int($v1/6)*6)*10;
@@ -408,25 +408,60 @@ CUL_HM_Parse($$)
}
foreach my $wd (@days) {
my $twentyfour = 0;
my $twentyfour = 0;
my $msg = sprintf("tempList%s:", $wd);
foreach(my $idx=0; $idx<24; $idx+=1) {
my $ptr = $shash->{TEMPLIST}{$wd}{$idx};
if(defined ($ptr->{TEMP}) && $ptr->{TEMP} ne "") {
if($twentyfour == 0) {
if($twentyfour == 0) {
$msg .= sprintf(" %02d:%02d %.1f",
$ptr->{HOUR}, $ptr->{MINUTE}, $ptr->{TEMP});
} else {
$ptr->{HOUR} = $ptr->{MINUTE} = $ptr->{TEMP} = "";
} else {
$ptr->{HOUR} = $ptr->{MINUTE} = $ptr->{TEMP} = "";
}
}
}
if(defined ($ptr->{HOUR}) && 0+$ptr->{HOUR} == 24) {
$twentyfour = 1; # next value uninteresting, only first counts.
}
$twentyfour = 1; # next value uninteresting, only first counts.
}
}
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);

View File

@@ -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 {
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";
# 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;

View File

@@ -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";
"subType:switch,contact,sensor,windowHandle,SR04,MD15," .
"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
@@ -176,14 +249,14 @@ EnOcean_Set($@)
my ($db_3, $status) = split(":", $ptm200btn{$c1}, 2);
$db_3 <<= 5;
$db_3 |= 0x10 if($c1 ne "released"); # set the pressed flag
if($c2) {
my ($d2, undef) = split(":", $ptm200btn{$c2}, 2);
$db_3 |= ($d2<<1) | 0x01;
}
IOWrite($hash, "",
sprintf("6B05%02X000000%s%s", $db_3, $hash->{DEF}, $status));
if($c2) {
my ($d2, undef) = split(":", $ptm200btn{$c2}, 2);
$db_3 |= ($d2<<1) | 0x01;
}
IOWrite($hash, "",
sprintf("6B05%02X000000%s%s", $db_3, $hash->{DEF}, $status));
}
}
select(undef, undef, undef, 0.1) if($i < int(@a)-1);
}
@@ -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
View 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;

View File

@@ -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);
}

View File

@@ -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,19 +336,21 @@ CommandUsb($$)
if($^O eq "linux") {
# One device at a time to avoid endless loop
my $lsusb = `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
$culType = "CUL_V2" if($lsusb =~ m/VID=03eb.PID=2ffa/s); # FritzBox
$culType = "CUL_V4" if($lsusb =~ m/03eb:2ff0/);
$culType = "CUL_V3" if($lsusb =~ m/03eb:2ff4/);
$culType = "CUL_V2" if($lsusb =~ m/03eb:2ffa/);
if($culType) {
$msg = "$culType: flash it with: CULflash none $culType";
Log 4, $msg; $ret .= $msg . "\n";
if(!$scan) {
CommandCULflash(undef, "none $culType");
sleep(4); # Leave time for linux to load th drivers
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
$culType = "CUL_V2" if($lsusb =~ m/VID=03eb.PID=2ffa/s); # FritzBox
$culType = "CUL_V4" if($lsusb =~ m/03eb:2ff0/);
$culType = "CUL_V3" if($lsusb =~ m/03eb:2ff4/);
$culType = "CUL_V2" if($lsusb =~ m/03eb:2ffa/);
if($culType) {
$msg = "$culType: flash it with: CULflash none $culType";
Log 4, $msg; $ret .= $msg . "\n";
if(!$scan) {
CommandCULflash(undef, "none $culType");
sleep(4); # Leave time for linux to load th drivers
}
}
}
}
@@ -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_TimeoutRead($hash, 0.1);
}
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) {

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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.
#
##############################################################################
#
@@ -61,15 +61,15 @@ use warnings;
use IO::Socket::INET;
my @gets = ('totalEnergyDay', # kWh
'totalEnergyMonth', # kWh
'totalEnergyYear', # kWh
'totalEnergy', # kWh
'currentPower', # W
'UDC', 'IDC', 'UDCB', # V, A, V
'IDCB', 'UDCC', 'IDCC', # A, V, A
'gridVoltage', 'gridPower', # V, A
'temperature'); # <EFBFBD>C
my @gets = ('totalEnergyDay', # kWh
'totalEnergyMonth', # kWh
'totalEnergyYear', # kWh
'totalEnergy', # kWh
'currentPower', # W
'UDC', 'IDC', 'UDCB', # V, A, V
'IDCB', 'UDCC', 'IDCC', # A, V, A
'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;

View File

@@ -82,6 +82,7 @@
<a href="#ECMD">ECMD</a> &nbsp;
<a href="#ECMDDevice">ECMDDevice</a> &nbsp;
<a href="#DS18S20">DS18S20</a> &nbsp;
<a href="#EIB">EIB</a> &nbsp;
<a href="#EnOcean">EnOcean</a> &nbsp;
<a href="#EM">EM</a> &nbsp;
<a href="#EMEM">EMEM</a> &nbsp;
@@ -104,6 +105,7 @@
<a href="#M232">M232</a> &nbsp;
<a href="#M232Counter">M232Counter</a> &nbsp;
<a href="#M232Voltage">M232Voltage</a> &nbsp;
<a href="#NetIO230B">NetIO230B</a> &nbsp;
<a href="#OREGON">OREGON</a> &nbsp;
<a href="#OWFS">OWFS</a> &nbsp;
<a href="#OWTEMP">OWTEMP</a> &nbsp;
@@ -115,6 +117,7 @@
<a href="#SIS_PMS">SIS_PMS</a> &nbsp;
<a href="#TCM">TCM</a> &nbsp;
<a href="#TellStick">TellStick</a> &nbsp;
<a href="#TUL">TUL</a> &nbsp;
<a href="#USF1000">USF1000</a> &nbsp;
<a href="#USBWX">USBWX</a> &nbsp;
<a href="#VantagePro2">VantagePro2</a> &nbsp;
@@ -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.
A folder where updatefhem will store the compressed backup file.
Note: this is a global attribute.<br>Example:
<ul>
attr global backupdir /Volumes/BigHD<br>
</ul>
</li><br>
Note: this is a global attribute, e.g.<br>
<ul>
attr global backup /Volumes/BigHD<br>
</ul>
</ul><br>
</ul>
@@ -2947,7 +2949,7 @@ A line ending with \ will be concatenated with the next one, so long lines
party-temp &lt;tmp&gt;<br>
desired-temp &lt;tmp&gt;<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 &lt;day&gt;</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 &lt;name&gt; EIB &lt;main group&gt; [&lt;additional group&gt; ..]</code>
<br><br>
Define an EIB device, connected via a <a href="#TUL">TUL</a>. The
&lt;group&gt; 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 &lt;main group&gt; 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 &lt;name&gt; &lt;value&gt; [&lt;time&gt;]</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 &lt;value&gt;</li>
<li>dimup &lt;value&gt;</li>
<li>dimdown &lt;value&gt;</li>
<li>on</li>
<li>off</li>
<li>teach</li>
</ul>
Attributes:
<ul>
<li>dimTime &lt;value&gt;<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 &nbsp;&nbsp; (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 &lt;name&gt; NetIO230B &lt;ip-address&gt; &lt;socket number(s)
&gt; [&lt;user name&gt; &lt;password&gt;]</code></li>
<li><code>define &lt;name&gt; NetIO230B &lt;ip-address&gt; &lt;socket number(s)
&gt; [&lt;config file path&gt;]</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 />
&nbsp;&nbsp;&nbsp;host => "192.168.61.40",<br />
&nbsp;&nbsp;&nbsp;user => "admin",<br />
&nbsp;&nbsp;&nbsp;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 &lt;name&gt; state</code>
<br><br>
returns the state of the socket(s)<br>
Example:
<ul>
<code>get coffeemaker_and_light</code>&nbsp;&nbsp; => <code>on or off</code><br>
</ul>
<br>
</ul>
<a name="NETIO230Bset"></a>
<b>Set </b>
<ul>
<code>set &lt;name&gt; &lt;value&gt;</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 &lt;name&gt; TUL &lt;device&gt; &lt;physical address&gt;</code> <br>
<br>
TUL usb stick / TPUART serial devices:<br><ul>
&lt;device&gt; 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>
&lt;device&gt; 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>

77
fhem.pl
View File

@@ -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
$defs{$d}{VOLATILE}); # e.g at, will be saved to the statefile
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
View 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

View File

@@ -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};