Compare commits
53 Commits
94de56a72b
...
776a4e28c5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
776a4e28c5 | ||
|
|
0c772d4224 | ||
|
|
06da7b1621 | ||
|
|
4bdf589e21 | ||
|
|
2e97eb1b45 | ||
|
|
0191306700 | ||
|
|
aeae590475 | ||
|
|
673898864c | ||
|
|
cce69b1c55 | ||
|
|
6277e551a2 | ||
|
|
618ed19f2a | ||
|
|
dd17d99373 | ||
|
|
a6e70be3c5 | ||
|
|
8a5ef48724 | ||
|
|
ec94aa34ec | ||
|
|
fb240641f7 | ||
|
|
f9553f3ec8 | ||
|
|
8d3a7853ed | ||
|
|
ee7f23de10 | ||
|
|
76b3c3a2a1 | ||
|
|
00880e422a | ||
|
|
36bddf5725 | ||
|
|
0cb0232351 | ||
|
|
2b62b6128f | ||
|
|
941263cb0e | ||
|
|
ce2cb19e5b | ||
|
|
d5983fc22e | ||
|
|
3e948fdaae | ||
|
|
dd7b32827a | ||
|
|
2f84ace39c | ||
|
|
4660d062e9 | ||
|
|
8ac3a56234 | ||
|
|
98d03d4cbf | ||
|
|
3c30fb995b | ||
|
|
b14869a491 | ||
|
|
4d20a21753 | ||
|
|
6366ccd411 | ||
|
|
eeaaaec733 | ||
|
|
1fbe536aff | ||
|
|
1305adabbd | ||
|
|
fa35186208 | ||
|
|
6bee66c4a5 | ||
|
|
1d4c6ba5bd | ||
|
|
87502bdf70 | ||
|
|
00c4f39773 | ||
|
|
12db1fa3ba | ||
|
|
5f0d12bbef | ||
|
|
3f4c2893a2 | ||
|
|
0a5f244905 | ||
|
|
f489fb1173 | ||
|
|
8be181ebad | ||
|
|
24437a97bc | ||
|
|
31ee2e5a1f |
7
CHANGED
@@ -1,6 +1,10 @@
|
||||
- SVN
|
||||
- feature: internal NotifyOrderPrefix: 98_average.pm is more straightforward
|
||||
|
||||
- feature: the usb command tries to flash unflashed CULs on linux
|
||||
- feature: FHEMWEB: jsonp support, .holiday and .cfg added to Edit Files
|
||||
- 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)
|
||||
|
||||
- 2011-12-31 (5.2)
|
||||
- bugfix: applying smallscreen attributes to firefox/opera
|
||||
@@ -35,6 +39,7 @@
|
||||
- feature: usb scan/create command added (part of autocreate).
|
||||
- feature: SaveAs added to FHEMWEB Edit-Files
|
||||
- feature: EnOcean ElTako dimmer by Marc.
|
||||
- feature: fhem is started as user fhem on the FB7390
|
||||
|
||||
|
||||
- 2011-07-08 (5.1)
|
||||
|
||||
@@ -213,6 +213,10 @@ HMLAN_Write($$$)
|
||||
$msg = sprintf("S%08X,00,00000000,01,%08X,%s",
|
||||
$tm, $tm, substr($msg, 4));
|
||||
HMLAN_SimpleWrite($hash, $msg);
|
||||
|
||||
# Avoid problems with structure set
|
||||
# TODO: rewrite it to use a queue+internaltimer like the CUL
|
||||
select(undef, undef, undef, 0.03);
|
||||
}
|
||||
|
||||
#####################################
|
||||
|
||||
@@ -12,7 +12,7 @@ package main;
|
||||
# EnOcean Serial Protocol 3 (ESP3) (for the TCM310)
|
||||
|
||||
|
||||
# TODO:
|
||||
# TODO:
|
||||
# Check BSC Temp
|
||||
# Check Stick Temp
|
||||
# Check Stick WriteRadio
|
||||
@@ -76,10 +76,16 @@ TCM_Define($$)
|
||||
$attr{$name}{dummy} = 1;
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
$hash->{DeviceName} = $dev;
|
||||
$hash->{MODEL} = $model;
|
||||
my $ret = DevIo_OpenDev($hash, 0, undef);
|
||||
|
||||
if($hash->{STATE} eq "opened") {
|
||||
my $answer=TCM_Get($hash, ($name, "baseid") );
|
||||
my @fields=split(/[=,]/, $answer);
|
||||
$hash->{BASEID}=$fields[1];
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
@@ -585,7 +591,7 @@ TCM_ReadAnswer($$)
|
||||
if($^O =~ m/Win/ && $hash->{USBDev}) {
|
||||
$hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
|
||||
# Read anstatt input sonst funzt read_const_time nicht.
|
||||
$buf = $hash->{USBDev}->read(999);
|
||||
$buf = $hash->{USBDev}->read(999);
|
||||
return ("$name Timeout reading answer for $arg", undef)
|
||||
if(length($buf) == 0);
|
||||
|
||||
|
||||
@@ -138,6 +138,8 @@ TUL_Set($@)
|
||||
my $arg = join("", @a);
|
||||
my $ll = GetLogLevel($name,3);
|
||||
|
||||
return "No $a[1] for dummies" if(IsDummy($name));
|
||||
|
||||
if($type eq "raw") {
|
||||
Log $ll, "set $name $type $arg";
|
||||
TUL_SimpleWrite($hash, $arg);
|
||||
|
||||
@@ -193,9 +193,20 @@ CUL_FHTTK_Parse($$)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
$def->{PREVTIMESTAMP} = defined($defs{$self}{PREV}{TIMESTAMP})?$defs{$self}{PREV}{TIMESTAMP}:time();
|
||||
$def->{PREVSTATE} = defined($def->{STATE})?$def->{STATE}:"Unknown";
|
||||
$defs{$self}{PREV}{STATE}=$state;
|
||||
|
||||
|
||||
|
||||
#READINGS
|
||||
my ($reading,$val) = split(/:/, $fhttfk_codes{$state});
|
||||
$defs{$self}{READINGS}{$reading}{VAL} = $val;
|
||||
|
||||
@@ -114,7 +114,7 @@ my %culHmModel=(
|
||||
"005C" => "HM-OU-CF-PL",
|
||||
"005F" => "HM-SCI-3-FM",
|
||||
"0060" => "HM-PB-4DIS-WM", # Tested
|
||||
"0061" => "HM-LC-SW4-DR",
|
||||
"0061" => "HM-LC-SW4-DR", # Tested by fhem-hm-knecht.
|
||||
"0062" => "HM-LC-SW2-DR",
|
||||
"0066" => "HM_LC_Sw4-WM", # Tested by peterp
|
||||
);
|
||||
@@ -251,7 +251,6 @@ CUL_HM_Parse($$)
|
||||
my $tn = TimeNow();
|
||||
|
||||
if($cmd eq "8002") { # Ack / Nack
|
||||
|
||||
# Multi-channel device: Switch to the shadow source hash
|
||||
my $chn = $2 if($p =~ m/^(..)(..)/);
|
||||
if($chn && $chn ne "01" && $chn ne "00") {
|
||||
@@ -275,7 +274,7 @@ CUL_HM_Parse($$)
|
||||
if($cmd ne "8002" && $shash->{cmdStack}) {
|
||||
# i have to tell something, dont care what it said
|
||||
CUL_HM_SendCmd($shash, "++8002$id${src}00",1,0) # Send Ack
|
||||
if($id eq $dst && $cmd ne "8002");
|
||||
if($id eq $dst && $p ne "00");
|
||||
CUL_HM_ProcessCmdStack($shash);
|
||||
push @event, "";
|
||||
|
||||
@@ -344,8 +343,6 @@ CUL_HM_Parse($$)
|
||||
push @event, "temperature:$t";
|
||||
push @event, "humidity:$h";
|
||||
|
||||
# If we have something to tell:
|
||||
CUL_HM_SendCmd($shash, "++A112$id$src", 1, 1) if($shash->{cmdStack});
|
||||
}
|
||||
|
||||
if($cmd eq "A258" && $p =~ m/^(..)(..)/) {
|
||||
@@ -443,6 +440,10 @@ CUL_HM_Parse($$)
|
||||
push @event, "desired-temp:" .hex($1)/2;
|
||||
}
|
||||
|
||||
if($cmd eq "A112" && $p =~ m/^0202(..)$/) { # Set desired temp
|
||||
push @event, "desired-temp:" .hex($1)/2;
|
||||
}
|
||||
|
||||
CUL_HM_SendCmd($shash, "++8002$id${src}00",1,0) # Send Ack
|
||||
if($id eq $dst && $cmd ne "8002");
|
||||
|
||||
@@ -825,8 +826,7 @@ CUL_HM_Set($@)
|
||||
my @h;
|
||||
@h = split(" ", $h) if($h);
|
||||
|
||||
my $isSender = (AttrVal($name, "hmClass", "") eq "sender" ||
|
||||
AttrVal($name, "model", "") eq "HM-CC-TC");
|
||||
my $isSender = (AttrVal($name,"hmClass","") eq "sender" || $md eq "HM-CC-TC");
|
||||
|
||||
|
||||
if(!defined($h) && defined($culHmSubTypeSets{$st}{pct}) && $cmd =~ m/^\d+/) {
|
||||
@@ -866,8 +866,8 @@ CUL_HM_Set($@)
|
||||
sprintf("++A011%s%s0400", $id,$dst));
|
||||
|
||||
} elsif($cmd eq "pair") { #############################################
|
||||
|
||||
return "pair is not enabled for this device, use set <IODev> hmPairForSec"
|
||||
return "pair is not enabled for this type of device, ".
|
||||
"use set <IODev> hmPairForSec"
|
||||
if($isSender);
|
||||
|
||||
my $serialNr = AttrVal($name, "serialNr", undef);
|
||||
@@ -941,14 +941,16 @@ CUL_HM_Set($@)
|
||||
} elsif($cmd =~ m/^desired-temp$/) { ##################
|
||||
my $temp = CUL_HM_convTemp($a[2]);
|
||||
return $temp if(length($temp) > 2);
|
||||
CUL_HM_PushCmdStack($hash, "++A112$id$dst"); # Wakeup...
|
||||
CUL_HM_PushCmdStack($hash,
|
||||
sprintf("++A011%s%s0201%s0000", $id,$dst,$temp));
|
||||
sprintf("++A011%s%s0202%s", $id,$dst,$temp));
|
||||
|
||||
} elsif($cmd =~ m/^(day|night|party)-temp$/) { ##################
|
||||
my %tt = (day=>"03", night=>"04", party=>"06");
|
||||
my $tt = $tt{$1};
|
||||
my $temp = CUL_HM_convTemp($a[2]);
|
||||
return $temp if(length($temp) > 2);
|
||||
CUL_HM_PushCmdStack($hash, "++A112$id$dst"); # Wakeup...
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, 5, "$tt$temp"); # List 5
|
||||
|
||||
} elsif($cmd =~ m/^tempList(...)/) { ##################################
|
||||
@@ -978,6 +980,7 @@ CUL_HM_Set($@)
|
||||
$hash->{TEMPLIST}{$wd}{($idx-2)/2}{TEMP} = $a[$idx+1];
|
||||
$msg .= sprintf(" %02d:%02d %.1f", $h, $m, $a[$idx+1]);
|
||||
}
|
||||
CUL_HM_PushCmdStack($hash, "++A112$id$dst"); # Wakeup...
|
||||
CUL_HM_pushConfig($hash, $id, $dst, 2, $list, $data);
|
||||
|
||||
my $vn = "tempList$wd";
|
||||
@@ -1045,7 +1048,7 @@ CUL_HM_Set($@)
|
||||
|
||||
my $dst2 = $dhash->{DEF};
|
||||
my $chn2 = "01";
|
||||
if(length($dst2) == 8) { # shadow switch device for multi-channel switch
|
||||
if(length($dst2) == 8) { # shadow switch device for multi-channel switch
|
||||
$chn2 = substr($dst2, 6, 2);
|
||||
$dst2 = substr($dst2, 0, 6);
|
||||
$dhash = $modules{CUL_HM}{defptr}{$dst2};
|
||||
@@ -1072,6 +1075,7 @@ CUL_HM_Set($@)
|
||||
}
|
||||
|
||||
$hash->{STATE} = $state if($state);
|
||||
Log GetLogLevel($name,2), "CUL_HM set $name " . join(" ", @a[1..$#a]);
|
||||
|
||||
CUL_HM_ProcessCmdStack($hash) if(!$isSender);
|
||||
return "";
|
||||
@@ -1212,6 +1216,7 @@ CUL_HM_PushCmdStack($$)
|
||||
{
|
||||
my ($hash, $cmd) = @_;
|
||||
my @arr = ();
|
||||
|
||||
$hash->{cmdStack} = \@arr if(!$hash->{cmdStack});
|
||||
push(@{$hash->{cmdStack}}, $cmd);
|
||||
}
|
||||
@@ -1454,10 +1459,16 @@ CUL_HM_encodeTime16($)
|
||||
sub
|
||||
CUL_HM_convTemp($)
|
||||
{
|
||||
my ($in) = @_;
|
||||
return "$in is not a number" if($in !~ m/^\d+$/ && $in !~ m/\d+\.\d+$/);
|
||||
return "$in is out of bounds (6 .. 30)" if($in < 6 || $in > 30);
|
||||
return sprintf("%02X", $in*2);
|
||||
my ($val) = @_;
|
||||
|
||||
my @list = map { ($_.".0", $_+0.5) } (6..30);
|
||||
pop @list;
|
||||
return "Invalid temperature $val, choose one of on off " . join(" ",@list)
|
||||
if(!($val eq "on" || $val eq "off" ||
|
||||
($val =~ m/^\d*\.?\d+$/ && $val >= 6 && $val <= 30)));
|
||||
$val = 100 if($val eq "on");
|
||||
$val = 0 if($val eq "off");
|
||||
return sprintf("%02X", $val*2);
|
||||
}
|
||||
|
||||
#############################
|
||||
|
||||
@@ -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
|
||||
@@ -67,7 +68,7 @@ EnOcean_Initialize($)
|
||||
$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 subId actualTemp";
|
||||
|
||||
for(my $i=0; $i<@ptm200btn;$i++) {
|
||||
$ptm200btn{$ptm200btn[$i]} = "$i:30";
|
||||
@@ -140,29 +141,57 @@ EnOcean_Set($@)
|
||||
$hash->{READINGS}{$cmd}{TIME} = $tn;
|
||||
$hash->{READINGS}{$cmd}{VAL} = $arg;
|
||||
|
||||
###########################
|
||||
} elsif($st eq "dimmCtrl") { # Tested for Eltako-Dimmer
|
||||
if($cmd eq "teach") {
|
||||
my $data=sprintf("A502000000%s00", $hash->{DEF});
|
||||
Log $ll2, "dimmCtrl.Teach: " . $data;
|
||||
IOWrite($hash, "000A0001", $data); # len:000a optlen:00 pakettype:1(radio)
|
||||
} elsif($st eq "eltakoDimmer") {
|
||||
if($cmd eq "teach") {
|
||||
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;
|
||||
my $dimVal=$a[1]; # for eltako relative (0-100) (but not compliant to EEP because DB0.2 is 0)
|
||||
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;
|
||||
}
|
||||
else {
|
||||
return "Unknown argument $cmd, choose one of: teach, dimm"
|
||||
}
|
||||
} elsif($cmd eq "dimto") {
|
||||
return "Usage: $cmd percent [time 01-FF FF:slowest] [on/off]" if(@a<2);
|
||||
my $time=0;
|
||||
my $onoff=1;
|
||||
# for eltako relative (0-100) (but not compliant to EEP because DB0.2 is 0)
|
||||
my $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 $idSrc=EnOcean_GetMyDeviceId($hash);
|
||||
#my $data=sprintf("A502%02X%02X%02X%s00", $dimVal, $time, $onoff|0x08, $hash->{DEF});
|
||||
my $data=sprintf("A502%02X%02X%02X%s00", $dimVal, $time, $onoff|0x08, $idSrc);
|
||||
IOWrite($hash, "000A0001", $data);
|
||||
Log $ll2, "$st.$cnd: " . $data;
|
||||
|
||||
} else {
|
||||
return "Unknown argument $cmd, choose one of: teach, dimto"
|
||||
|
||||
}
|
||||
|
||||
###########################
|
||||
|
||||
} elsif($st eq "eltakoRollCtrl") {
|
||||
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) . ") [time 0-255 sek]";
|
||||
my $rollcmd= $eltakoRollCtrlCommands{$cmd}
|
||||
return $usage if( (!defined($rollcmd)) or (@a<1) );
|
||||
my $time=0;
|
||||
if(defined($a[1])) { $time=$a[1]; } # time
|
||||
shift(@a);
|
||||
# EEP: A5/3F/7F Universal ???
|
||||
my $data=sprintf("A5%02X%02X%02X%02X%s00", 0, $time, $rollcmd, 0x08, $hash->{DEF});
|
||||
IOWrite($hash, "000A0001", $data);
|
||||
Log $ll2, "eltakoRollCtrl.$cmd" . $data;
|
||||
}
|
||||
|
||||
###########################
|
||||
} else { # Simulate a PTM
|
||||
my ($c1,$c2) = split(",", $cmd, 2);
|
||||
return "Unknown argument $cmd, choose one of " .
|
||||
@@ -173,14 +202,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);
|
||||
}
|
||||
@@ -206,7 +235,7 @@ EnOcean_Parse($$)
|
||||
return "";
|
||||
}
|
||||
|
||||
my $hash = $modules{EnOcean}{defptr}{$id};
|
||||
my $hash = $modules{EnOcean}{defptr}{$id};
|
||||
if(!$hash) {
|
||||
Log 3, "EnOcean Unknown device with ID $id, please define it";
|
||||
return "UNDEFINED EnO_${rorgname}_$id EnOcean $id";
|
||||
@@ -259,15 +288,17 @@ EnOcean_Parse($$)
|
||||
} else {
|
||||
if($st eq "keycard") {
|
||||
$msg = "keycard removed";
|
||||
|
||||
|
||||
} else {
|
||||
$msg = (($db_3&0x10) ? "pressed" : "released");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
# eltakoRoll: BI: unten / B0: oben / released:running/stopped
|
||||
|
||||
|
||||
# released events are disturbing when using a remote, since it overwrites
|
||||
# the "real" state immediately
|
||||
@@ -300,7 +331,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
|
||||
}
|
||||
@@ -341,22 +372,23 @@ EnOcean_Parse($$)
|
||||
push @event, "3:measured-temp:". sprintf "%.1f", ($db_1*40/255);
|
||||
EnOcean_MD15Cmd($hash, $name, $db_1);
|
||||
|
||||
} 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");
|
||||
push @event, "3:time:" . ($db_2<<8 + $db_1);
|
||||
push @event, "3:timeType:" . (($db_0 & 0x02) ? "delay": "duration");
|
||||
push @event, "3:state:" . (($db_0 & 0x01) ? "on": "off");
|
||||
push @event, "3:time:" . ($db_2<<8 + $db_1);
|
||||
push @event, "3:timeType:" . (($db_0 & 0x02) ? "delay": "duration");
|
||||
|
||||
} elsif($db_3 eq 0x02) { # dimm
|
||||
push @event, "3:state:" . (($db_0 & 0x01) ? "on": "off");
|
||||
push @event, "3:value:$db_2";
|
||||
|
||||
} elsif($db_3 eq 0x03) { # setpoint-switch, todo
|
||||
} elsif($db_3 eq 0x04) { # basic setpoint, todo
|
||||
} elsif($db_3 eq 0x05) { # control-variable, todo
|
||||
} elsif($db_3 eq 0x06) { # fan-stage, todo
|
||||
}
|
||||
elsif($db_3 eq 0x02) { # dimm
|
||||
push @event, "3:state:" . (($db_0 & 0x01) ? "on": "off");
|
||||
push @event, "3:dimmValue:$db_2";
|
||||
}
|
||||
elsif($db_3 eq 0x03) {} # setpoint-switch, todo
|
||||
elsif($db_3 eq 0x04) {} # basic setpoint, todo
|
||||
elsif($db_3 eq 0x05) {} # control-variable, todo
|
||||
elsif($db_3 eq 0x06) {} # fan-stage, todo
|
||||
|
||||
|
||||
} else {
|
||||
push @event, "3:state:$db_3";
|
||||
push @event, "3:sensor1:$db_3";
|
||||
@@ -396,7 +428,7 @@ EnOcean_Parse($$)
|
||||
}
|
||||
}
|
||||
$hash->{CHANGED} = \@changed;
|
||||
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
@@ -413,7 +445,7 @@ EnOcean_MD15Cmd($$$)
|
||||
$msg = sprintf("%02X000000", $arg1);
|
||||
|
||||
} elsif($cmd eq "desired-temp") {
|
||||
$msg = sprintf("%02X%02X0400", $arg1*255/40,
|
||||
$msg = sprintf("%02X%02X0400", $arg1*255/40,
|
||||
AttrVal($name, "actualTemp", ($db_1*40/255)) * 255/40);
|
||||
|
||||
} elsif($cmd eq "initialize") {
|
||||
@@ -423,7 +455,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});
|
||||
@@ -435,10 +467,29 @@ EnOcean_MD15Cmd($$$)
|
||||
sub
|
||||
EnOcean_A5Cmd($$$)
|
||||
{
|
||||
my ($hash, $msg, $org) = @_;
|
||||
IOWrite($hash, "000A0701", # varLen=0A optLen=07 msgType=01=radio,
|
||||
my ($hash, $msg, $org) = @_;
|
||||
IOWrite($hash, "000A0701", # varLen=0A optLen=07 msgType=01=radio,
|
||||
sprintf("A5%s%s0001%sFF00",$msg,$org,$hash->{DEF}));
|
||||
# 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 $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;
|
||||
|
||||
242
FHEM/10_IT.pm
Normal file
@@ -0,0 +1,242 @@
|
||||
######################################################
|
||||
# InterTechno Switch Manager as FHM-Module
|
||||
#
|
||||
# (c) Olaf Droegehorn / DHS-Computertechnik GmbH
|
||||
#
|
||||
# Published under GNU GPL License
|
||||
######################################################package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
||||
my %codes = (
|
||||
"XMIToff" => "off",
|
||||
"XMITon" => "on",
|
||||
"XMITdimup" => "dimup",
|
||||
"XMITdimdown" => "dimdown",
|
||||
"99" => "on-till",
|
||||
|
||||
);
|
||||
|
||||
my %it_c2b;
|
||||
|
||||
my $it_simple ="off on";
|
||||
my %models = (
|
||||
itremote => 'sender',
|
||||
itswitch => 'simple',
|
||||
itdimmer => 'dimmer',
|
||||
);
|
||||
|
||||
sub
|
||||
IT_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
foreach my $k (keys %codes) {
|
||||
$it_c2b{$codes{$k}} = $k;
|
||||
}
|
||||
|
||||
# $hash->{Match} = "";
|
||||
$hash->{SetFn} = "IT_Set";
|
||||
$hash->{StateFn} = "IT_SetState";
|
||||
$hash->{DefFn} = "IT_Define";
|
||||
$hash->{UndefFn} = "IT_Undef";
|
||||
# $hash->{ParseFn} = "IT_Parse";
|
||||
$hash->{AttrList} = "IODev switch_rfmode:1,0 do_not_notify:1,0 ignore:0,1 dummy:1,0 model;itremote,itswitch,itdimmer loglevel:0,1,2,3,4,5,6";
|
||||
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
IT_SetState($$$$)
|
||||
{
|
||||
my ($hash, $tim, $vt, $val) = @_;
|
||||
|
||||
$val = $1 if($val =~ m/^(.*) \d+$/);
|
||||
return "Undefined value $val" if(!defined($it_c2b{$val}));
|
||||
return undef;
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
IT_Do_On_Till($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
return "Timespec (HH:MM[:SS]) needed for the on-till command" if(@a != 3);
|
||||
|
||||
my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($a[2]);
|
||||
return $err if($err);
|
||||
|
||||
my @lt = localtime;
|
||||
my $hms_till = sprintf("%02d:%02d:%02d", $hr, $min, $sec);
|
||||
my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]);
|
||||
if($hms_now ge $hms_till) {
|
||||
Log 4, "on-till: won't switch as now ($hms_now) is later than $hms_till";
|
||||
return "";
|
||||
}
|
||||
|
||||
my @b = ($a[0], "on");
|
||||
IT_Set($hash, @b);
|
||||
my $tname = $hash->{NAME} . "_till";
|
||||
CommandDelete(undef, $tname) if($defs{$tname});
|
||||
CommandDefine(undef, "$tname at $hms_till set $a[0] off");
|
||||
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
IT_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $ret = undef;
|
||||
my $na = int(@a);
|
||||
|
||||
return "no set value specified" if($na < 2 || $na > 3);
|
||||
|
||||
my $c = $it_c2b{$a[1]};
|
||||
if(!defined($c)) {
|
||||
|
||||
# Model specific set arguments
|
||||
if(defined($attr{$a[0]}) && defined($attr{$a[0]}{"model"})) {
|
||||
my $mt = $models{$attr{$a[0]}{"model"}};
|
||||
return "Unknown argument $a[1], choose one of "
|
||||
if($mt && $mt eq "sender");
|
||||
return "Unknown argument $a[1], choose one of $it_simple"
|
||||
if($mt && $mt eq "simple");
|
||||
}
|
||||
return "Unknown argument $a[1], choose one of " .
|
||||
join(" ", sort keys %it_c2b);
|
||||
|
||||
}
|
||||
|
||||
return IT_Do_On_Till($hash, @a) if($a[1] eq "on-till");
|
||||
|
||||
return "Bad time spec" if($na == 3 && $a[2] !~ m/^\d*\.?\d+$/);
|
||||
|
||||
my $v = join(" ", @a);
|
||||
|
||||
my $message = "is".uc($hash->{XMIT}.$hash->{$c});
|
||||
my $io = $hash->{IODev};
|
||||
|
||||
## Do we need to change RFMode to SlowRF??
|
||||
if(defined($attr{$a[0]}) && defined($attr{$a[0]}{"switch_rfmode"})) {
|
||||
if ($attr{$a[0]}{"switch_rfmode"} eq "1") { # do we need to change RFMode of IODev
|
||||
my $ret = CallFn($io->{NAME}, "AttrFn", "set", ($io->{NAME}, "rfmode", "SlowRF"));
|
||||
}
|
||||
}
|
||||
|
||||
## Log that we are going to switch InterTechno
|
||||
Log GetLogLevel($a[0],2), "IT set $v";
|
||||
(undef, $v) = split(" ", $v, 2); # Not interested in the name...
|
||||
|
||||
## Send Message to IODev and wait for correct answer
|
||||
my $msg = CallFn($io->{NAME}, "GetFn", $io, (" ", "raw", $message));
|
||||
if ($msg =~ m/raw => $message/) {
|
||||
Log 4, "Answer from $io->{NAME}: $msg";
|
||||
} else {
|
||||
Log 2, "IT IODev device didn't answer is command correctly: $msg";
|
||||
}
|
||||
|
||||
## Do we need to change RFMode back to HomeMatic??
|
||||
if(defined($attr{$a[0]}) && defined($attr{$a[0]}{"switch_rfmode"})) {
|
||||
if ($attr{$a[0]}{"switch_rfmode"} eq "1") { # do we need to change RFMode of IODev
|
||||
my $ret = CallFn($io->{NAME}, "AttrFn", "set", ($io->{NAME}, "rfmode", "HomeMatic"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
##########################
|
||||
# Look for all devices with the same code, and set state, timestamp
|
||||
my $code = "$hash->{XMIT}";
|
||||
my $tn = TimeNow();
|
||||
foreach my $n (keys %{ $modules{IT}{defptr}{$code} }) {
|
||||
|
||||
my $lh = $modules{IT}{defptr}{$code}{$n};
|
||||
$lh->{CHANGED}[0] = $v;
|
||||
$lh->{STATE} = $v;
|
||||
$lh->{READINGS}{state}{TIME} = $tn;
|
||||
$lh->{READINGS}{state}{VAL} = $v;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
IT_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
my $u = "wrong syntax: define <name> IT 10-bit-housecode " .
|
||||
"off-code on-code [dimup-code] [dimdown-code]";
|
||||
|
||||
return $u if(int(@a) < 5);
|
||||
return "Define $a[0]: wrong IT-Code format: specify a 10 digits 0/1/f "
|
||||
if( ($a[2] !~ m/^[f0-1]{10}$/i) );
|
||||
|
||||
return "Define $a[0]: wrong ON format: specify a 2 digits 0/1/f "
|
||||
if( ($a[3] !~ m/^[f0-1]{2}$/i) );
|
||||
|
||||
return "Define $a[0]: wrong OFF format: specify a 2 digits 0/1/f "
|
||||
if( ($a[4] !~ m/^[f0-1]{2}$/i) );
|
||||
|
||||
my $housecode = $a[2];
|
||||
my $oncode = $a[3];
|
||||
my $offcode = $a[4];
|
||||
|
||||
$hash->{XMIT} = lc($housecode);
|
||||
$hash->{$it_c2b{"on"}} = lc($oncode);
|
||||
$hash->{$it_c2b{"off"}} = lc($offcode);
|
||||
|
||||
if (int(@a) > 5) {
|
||||
return "Define $a[0]: wrong dimup-code format: specify a 2 digits 0/1/f "
|
||||
if( ($a[5] !~ m/^[f0-1]{2}$/i) );
|
||||
$hash->{$it_c2b{"dimup"}} = lc($a[5]);
|
||||
|
||||
if (int(@a) == 7) {
|
||||
return "Define $a[0]: wrong dimdown-code format: specify a 2 digits 0/1/f "
|
||||
if( ($a[6] !~ m/^[f0-1]{2}$/i) );
|
||||
$hash->{$it_c2b{"dimdown"}} = lc($a[6]);
|
||||
}
|
||||
} else {
|
||||
$hash->{$it_c2b{"dimup"}} = "00";
|
||||
$hash->{$it_c2b{"dimdown"}} = "00";
|
||||
}
|
||||
|
||||
my $code = lc($housecode);
|
||||
my $ncode = 1;
|
||||
my $name = $a[0];
|
||||
|
||||
$hash->{CODE}{$ncode++} = $code;
|
||||
$modules{IT}{defptr}{$code}{$name} = $hash;
|
||||
|
||||
AssignIoPort($hash);
|
||||
}
|
||||
|
||||
#############################
|
||||
sub
|
||||
IT_Undef($$)
|
||||
{
|
||||
my ($hash, $name) = @_;
|
||||
|
||||
foreach my $c (keys %{ $hash->{CODE} } ) {
|
||||
$c = $hash->{CODE}{$c};
|
||||
|
||||
# As after a rename the $name my be different from the $defptr{$c}{$n}
|
||||
# we look for the hash.
|
||||
foreach my $dname (keys %{ $modules{IT}{defptr}{$c} }) {
|
||||
delete($modules{IT}{defptr}{$c}{$dname})
|
||||
if($modules{IT}{defptr}{$c}{$dname} == $hash);
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
IT_Parse($$)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -219,12 +219,16 @@ FHT_Set($@)
|
||||
|
||||
if ($cmd =~ m/-temp/) {
|
||||
|
||||
return "Invalid temperature, use NN.N" if($val !~ m/^\d*\.?\d+$/);
|
||||
return "Invalid temperature, must between 5.5 and 30.5"
|
||||
if($val < 5.5 || $val > 30.5);
|
||||
my @list = map { ($_.".0", $_+0.5) } (6..30);
|
||||
pop @list;
|
||||
return "Invalid temperature $val, choose one of on off " . join(" ",@list)
|
||||
if(!($val eq "on" || $val eq "off" ||
|
||||
($val =~ m/^\d*\.?\d+$/ && $val >= 5.5 && $val <= 30.5)));
|
||||
|
||||
$val = 30.5 if($val eq "on");
|
||||
$val = 5.5 if($val eq "off");
|
||||
my $a = int($val*2);
|
||||
$arg .= sprintf("%02x", $a);
|
||||
$ret .= sprintf("Rounded temperature to %.1f", $a/2) if($a/2 != $val);
|
||||
$val = sprintf("%.1f", $a/2);
|
||||
|
||||
} elsif($cmd =~ m/-from/ || $cmd =~ m/-to/) {
|
||||
@@ -239,7 +243,7 @@ FHT_Set($@)
|
||||
|
||||
} elsif($cmd eq "mode") {
|
||||
|
||||
return "Invalid mode, use one of " . join(" ", sort keys %m2c)
|
||||
return "Invalid mode, choose one of " . join(" ", sort keys %m2c)
|
||||
if(!defined($m2c{$val}));
|
||||
$arg .= sprintf("%02x", $m2c{$val});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ CUL_TX_Parse($$)
|
||||
{
|
||||
my ($hash, $msg) = @_;
|
||||
$msg = substr($msg, 1);
|
||||
# Msg format: taTHHXYZXY, see http://www.f6fbb.org/domo/sensors/tx3_th.php
|
||||
# Msg format: TXTHHXYZXY, see http://www.f6fbb.org/domo/sensors/tx3_th.php
|
||||
my @a = split("", $msg);
|
||||
my $id2 = hex($a[4]) & 1; #meaning unknown
|
||||
my $id3 = (hex($a[3])<<3) + (hex($a[4])>>1);
|
||||
@@ -79,36 +79,51 @@ CUL_TX_Parse($$)
|
||||
my $ll4 = GetLogLevel($name,4);
|
||||
Log $ll4, "CUL_TX $name $id3 ($msg)";
|
||||
|
||||
my ($devtype, $val, $no);
|
||||
my ($msgtype, $val, $changedTxt);
|
||||
my $valraw = ($a[5].$a[6].".".$a[7]);
|
||||
my $type = $a[2];
|
||||
if($type eq "0") {
|
||||
$devtype = "temperature";
|
||||
$val = sprintf("%2.1f", ($valraw - 50 + $def->{corr}) );
|
||||
Log $ll4, "CUL_TX $devtype $name $id3 T: $val F: $id2";
|
||||
$no = "temperature: $val";
|
||||
$msgtype = "temperature";
|
||||
$val = sprintf("%2.1f", ($valraw - 50 + $def->{corr}) );
|
||||
Log $ll4, "CUL_TX $msgtype $name $id3 T: $val F: $id2";
|
||||
$changedTxt = "temperature: $val";
|
||||
|
||||
} elsif ($type eq "E") {
|
||||
$devtype = "humidity";
|
||||
$val = $valraw;
|
||||
Log $ll4, "CUL_TX $devtype $name $id3 H: $val F: $id2";
|
||||
$no = "humidity: $val";
|
||||
$msgtype = "humidity";
|
||||
$val = $valraw;
|
||||
Log $ll4, "CUL_TX $msgtype $name $id3 H: $val F: $id2";
|
||||
$changedTxt = "humidity: $val";
|
||||
|
||||
} else {
|
||||
my $ll2 = GetLogLevel($name,4);
|
||||
Log $ll2, "CUL_TX $type $name $id3 ($msg) unknown type";
|
||||
return "";
|
||||
my $ll2 = GetLogLevel($name,4);
|
||||
Log $ll2, "CUL_TX $type $name $id3 ($msg) unknown type";
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
|
||||
my $state="";
|
||||
my $t = ReadingsVal($name, "temperature", undef);
|
||||
my $h = ReadingsVal($name, "humidity", undef);
|
||||
if(defined($t) && defined($h)) {
|
||||
$state="T: $t H: $h";
|
||||
|
||||
} elsif(defined($t)) {
|
||||
$state="T: $t";
|
||||
|
||||
} elsif(defined($h)) {
|
||||
$state="H: $h";
|
||||
|
||||
}
|
||||
|
||||
my $tn = TimeNow();
|
||||
$def->{STATE} = $no;
|
||||
$def->{STATE} = $state;
|
||||
$def->{READINGS}{state}{TIME} = $tn;
|
||||
$def->{READINGS}{state}{VAL} = $val;
|
||||
$def->{CHANGED}[0] = $no;
|
||||
$def->{READINGS}{state}{VAL} = $state;
|
||||
$def->{CHANGED}[0] = $changedTxt;
|
||||
|
||||
$def->{READINGS}{$devtype}{VAL} = $val;
|
||||
$def->{READINGS}{$devtype}{TIME} = $tn;
|
||||
$def->{READINGS}{$msgtype}{VAL} = $val;
|
||||
$def->{READINGS}{$msgtype}{TIME} = $tn;
|
||||
|
||||
DoTrigger($name, undef) if($init_done);
|
||||
return $name;
|
||||
|
||||
@@ -44,7 +44,7 @@ SIS_PMS_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{Match} = "^socket ..:..:..:..:.. . state o.*";
|
||||
$hash->{Match} = "^socket ..:..:..:..:.. .+ state o.*";
|
||||
$hash->{SetFn} = "SIS_PMS_Set";
|
||||
# $hash->{StateFn} = "SIS_PMS_SetState";
|
||||
$hash->{DefFn} = "SIS_PMS_Define";
|
||||
@@ -141,7 +141,12 @@ SIS_PMS_Parse($$)
|
||||
|
||||
$devname =~ s/:/_/g;
|
||||
Log 3, "SIS_PMS Unknown device $serial $socknr, please define it";
|
||||
return "UNDEFINED SIS_PMS_$devname.$socknr SIS_PMS $serial $socknr";
|
||||
if(defined($hash->{TMPLABEL})) {
|
||||
$devname=$hash->{TMPLABEL};
|
||||
return "UNDEFINED $devname SIS_PMS $serial $socknr";
|
||||
} else {
|
||||
return "UNDEFINED SIS_PMS_$devname.$socknr SIS_PMS $serial $socknr";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,13 +60,10 @@ sub Weather_UpdateReading($$$$$$) {
|
||||
$r->{$reading}{VAL} = $value;
|
||||
|
||||
my $name= $hash->{NAME};
|
||||
Log 1, "Weather $name: $reading= $value";
|
||||
# Log 1, "Weather $name: $reading= $value";
|
||||
|
||||
#if(!$hash->{LOCAL}) {
|
||||
DoTrigger($name, ReadingsVal($name, $reading, "")); # if($init_done);
|
||||
#}
|
||||
$hash->{CHANGED}[$n]= "$reading: $value";
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -107,6 +104,7 @@ sub Weather_GetUpdate($)
|
||||
foreach my $condition ( keys ( %$current ) ) {
|
||||
my $value= $current->{$condition};
|
||||
Weather_UpdateReading($hash,"",$condition,$tn,$value,$n);
|
||||
$n++;
|
||||
}
|
||||
|
||||
my $fci= $WeatherObj->forecast_information;
|
||||
@@ -114,6 +112,7 @@ sub Weather_GetUpdate($)
|
||||
my $reading= $i;
|
||||
my $value= $fci->{$i};
|
||||
Weather_UpdateReading($hash,"",$i,$tn,$value,$n);
|
||||
$n++;
|
||||
}
|
||||
|
||||
for(my $t= 0; $t<= 3; $t++) {
|
||||
@@ -122,9 +121,15 @@ sub Weather_GetUpdate($)
|
||||
foreach my $condition ( keys ( %$fcc ) ) {
|
||||
my $value= $fcc->{$condition};
|
||||
Weather_UpdateReading($hash,$prefix,$condition,$tn,$value,$n);
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$hash->{LOCAL}) {
|
||||
DoTrigger($name, undef) if($init_done);
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ SISPM_Initialize($)
|
||||
$hash->{Clients} =
|
||||
":SIS_PMS:";
|
||||
my %mc = (
|
||||
"1:SIS_PMS" => "^socket ..:..:..:..:.. . state o.*",
|
||||
"1:SIS_PMS" => "^socket ..:..:..:..:.. .+ state o.*",
|
||||
);
|
||||
$hash->{MatchList} = \%mc;
|
||||
$hash->{AttrList}= "model:SISPM loglevel:0,1,2,3,4,5,6";
|
||||
@@ -111,6 +111,7 @@ SISPM_GetCurrentConfig($)
|
||||
return "Can't start $tmpdev: $!";
|
||||
}
|
||||
|
||||
my $tmpnr=-1;
|
||||
local $_;
|
||||
while (<$FH>) {
|
||||
if(/^(No GEMBIRD SiS-PM found.)/) {
|
||||
@@ -124,6 +125,16 @@ SISPM_GetCurrentConfig($)
|
||||
$numdetected++;
|
||||
$hash->{NUMUNITS}=$numdetected;
|
||||
}
|
||||
if(/^Gembird #(\d+)$/) {
|
||||
Log 3, "SISPM_GetCurrentConfig: Found SISPM device number $1 (sispmctl v3)";
|
||||
$currentdevice=$1;
|
||||
$numdetected++;
|
||||
$hash->{NUMUNITS}=$numdetected;
|
||||
}
|
||||
if(/^USB information: bus .*, device (\d+)/) {
|
||||
Log 3, "SISPM_GetCurrentConfig: SISPM device number $currentdevice is USB device $1 (sispmctl v3)";
|
||||
$hash->{UNITS}{$currentdevice}{USB}=$1;
|
||||
}
|
||||
|
||||
if(/^This device has a serial number of (.*)/) {
|
||||
my $serial=$1;
|
||||
@@ -135,14 +146,25 @@ SISPM_GetCurrentConfig($)
|
||||
$hash->{UNITS}{$currentdevice}{SERIAL}=$serial;
|
||||
$hash->{SERIALS}{$serial}{UNIT}=$currentdevice;
|
||||
$hash->{SERIALS}{$serial}{USB}=$hash->{UNITS}{$currentdevice}{USB};
|
||||
}
|
||||
if(/^serial number:\s+(.*)/) { # sispmctl v3
|
||||
my $serial=$1;
|
||||
Log 3, "SISPM_GetCurrentConfig: Device number " . $currentdevice . " has serial $serial (sispmctl v3)";
|
||||
if(length($serial)!=length("..:..:..:..:..")){
|
||||
$serial = FixSISPMSerial($serial);
|
||||
Log 3, "SISPM_GetCurrentConfig: Whoopsi, weird serial format; fixing to $serial.";
|
||||
}
|
||||
$hash->{UNITS}{$currentdevice}{SERIAL}=$serial;
|
||||
$hash->{SERIALS}{$serial}{UNIT}=$currentdevice;
|
||||
$hash->{SERIALS}{$serial}{USB}=$hash->{UNITS}{$currentdevice}{USB};
|
||||
}
|
||||
}
|
||||
close($FH);
|
||||
Log 3, "SISPM_GetCurrentConfig: Initial read done";
|
||||
|
||||
if ($numdetected==0) {
|
||||
Log 3, "SISPM_GetCurrentConfig: No SIMPM devices found.";
|
||||
return "no SIMPM devices found.";
|
||||
Log 3, "SISPM_GetCurrentConfig: No SISPM devices found.";
|
||||
return "no SISPM devices found.";
|
||||
}
|
||||
|
||||
$hash->{NUMUNITS} = $numdetected;
|
||||
@@ -178,7 +200,7 @@ SISPM_Define($$)
|
||||
}
|
||||
|
||||
if($hash->{NUMUNITS} < 1) {
|
||||
return "SISPM no SIMPM devices found.";
|
||||
return "SISPM no SISPM devices found.";
|
||||
}
|
||||
|
||||
$hash->{Timer} = 30;
|
||||
@@ -286,6 +308,7 @@ SISPM_Read($)
|
||||
my $currentusbid=0;
|
||||
my $renumbered=0;
|
||||
my $newPMfound=0;
|
||||
my $tmpnr=-1;
|
||||
|
||||
($eof, @lines) = nonblockGetLinesSISPM($FH);
|
||||
|
||||
@@ -301,6 +324,7 @@ SISPM_Read($)
|
||||
if($eof != 1) {
|
||||
foreach my $inputline ( @lines ) {
|
||||
$inputline =~ s/\s+$//;
|
||||
Log 5, "SISPM_Read: read /$inputline/";
|
||||
|
||||
# wusel, 2010-01-16: Seems as if reading not always works as expected;
|
||||
# throw away the whole readings if there's a NULL
|
||||
@@ -351,7 +375,35 @@ SISPM_Read($)
|
||||
}
|
||||
}
|
||||
|
||||
if($inputline =~ /^This device has a serial number of (.*)/) {
|
||||
# New for SiS PM Control for Linux 3.1
|
||||
if($inputline =~ /^Gembird #(\d+)$/) {
|
||||
Log 5, "SISPM found SISPM device number $1 (sispmctl v3)";
|
||||
$tmpnr=$1;
|
||||
}
|
||||
if($tmpnr >= 0 && $inputline =~ /^USB information: bus 001, device (\d+)/) {
|
||||
Log 5, "SISPM found SISPM device number $tmpnr as USB $1";
|
||||
if($tmpnr < $hash->{NUMUNITS}) {
|
||||
if($hash->{UNITS}{$tmpnr}{USB}!=$1) {
|
||||
Log 3, "SISPM: USB ids changed (unit $tmpnr is now USB $1 but was " . $hash->{UNITS}{$tmpnr}{USB} . "); will fix.";
|
||||
$renumbered=1;
|
||||
$hash->{FIXRENUMBER}="yes";
|
||||
}
|
||||
} else { # Something wonderful has happened, we have a new SIS PM!
|
||||
Log 3, "SISPM: Wuuuhn! Found a new unit $tmpnr as USB $1 with sispmctl v3. Will assimilate it.";
|
||||
$newPMfound=1;
|
||||
$hash->{FIXNEW}="yes";
|
||||
}
|
||||
$currentdevice=$tmpnr;
|
||||
$currentusbid=$1;
|
||||
$currentserial="none";
|
||||
if(defined($hash->{UNITS}{$currentdevice}{SERIAL})) {
|
||||
$currentserial=$hash->{UNITS}{$currentdevice}{SERIAL};
|
||||
}
|
||||
$tmpnr=-1;
|
||||
}
|
||||
|
||||
if($inputline =~ /^This device has a serial number of (.*)/ ||
|
||||
$inputline =~ /^serial number:\s+(.*)/) {
|
||||
$currentserial=FixSISPMSerial($1);
|
||||
if($currentserial eq "00:00:00:00:00") {
|
||||
Log 3, "SISPM Whooopsie! Your serial nullified ($currentserial). Skipping ...";
|
||||
|
||||
333
FHEM/70_TellStick.pm
Normal file
@@ -0,0 +1,333 @@
|
||||
################################################################
|
||||
#
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2012 Copyright: Kai 'wusel' Siering (wusel+fhem at uu dot org)
|
||||
# All rights reserved
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||
#
|
||||
# This script is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
###############################################
|
||||
|
||||
###########################
|
||||
# 70_TellStick.pm
|
||||
# Module for FHEM
|
||||
#
|
||||
# Contributed by Kai 'wusel' Siering <wusel+fhem@uu.org> in 2012
|
||||
# Based in part on work for FHEM by other authors ...
|
||||
# $Id: 70_TellStick.pm 1 2011-11-12 07:51:08Z real-wusel $
|
||||
###########################
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
# Consumer
|
||||
$hash->{DefFn} = "TellStick_Define";
|
||||
$hash->{Clients} =
|
||||
":SIS_PMS:";
|
||||
my %mc = (
|
||||
"1:SIS_PMS" => "^socket ..:..:..:..:.. .+ state o.*",
|
||||
);
|
||||
$hash->{MatchList} = \%mc;
|
||||
$hash->{AttrList}= "loglevel:0,1,2,3,4,5,6";
|
||||
$hash->{ReadFn} = "TellStick_Read";
|
||||
$hash->{WriteFn} = "TellStick_Write";
|
||||
$hash->{UndefFn} = "TellStick_Undef";
|
||||
}
|
||||
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_GetCurrentConfig($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $numdetected=0;
|
||||
my $currentdevice=0;
|
||||
my $FH;
|
||||
my $i;
|
||||
my $dev = sprintf("%s", $hash->{DeviceName});
|
||||
|
||||
Log 3, "TellStick_GetCurrentConfig: Using \"$dev\" as parameter to open(); trying ...";
|
||||
|
||||
my $tmpdev=sprintf("%s --list 2>&1 |", $dev);
|
||||
open($FH, $tmpdev);
|
||||
if(!$FH) {
|
||||
Log 3, "TellStick_GetCurrentConfig: Can't start $tmpdev: $!";
|
||||
return "Can't start $tmpdev: $!";
|
||||
}
|
||||
|
||||
local $_;
|
||||
while (<$FH>) {
|
||||
my $msg=<$FH>;
|
||||
chomp($msg);
|
||||
my ($devid, $name, $state) = split('\t', $msg);
|
||||
Log 3, "TellStick_GetCurrentConfig: read: /$devid/$name/$state/";
|
||||
|
||||
if(defined($devid) && defined($name) && defined($state)) {
|
||||
$numdetected++;
|
||||
Log 3, "TellStick_GetCurrentConfig: $devid $name $state";
|
||||
}
|
||||
}
|
||||
close($FH);
|
||||
Log 3, "TellStick_GetCurrentConfig: Initial read done, $numdetected devices found";
|
||||
|
||||
if ($numdetected==0) {
|
||||
Log 3, "TellStick_GetCurrentConfig: No TellStick devices found.";
|
||||
return "no TellStick or configured devices found.";
|
||||
}
|
||||
|
||||
$hash->{NUMDEVS} = $numdetected;
|
||||
$hash->{STATE} = "initialized";
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
my $numdetected=0;
|
||||
my $currentdevice=0;
|
||||
my $retval;
|
||||
|
||||
return "Define the /path/to/tdtool as a parameter" if(@a != 3);
|
||||
|
||||
my $FH;
|
||||
my $dev = sprintf("%s", $a[2]);
|
||||
$hash->{DeviceName} = $dev;
|
||||
Log 3, "TellStick using \"$dev\" as parameter to open(); trying ...";
|
||||
|
||||
$retval=TellStick_GetCurrentConfig($hash);
|
||||
|
||||
Log 3, "TellStick GetCurrentConfing done";
|
||||
|
||||
if(defined($retval)) {
|
||||
Log 3, "TellStick: An error occured: $retval";
|
||||
return $retval;
|
||||
}
|
||||
|
||||
if($hash->{NUMDEVS} < 1) {
|
||||
return "TellStick no configured devices found.";
|
||||
}
|
||||
|
||||
$hash->{Timer} = 30;
|
||||
|
||||
Log 3, "TellStick setting callback timer";
|
||||
|
||||
my $oid = $init_done;
|
||||
$init_done = 1;
|
||||
InternalTimer(gettimeofday() + 10, "TellStick_GetStatus", $hash, 1);
|
||||
$init_done = $oid;
|
||||
|
||||
Log 3, "TellStick initialized";
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_Undef($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
if(defined($hash->{FD})) {
|
||||
close($hash->{FD});
|
||||
delete $hash->{FD};
|
||||
}
|
||||
delete $selectlist{"$name.pipe"};
|
||||
|
||||
$hash->{STATE}='undefined';
|
||||
Log 3, "$name shutdown complete";
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_GetStatus($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $dnr = $hash->{DEVNR};
|
||||
my $name = $hash->{NAME};
|
||||
my $dev = $hash->{DeviceName};
|
||||
my $FH;
|
||||
my $i;
|
||||
|
||||
Log 4, "TellStick contacting device";
|
||||
|
||||
my $tmpdev=sprintf("%s --list", $dev);
|
||||
|
||||
$tmpdev=sprintf("%s 2>&1 |", $tmpdev);
|
||||
open($FH, $tmpdev);
|
||||
if(!$FH) {
|
||||
return "TellStick Can't open pipe: $dev: $!";
|
||||
}
|
||||
|
||||
$hash->{FD}=$FH;
|
||||
$selectlist{"$name.pipe"} = $hash;
|
||||
Log 4, "TellStick pipe opened";
|
||||
$hash->{STATE} = "reading";
|
||||
$hash->{pipeopentime} = time();
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
TellStick_Read($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $dnr = $hash->{DEVNR};
|
||||
my $name = $hash->{NAME};
|
||||
my $dev = $hash->{DeviceName};
|
||||
my $FH;
|
||||
my $inputline;
|
||||
|
||||
Log 4, "TellStick Read entered";
|
||||
|
||||
if(!defined($hash->{FD})) {
|
||||
Log 3, "Oops, TellStick FD undef'd";
|
||||
return undef;
|
||||
}
|
||||
if(!$hash->{FD}) {
|
||||
Log 3, "Oops, TellStick FD empty";
|
||||
return undef;
|
||||
}
|
||||
$FH = $hash->{FD};
|
||||
|
||||
Log 4, "TellStick reading started";
|
||||
|
||||
my @lines;
|
||||
my $eof;
|
||||
my $i=0;
|
||||
my $tn = TimeNow();
|
||||
my $reading;
|
||||
my $readingforstatus;
|
||||
|
||||
($eof, @lines) = nonblockGetLinesTellStick($FH);
|
||||
|
||||
if(!defined($eof)) {
|
||||
Log 4, "TellStick FIXME: eof undefined?!";
|
||||
$eof=0;
|
||||
}
|
||||
Log 4, "TellStick reading ended with eof==$eof";
|
||||
|
||||
# FIXME! Current observed behaviour is "would block", then read of only EOF.
|
||||
# Not sure if it's always that way; more correct would be checking
|
||||
# for empty $inputline or undef'd $rawreading,$val. -wusel, 2010-01-04
|
||||
# UPDATE: Seems to work so far, so I'll re-use this as-is ;) -wusel, 2012-01-21
|
||||
if($eof != 1) {
|
||||
foreach my $inputline ( @lines ) {
|
||||
Log 5, "TellStick read: $inputline";
|
||||
chomp($inputline);
|
||||
my ($devid, $name, $state) = split('\t', $inputline);
|
||||
if(defined($devid) && defined($name) && defined($state)) {
|
||||
$state=lc($state);
|
||||
my $dmsg = sprintf("socket te:ll:st:ck:01 %d state %s", $devid, $state);
|
||||
$name =~ s/\W/_/;
|
||||
$hash->{TMPLABEL} = $name;
|
||||
Dispatch($hash, $dmsg, undef);
|
||||
} else {
|
||||
Log 4, "TellStick line /$inputline/ ignored";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($eof) {
|
||||
close($FH);
|
||||
delete $hash->{FD};
|
||||
delete $selectlist{"$name.pipe"};
|
||||
undef($hash->{TMPLABEL});
|
||||
# InternalTimer(gettimeofday()+ $hash->{Timer}, "TellStick_GetStatus", $hash, 1);
|
||||
$hash->{STATE} = "read";
|
||||
Log 4, "TellStick done reading pipe";
|
||||
} else {
|
||||
$hash->{STATE} = "reading";
|
||||
Log 4, "TellStick (further) reading would block";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub TellStick_Write($$$) {
|
||||
my ($hash,$fn,$msg) = @_;
|
||||
my $dev = $hash->{DeviceName};
|
||||
|
||||
my ($serial, $devid, $what) = split(' ', $msg);
|
||||
Log 4, "TellStick_Write entered for $hash->{NAME}: $serial, $devid, $what";
|
||||
|
||||
my $cmdline;
|
||||
my $cmdletter="l";
|
||||
|
||||
if($what eq "on") {
|
||||
$cmdletter="n";
|
||||
} elsif($what eq "off") {
|
||||
$cmdletter="f";
|
||||
}
|
||||
|
||||
$cmdline=sprintf("%s -%s %d 2>&1 >/dev/null", $dev, $cmdletter, $devid);
|
||||
system($cmdline);
|
||||
Log 4, "TellStick_Write executed $cmdline";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
# From http://www.perlmonks.org/?node_id=713384 / http://davesource.com/Solutions/20080924.Perl-Non-blocking-Read-On-Pipes-Or-Files.html
|
||||
#
|
||||
# Used, hopefully, with permission ;)
|
||||
#
|
||||
# An non-blocking filehandle read that returns an array of lines read
|
||||
# Returns: ($eof,@lines)
|
||||
my %nonblockGetLines_lastTellStick;
|
||||
sub nonblockGetLinesTellStick {
|
||||
my ($fh,$timeout) = @_;
|
||||
|
||||
$timeout = 0 unless defined $timeout;
|
||||
my $rfd = '';
|
||||
$nonblockGetLines_lastTellStick{$fh} = ''
|
||||
unless defined $nonblockGetLines_lastTellStick{$fh};
|
||||
|
||||
vec($rfd,fileno($fh),1) = 1;
|
||||
return unless select($rfd, undef, undef, $timeout)>=0;
|
||||
# I'm not sure the following is necessary?
|
||||
return unless vec($rfd,fileno($fh),1);
|
||||
my $buf = '';
|
||||
my $n = sysread($fh,$buf,1024*1024);
|
||||
# If we're done, make sure to send the last unfinished line
|
||||
return (1,$nonblockGetLines_lastTellStick{$fh}) unless $n;
|
||||
# Prepend the last unfinished line
|
||||
$buf = $nonblockGetLines_lastTellStick{$fh}.$buf;
|
||||
# And save any newly unfinished lines
|
||||
$nonblockGetLines_lastTellStick{$fh} =
|
||||
(substr($buf,-1) !~ /[\r\n]/ && $buf =~ s/([^\r\n]*)$//)
|
||||
? $1 : '';
|
||||
$buf ? (0,split(/\n/,$buf)) : (0);
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
@@ -54,6 +54,7 @@ notify_Exec($$)
|
||||
|
||||
my $n = $dev->{NAME};
|
||||
my $re = $ntfy->{REGEXP};
|
||||
return if(!$dev->{CHANGED}); # Some previous notify deleted the array.
|
||||
my $max = int(@{$dev->{CHANGED}});
|
||||
my $t = $dev->{TYPE};
|
||||
|
||||
|
||||
@@ -62,6 +62,29 @@ FileLog_Undef($$)
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
FileLog_Switch($)
|
||||
{
|
||||
my ($log) = @_;
|
||||
|
||||
my $fh = $log->{FH};
|
||||
my @t = localtime;
|
||||
my $cn = ResolveDateWildcards($log->{logfile}, @t);
|
||||
|
||||
if($cn ne $log->{currentlogfile}) { # New logfile
|
||||
$fh->close();
|
||||
HandleArchiving($log);
|
||||
$fh = new IO::File ">>$cn";
|
||||
if(!defined($fh)) {
|
||||
Log(0, "Can't open $cn");
|
||||
return;
|
||||
}
|
||||
$log->{currentlogfile} = $cn;
|
||||
$log->{FH} = $fh;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
FileLog_Log($$)
|
||||
@@ -81,24 +104,11 @@ FileLog_Log($$)
|
||||
if($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/) {
|
||||
my $t = TimeNow();
|
||||
$t = $dev->{CHANGETIME}[$i] if(defined($dev->{CHANGETIME}[$i]));
|
||||
$t =~ s/ /_/o; # Makes it easier to parse with gnuplot
|
||||
$t =~ s/ /_/; # Makes it easier to parse with gnuplot
|
||||
|
||||
FileLog_Switch($log);
|
||||
|
||||
my $fh = $log->{FH};
|
||||
my @t = localtime;
|
||||
my $cn = ResolveDateWildcards($log->{logfile}, @t);
|
||||
|
||||
if($cn ne $log->{currentlogfile}) { # New logfile
|
||||
$fh->close();
|
||||
HandleArchiving($log);
|
||||
$fh = new IO::File ">>$cn";
|
||||
if(!defined($fh)) {
|
||||
Log(0, "Can't open $cn");
|
||||
return;
|
||||
}
|
||||
$log->{currentlogfile} = $cn;
|
||||
$log->{FH} = $fh;
|
||||
}
|
||||
|
||||
print $fh "$t $n $s\n";
|
||||
$fh->flush;
|
||||
$fh->sync if !($^O eq 'MSWin32'); #not implemented in Windows
|
||||
@@ -179,12 +189,14 @@ FileLog_Get($@)
|
||||
$internal = 1;
|
||||
}
|
||||
|
||||
FileLog_Switch($hash);
|
||||
|
||||
if($inf eq "-") {
|
||||
$inf = $hash->{currentlogfile};
|
||||
} else {
|
||||
my $linf = "$1/$inf" if($hash->{currentlogfile} =~ m,^(.*)/[^/]*$,o);
|
||||
if(!-f $linf) {
|
||||
$linf = AttrValue($hash->{NAME},"archivedir",".") ."/". $inf;
|
||||
$linf = AttrVal($hash->{NAME},"archivedir",".") ."/". $inf;
|
||||
return "Error: cannot access $linf" if(!-f $linf);
|
||||
}
|
||||
$inf = $linf;
|
||||
|
||||
150
FHEM/98_WOL.pm
Normal file
@@ -0,0 +1,150 @@
|
||||
##############################################
|
||||
# $Id: 99_WOL.pm 1116 2012-01-21 15:01:34Z matthiasklass $
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use IO::Socket;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
sub
|
||||
WOL_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{SetFn} = "WOL_Set";
|
||||
$hash->{DefFn} = "WOL_Define";
|
||||
$hash->{UndefFn} = "WOL_Undef";
|
||||
$hash->{AttrList} = "loglevel:0,1,2,3,4,5,6";
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
WOL_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "no set value specified" if(int(@a) < 2);
|
||||
return "Unknown argument $a[1], choose one of refresh on" if($a[1] eq "?");
|
||||
|
||||
|
||||
my $name = shift @a;
|
||||
my $v = join(" ", @a);
|
||||
my $logLevel = GetLogLevel($name,2);
|
||||
|
||||
Log $logLevel, "WOL set $name $v";
|
||||
|
||||
if($v eq "on")
|
||||
{
|
||||
eval {
|
||||
for(my $i = 1; $i <= 3; $i++) {
|
||||
wake($hash, $logLevel);
|
||||
}
|
||||
};
|
||||
if ($@){
|
||||
### catch block
|
||||
Log $logLevel, "WOL error: $@";
|
||||
};
|
||||
Log $logLevel, "WOL waking $name";
|
||||
|
||||
} elsif ($v eq "refresh")
|
||||
{
|
||||
WOL_GetUpdate($hash);
|
||||
} else
|
||||
{
|
||||
return "unknown argument $v, choose one of refresh, on";
|
||||
}
|
||||
|
||||
$hash->{CHANGED}[0] = $v;
|
||||
$hash->{STATE} = $v;
|
||||
$hash->{READINGS}{state}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{state}{VAL} = $v;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
WOL_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
my $u = "wrong syntax: define <name> WOL MAC_ADRESS IP";
|
||||
return $u if(int(@a) < 4);
|
||||
|
||||
$hash->{MAC} = $a[2];
|
||||
$hash->{IP} = $a[3];
|
||||
$hash->{INTERVAL} = 600;
|
||||
|
||||
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_GetUpdate", $hash, 0);
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub WOL_Undef($$) {
|
||||
|
||||
my ($hash, $arg) = @_;
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub WOL_GetUpdate($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
my $ip = $hash->{IP};
|
||||
#if (system("ping -q -c 1 $ip > /dev/null") == 0)
|
||||
if (`ping -c 1 $ip` =~ m/100/)
|
||||
{
|
||||
$hash->{READINGS}{state}{VAL} = "off";
|
||||
$hash->{READINGS}{isRunning}{VAL} = "false";
|
||||
} else
|
||||
{
|
||||
$hash->{READINGS}{state}{VAL} = "on";
|
||||
$hash->{READINGS}{isRunning}{VAL} = "true";
|
||||
}
|
||||
$hash->{READINGS}{state}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{isRunning}{TIME} = TimeNow();
|
||||
|
||||
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_GetUpdate", $hash, 0);
|
||||
}
|
||||
|
||||
sub wake
|
||||
{
|
||||
my ($hash, $logLevel) = @_;
|
||||
my $mac = $hash->{MAC};
|
||||
|
||||
Log $logLevel, "trying to wake $mac";
|
||||
|
||||
my $response = `/usr/bin/ether-wake $mac`;
|
||||
Log $logLevel, "trying etherwake with response: $response";
|
||||
|
||||
wol_by_udp($mac);
|
||||
Log $logLevel, "trying direct socket via UDP";
|
||||
}
|
||||
|
||||
# method to wake via lan, taken from Net::Wake package
|
||||
sub wol_by_udp {
|
||||
my ($mac_addr, $host, $port) = @_;
|
||||
|
||||
# use the discard service if $port not passed in
|
||||
if (! defined $host) { $host = '255.255.255.255' }
|
||||
if (! defined $port || $port !~ /^\d+$/ ) { $port = 9 }
|
||||
|
||||
my $sock = new IO::Socket::INET(Proto=>'udp') or die "socket : $!";
|
||||
die "Can't create WOL socket" if(!$sock);
|
||||
|
||||
my $ip_addr = inet_aton($host);
|
||||
my $sock_addr = sockaddr_in($port, $ip_addr);
|
||||
$mac_addr =~ s/://g;
|
||||
my $packet = pack('C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16);
|
||||
|
||||
setsockopt($sock, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
|
||||
|
||||
send($sock, $packet, 0, $sock_addr) or die "send : $!";
|
||||
close ($sock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -289,7 +289,7 @@ my @usbtable = (
|
||||
flush => "\n",
|
||||
request => "V\n",
|
||||
response => "^V .* CU.*",
|
||||
define => "CUL_PARAM CUL DEVICE\@9600 1234", },
|
||||
define => "CUL_PARAM CUL DEVICE\@9600 1PARAM34", },
|
||||
|
||||
{ NAME => "TCM310",
|
||||
matchList => ["cu.usbserial(.*)", "cu.usbmodem(.*)", "ttyUSB(.*)"],
|
||||
@@ -320,7 +320,7 @@ CommandUsb($$)
|
||||
my ($cl, $n) = @_;
|
||||
|
||||
return "Usage: usb [scan|create]" if("$n" !~ m/^(scan|create)$/);
|
||||
my $scan = 1 if($n =~ m/scan/);
|
||||
my $scan = 1 if($n eq "scan");
|
||||
my $ret = "";
|
||||
my $msg;
|
||||
my $dir = "/dev";
|
||||
@@ -331,6 +331,30 @@ CommandUsb($$)
|
||||
|
||||
require "$attr{global}{modpath}/FHEM/DevIo.pm";
|
||||
|
||||
################
|
||||
# First try to flash unflashed CULs
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################
|
||||
# Now the /dev scan
|
||||
foreach my $dev (sort split("\n", `ls $dir`)) {
|
||||
foreach my $thash (@usbtable) {
|
||||
foreach my $ml (@{$thash->{matchList}}) {
|
||||
@@ -383,7 +407,11 @@ CommandUsb($$)
|
||||
$define =~ s,DEVICE,$dir/$dev,g;
|
||||
$msg = "$dev: create as a fhem device with: define $define";
|
||||
Log 4, $msg; $ret .= $msg . "\n";
|
||||
CommandDefine($cl, $define);
|
||||
|
||||
if(!$scan) {
|
||||
Log 1, "define $define";
|
||||
CommandDefine($cl, $define);
|
||||
}
|
||||
|
||||
goto NEXTDEVICE;
|
||||
}
|
||||
|
||||
@@ -164,10 +164,9 @@ CommandCULflash($$)
|
||||
my @a = split("[ \t]+", $param);
|
||||
return "Usage: CULflash <Fhem-CUL-Device> <CUL-type>, ".
|
||||
"where <CUL-type> is one of ". join(" ", sort keys %ctypes)
|
||||
if(int(@a) != 2 ||
|
||||
!$defs{$a[0]} ||
|
||||
$defs{$a[0]}{TYPE} ne "CUL" ||
|
||||
!$ctypes{$a[1]});
|
||||
if(!(int(@a) == 2 &&
|
||||
($a[0] eq "none" || ($defs{$a[0]} && $defs{$a[0]}{TYPE} eq "CUL")) &&
|
||||
$ctypes{$a[1]}));
|
||||
|
||||
my $cul = $a[0];
|
||||
my $target = $a[1];
|
||||
@@ -202,8 +201,10 @@ CommandCULflash($$)
|
||||
$cmd =~ s/MCU/$mcu/g;
|
||||
$cmd =~ s/TARGET/$localfile/g;
|
||||
|
||||
CUL_SimpleWrite($defs{CUL}, "B01");
|
||||
sleep(4); # B01 needs 2 seconds for the reset
|
||||
if($cul ne "none") {
|
||||
CUL_SimpleWrite($defs{$cul}, "B01");
|
||||
sleep(4); # B01 needs 2 seconds for the reset
|
||||
}
|
||||
Log 1, $cmd;
|
||||
my $result = `$cmd`;
|
||||
Log 1, $result;
|
||||
|
||||
252
contrib/70_SolarView.pm
Normal file
@@ -0,0 +1,252 @@
|
||||
##############################################################################
|
||||
#
|
||||
# 70_SolarView.pm
|
||||
#
|
||||
# A FHEM module to read power/energy values from solarview.
|
||||
#
|
||||
# written 2012 by Tobe Toben <fhem@toben.net>
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
##############################################################################
|
||||
#
|
||||
# SolarView is a powerful datalogger for photovoltaic systems that runs on
|
||||
# an AVM Fritz!Box (and also on x86 systems). For details see the SV homepage:
|
||||
# http://www.amhamberg.de/solarview_fritzbox.aspx
|
||||
#
|
||||
# SV supports many different inverters. To read the SV power values using
|
||||
# this module, a TCP-Server must be enabled for SV by adding the parameter
|
||||
# "-TCP <port>" to the startscript (see the SV manual).
|
||||
#
|
||||
# usage:
|
||||
# define <name> SolarView <host> <port> [<interval> [<timeout>]]
|
||||
#
|
||||
# If <interval> is positive, new values are read every <interval> seconds.
|
||||
# If <interval> is 0, new values are read whenever a get request is called
|
||||
# on <name>. The default for <interval> is 300 (i.e. 5 minutes).
|
||||
#
|
||||
# get <name> <key>
|
||||
#
|
||||
# where <key> is one of currentPower, totalEnergy, totalEnergyDay,
|
||||
# totalEnergyMonth, totalEnergyYear, UDC, IDC, UDCB, IDCB, UDCC, IDCC,
|
||||
# gridVoltage, gridPower and temperature.
|
||||
#
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2012 Tobe Toben <fhem@toben.net>
|
||||
#
|
||||
# This script is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
#
|
||||
# This script is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
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'); # <20>C
|
||||
|
||||
sub
|
||||
SolarView_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "SolarView_Define";
|
||||
$hash->{UndefFn} = "SolarView_Undef";
|
||||
$hash->{GetFn} = "SolarView_Get";
|
||||
$hash->{AttrList} = "loglevel:0,1,2,3,4,5";
|
||||
}
|
||||
|
||||
sub
|
||||
SolarView_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
|
||||
my @args = split("[ \t]+", $def);
|
||||
|
||||
if (@args < 4)
|
||||
{
|
||||
return "SolarView_Define: too few arguments. Usage:\n" .
|
||||
"define <name> SolarView <host> <port> [<interval> [<timeout>]]";
|
||||
}
|
||||
|
||||
$hash->{Host} = $args[2];
|
||||
$hash->{Port} = $args[3];
|
||||
$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;
|
||||
|
||||
$hash->{STATE} = 'Initializing';
|
||||
|
||||
my $timenow = TimeNow();
|
||||
|
||||
for my $get (@gets)
|
||||
{
|
||||
$hash->{READINGS}{$get}{VAL} = $hash->{Invalid};
|
||||
$hash->{READINGS}{$get}{TIME} = $timenow;
|
||||
}
|
||||
|
||||
SolarView_Update($hash);
|
||||
|
||||
Log 2, "$hash->{NAME} will read from solarview at $hash->{Host}:$hash->{Port} " .
|
||||
($hash->{Interval} ? "every $hash->{Interval} seconds" : "for every 'get $hash->{NAME} <key>' request");
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
SolarView_Update($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
if ($hash->{Interval} > 0) {
|
||||
InternalTimer(gettimeofday() + $hash->{Interval}, "SolarView_Update", $hash, 0);
|
||||
}
|
||||
|
||||
# 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})
|
||||
{
|
||||
my ($sec,$min,$hour) = localtime(time);
|
||||
return undef if ($hour < 6 or $hour > 22);
|
||||
}
|
||||
|
||||
sleep($hash->{Sleep}) if $hash->{Sleep};
|
||||
|
||||
Log 4, "$hash->{NAME} tries to connect solarview at $hash->{Host}:$hash->{Port}";
|
||||
|
||||
my $success = 0;
|
||||
|
||||
eval {
|
||||
local $SIG{ALRM} = sub { die 'timeout'; };
|
||||
alarm $hash->{Timeout};
|
||||
|
||||
my $socket = IO::Socket::INET->new(PeerAddr => $hash->{Host},
|
||||
PeerPort => $hash->{Port},
|
||||
Timeout => $hash->{Timeout});
|
||||
|
||||
if ($socket and $socket->connected())
|
||||
{
|
||||
$socket->autoflush(1);
|
||||
print $socket "00*\r\n";
|
||||
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 $cpVal = $hash->{READINGS}{currentPower}{VAL};
|
||||
my $cpTime = $hash->{READINGS}{currentPower}{TIME};
|
||||
|
||||
for my $i (6..19)
|
||||
{
|
||||
my $getIdx = $i-6;
|
||||
|
||||
if (defined($vals[$i]))
|
||||
{
|
||||
$hash->{READINGS}{$gets[$getIdx]}{VAL} = 0 + $vals[$i];
|
||||
$hash->{READINGS}{$gets[$getIdx]}{TIME} = $tn;
|
||||
}
|
||||
}
|
||||
|
||||
# if Debounce is enabled, 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} = $cpVal;
|
||||
$hash->{READINGS}{currentPower}{TIME} = $cpTime;
|
||||
}
|
||||
|
||||
$success = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
alarm 0;
|
||||
|
||||
if ($success)
|
||||
{
|
||||
$hash->{STATE} = 'Initialized';
|
||||
Log 4, "$hash->{NAME} got fresh values from solarview";
|
||||
} else {
|
||||
$hash->{STATE} = 'Failure';
|
||||
Log 4, "$hash->{NAME} was unable to get fresh values from solarview";
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
SolarView_Get($@)
|
||||
{
|
||||
my ($hash, @args) = @_;
|
||||
|
||||
return 'SolarView_Get needs two arguments' if (@args != 2);
|
||||
|
||||
SolarView_Update($hash) unless $hash->{Interval};
|
||||
|
||||
my $get = $args[1];
|
||||
my $val = $hash->{Invalid};
|
||||
|
||||
if (defined($hash->{READINGS}{$get})) {
|
||||
$val = $hash->{READINGS}{$get}{VAL};
|
||||
} else {
|
||||
return "SolarView_Get: no such reading: $get";
|
||||
}
|
||||
|
||||
Log 3, "$args[0] $get => $val";
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
sub
|
||||
SolarView_Undef($$)
|
||||
{
|
||||
my ($hash, $args) = @_;
|
||||
|
||||
RemoveInternalTimer($hash) if $hash->{Interval};
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -57,9 +57,7 @@ if test $r != 0; then
|
||||
fi
|
||||
rm -f /var/nvi.tmp
|
||||
|
||||
echo "########################### Starting fhem ############################"
|
||||
sync
|
||||
$home/startfhem
|
||||
|
||||
# We have to restart with exit code 1, else the frontend tells us:
|
||||
# update failed: no error
|
||||
echo "########################### FHEM INSTALL END #########################"
|
||||
exit 1 # INSTALL_SUCCESS_REBOOT
|
||||
exit 1
|
||||
|
||||
@@ -17,15 +17,18 @@ export PATH
|
||||
export LD_LIBRARY_PATH=$home/lib
|
||||
export PERL5LIB=$home/lib/perl5/site_perl/5.12.2/mips-linux:$home/lib/perl5/site_perl/5.12.2:$home/lib/perl5/5.12.2/mips-linux:$home/lib/perl5/5.12.2
|
||||
|
||||
# let FHEM run as user boxusr80
|
||||
# add user fhem with uid of boxusr80
|
||||
# let FHEM run as user boxusr80 by adding user fhem with uid of boxusr80
|
||||
id fhem > /dev/null 2>&1
|
||||
if [ "$?" -ne "0" ]; then
|
||||
echo "user fhem does not exist. Adding it."
|
||||
echo "fhem:any:1080:0:fhem:/home-not-used:/bin/sh" >>/var/tmp/passwd
|
||||
# set files ownership
|
||||
chown -R boxusr80 ${home}/log
|
||||
chown -R boxusr80 ${home}/FHEM
|
||||
fi
|
||||
|
||||
# set file ownership
|
||||
chown -R boxusr80 ${home}/log
|
||||
chown -R boxusr80 ${home}/FHEM
|
||||
|
||||
chown root ${home}/dfu-programmer
|
||||
chmod 4755 ${home}/dfu-programmer
|
||||
|
||||
perl fhem.pl fhem.cfg
|
||||
|
||||
16
contrib/gplotmapping.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
fhem <= 5.1 fhem >= 5.2
|
||||
=====================================================
|
||||
ks300_3.gplot -> dayAvgTemp5rain11.gplot
|
||||
ks300_2.gplot -> hum6wind8.gplot
|
||||
ks300_4.gplot -> monthAvgTemp5Rain11.gplot
|
||||
em.gplot -> power4.gplot
|
||||
cul_em.gplot -> power8.gplot
|
||||
cul_emem.gplot -> power8top10.gplot
|
||||
oregon_rain.gplot -> rain4.gplot
|
||||
oregon_temp_press.gplot -> rain4press4.gplot
|
||||
hms_t.gplot -> temp4.gplot
|
||||
hms.gplot -> temp4hum6.gplot
|
||||
ks300.gplot -> temp4rain10.gplot
|
||||
oregon_wind.gplot -> wind4windDir4.gplot
|
||||
|
||||
143
contrib/survey.pl
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use CGI;
|
||||
|
||||
sub collectSubmitted($$@);
|
||||
sub printChapter($$@);
|
||||
|
||||
my @hw = qw(
|
||||
CM11 CUL FHZ HMLAN KM271 LIRC TCM TUL BS CUL_FHTTK USF1000 CUL_HM EIB EnOcean
|
||||
FS20 FHT FHT8V HMS KS300 CUL_TX CUL_WS CUL_EM CUL_RFR SIS_PMS CUL_HOERMANN
|
||||
OWFS X10 OWTEMP ALL3076 ALL4027 WEBIO WEBIO_12DIGITAL WEBTHERM RFXCOM OREGON
|
||||
RFXMETER RFXX10REC RFXELSE WS300 Weather EM EMWZ EMEM EMGZ ESA2000 ECMD
|
||||
ECMDDevice SCIVT SISPM USBWX WS3600 M232 xxLG7000 M232Counter LGTV
|
||||
M232Voltage WS2000 ALL4000T IPWE VantagePro2
|
||||
);
|
||||
my @help = qw(
|
||||
at notify sequence watchdog FileLog FHEM2FHEM PachLog holiday PID autocreate
|
||||
dummy structure SUNRISE_EL Utils XmlList updatefhem
|
||||
);
|
||||
my @fe = (
|
||||
"FHEMRENDERER", "fheME", "iPhone: dhs-computertechnik", "iPhone: fhemgw",
|
||||
"iPhone: fhemobile", "iPhone: phyfhem", "myHCE", "pgm2/FHEMWEB with SVG",
|
||||
"pgm2/FHEMWEB with gnuplot", "pgm3", "pgm5", "HomeMini",
|
||||
);
|
||||
my @platform = (
|
||||
"PC: Linux", "OSX", "PC: Windows", "PC: BSD", "Fritz!Box 7390", "Fritz!Box 7270",
|
||||
"Fritz!Box 7170", "Synology", "NSLU2", "TuxRadio", "Plug Computer",
|
||||
);
|
||||
|
||||
|
||||
my $title = "Used FHEM Modules & Components";
|
||||
|
||||
my $TIMES_HOME = "/opt/times/TIMES.rko";
|
||||
#my $TIMES_HOME = "/home/ipqmbe/times/TIMES";
|
||||
|
||||
my $q = new CGI;
|
||||
print $q->header,
|
||||
$q->start_html( -title => $title, -style=>{-src=>"style.css"}), "\n";
|
||||
|
||||
print '<div id="left">', "\n",
|
||||
'<img src="fhem.png" alt="fhem-logo"/>', "\n",
|
||||
' <h3>FHEM survey</h3>', "\n",
|
||||
'</div>', "\n";
|
||||
|
||||
print '<div id="right">',"\n",
|
||||
$q->h3("$title"), "\n";
|
||||
|
||||
|
||||
if($q->param('Submit')) {
|
||||
my $ret = "";
|
||||
$ret .= collectSubmitted("1. User", 0, ("user"));
|
||||
$ret .= collectSubmitted("2. Hardware", 1, @hw);
|
||||
$ret .= collectSubmitted("3. Helper", 1, @help);
|
||||
$ret .= collectSubmitted("4. Frontends",1, @fe);
|
||||
$ret .= collectSubmitted("5. Platform", 1, @platform);
|
||||
$ret .= collectSubmitted("6. Other", 0, ("other"));
|
||||
|
||||
|
||||
if(0) {
|
||||
$ret =~ s/\n/<br>\n/g;
|
||||
print $ret;
|
||||
|
||||
} else {
|
||||
require Mail::Send;
|
||||
my $msg = Mail::Send->new;
|
||||
$msg->to('info-r@koeniglich.de');
|
||||
$msg->subject('Formulardaten');
|
||||
my $fh = $msg->open;
|
||||
print $fh $ret;
|
||||
if(!$fh->close) {
|
||||
print "Couldn't send message: $!\n";
|
||||
} else {
|
||||
print "Collected data is forwarded for half-automated evaluation.\n";
|
||||
}
|
||||
}
|
||||
|
||||
print "</div>\n";
|
||||
print $q->end_html;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
print "This is a survey to get a feeling which fhem modules are used.<br>";
|
||||
print "<br>";
|
||||
print $q->start_form;
|
||||
|
||||
##############################################
|
||||
print $q->h4("User (optional):");
|
||||
print $q->textfield(-name=>'user', -size=>18, -maxsize=>36);
|
||||
|
||||
##############################################
|
||||
sub
|
||||
printChapter($$@)
|
||||
{
|
||||
my @arr = @_;
|
||||
my $name = shift @arr;
|
||||
my $cols = shift @arr;
|
||||
@arr = sort(@arr);
|
||||
print $q->h4("$name:");
|
||||
print "<div id=\"block\">";
|
||||
print "<table><tr>";
|
||||
foreach(my $i=0; $i < @arr; $i++) {
|
||||
print "<td>",$q->checkbox(-name=>"$arr[$i]",-label=>"$arr[$i]"),"</td>";
|
||||
print "</tr><tr>\n" if($i % $cols == ($cols-1));
|
||||
}
|
||||
print "</tr></table>";
|
||||
print "</div>";
|
||||
}
|
||||
|
||||
sub
|
||||
collectSubmitted($$@)
|
||||
{
|
||||
my ($name, $flags, @arr) = @_;
|
||||
my $ret = "";
|
||||
my @set;
|
||||
foreach my $f (@arr) {
|
||||
#print "Testing $f ", ($q->param($f) ? $q->param($f) : "UNDEF"), "<br>\n";
|
||||
push @set, $f if($q->param($f) && $flags);
|
||||
push @set, $q->param($f) if($q->param($f) && !$flags);
|
||||
}
|
||||
$ret .= join(", ' '", @set) if(@set);
|
||||
return "$name\n '$ret'\n";
|
||||
}
|
||||
|
||||
printChapter("Hardware devices", 4, @hw);
|
||||
printChapter("Helper modules", 6, @help);
|
||||
printChapter("Frontends", 3, @fe);
|
||||
printChapter("Platform", 5, @platform);
|
||||
|
||||
|
||||
##############################################
|
||||
print $q->h4("Other modules:");
|
||||
print $q->textfield(-name=>'other', -size=>80, -maxsize=>80);
|
||||
print "<br><br><br>\n";
|
||||
|
||||
print $q->submit('Submit');
|
||||
print "<br><br><br>\n";
|
||||
|
||||
print $q->end_form;
|
||||
print "</div>\n";
|
||||
print $q->end_html;
|
||||
@@ -79,6 +79,19 @@
|
||||
<a href="http://localhost:8084/fhem">http://fhem-host:8085/fhem</a> if
|
||||
you are using a tablet like the iPad.<br><br>
|
||||
|
||||
In the default configuration, fhem will look for USB attached FHZ, CUL
|
||||
and TCM devices on startup (unix/OSX only) and will create
|
||||
appropriate fhem devices.<br><br>
|
||||
|
||||
On linux (esp. FB7390) it will even try to
|
||||
flash the unflashed CUL, if it is attached at startup. See the <a
|
||||
href="commandref.html#usb">usb</a> and <a
|
||||
href="commandref.html#CULflash">CULflash</a> commands for details, and
|
||||
check the "unsorted" room in FHEMWEB for the newly created
|
||||
devices. Note that switching a CUL to HomeMatic mode is still has to be
|
||||
done manually. Only one device is flashed per fhem-startup.<br><br>
|
||||
|
||||
For doing it manually (or if fhem failed to discover your device):
|
||||
Attach the USB device (CUL, FHZ1000PC/FHZ1300, TUL, EUL, etc) to your
|
||||
computer, and look for the corresponding device in the /dev
|
||||
directory. For <a href="commandref.html#CUL">CUL</a> a file named
|
||||
|
||||
16788
docs/commandref.html
@@ -107,6 +107,10 @@
|
||||
<a href="http://www.dhs-computertechnik.de/support-iphone.html">
|
||||
dhs-computertechnik</a> or
|
||||
<a href="http://code.google.com/p/phyfhem/">phyfhem</a>
|
||||
|
||||
<br><br>
|
||||
Android frontends:
|
||||
<a href="http://andFHEM.klass.li">AndFHEM</a> (native app)
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
BIN
docs/fhem.png
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -67,7 +67,7 @@
|
||||
<li>With the command df check the name of the stick on the FB.
|
||||
<li>Execute the following command in the FB telnet window to start fhem
|
||||
when the FB reboots:<br>
|
||||
echo "<path-of-the-stick>/fhem/startfhem" >> /var/flash/debug.cfg
|
||||
echo "<path-of-the-stick>/fhem/startfhem" > /var/flash/debug.cfg
|
||||
<li>Start fhem manually with "<path-of-the-stick>/fhem/startfhem"
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
BIN
docs/pgm2-3.png
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 247 KiB |
@@ -4,8 +4,7 @@ a { color: #278727; }
|
||||
#logo { position:fixed; top:20px; left:20px;
|
||||
width:100px; height:105px; background-image:url(fhem.png); }
|
||||
#menu { position:fixed; top:140px;left:20px; width:140px; }
|
||||
#right { position:fixed; top:20px; left:170px; bottom:20px; right:10px;
|
||||
overflow: auto; }
|
||||
#right { position:absolute; top:50px; left:180px; bottom:10px; right:10px; }
|
||||
h2,h3,h4 { color:#52865D; line-height:1.3;
|
||||
margin-top:1.5em; font-family:Arial,Sans-serif; }
|
||||
div#block { border:1px solid gray; background: #F8F8E0; padding:0.7em; }
|
||||
|
||||
58
fhem.pl
@@ -253,17 +253,29 @@ if(int(@ARGV) != 1 && int(@ARGV) != 2) {
|
||||
# If started as root, and there is a fhem user in the /etc/passwd, su to it
|
||||
if($^O !~ m/Win/ && $< == 0) {
|
||||
|
||||
my @gr = getgrnam("dialout");
|
||||
if(@gr) {
|
||||
use POSIX qw(setgid);
|
||||
setgid($gr[2]);
|
||||
}
|
||||
|
||||
my @pw = getpwnam("fhem");
|
||||
if(@pw) {
|
||||
use POSIX qw(setuid);
|
||||
use POSIX qw(setuid setgid);
|
||||
|
||||
# set primary group
|
||||
setgid($pw[3]);
|
||||
|
||||
# read all secondary groups into an array:
|
||||
my @groups;
|
||||
while ( my ($name, $pw, $gid, $members) = getgrent() ) {
|
||||
push(@groups, $gid) if ( grep($_ eq $pw[0],split(/\s+/,$members)) );
|
||||
}
|
||||
|
||||
# set the secondary groups via $)
|
||||
if (@groups) {
|
||||
$) = "$pw[3] ".join(" ",@groups);
|
||||
} else {
|
||||
$) = "$pw[3] $pw[3]";
|
||||
}
|
||||
|
||||
setuid($pw[2]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
###################################################
|
||||
@@ -2124,21 +2136,23 @@ DoTrigger($$)
|
||||
|
||||
################
|
||||
# Inform
|
||||
$max = int(@{$defs{$dev}{CHANGED}}); # can be enriched in the notifies
|
||||
foreach my $c (keys %client) { # Do client loop first, is cheaper
|
||||
next if(!$client{$c}{inform} || $client{$c}{inform} eq "raw");
|
||||
my $tn = TimeNow();
|
||||
if($attr{global}{mseclog}) {
|
||||
my ($seconds, $microseconds) = gettimeofday();
|
||||
$tn .= sprintf(".%03d", $microseconds/1000);
|
||||
}
|
||||
my $re = $client{$c}{informRegexp};
|
||||
for(my $i = 0; $i < $max; $i++) {
|
||||
my $state = $defs{$dev}{CHANGED}[$i];
|
||||
next if($re && $state !~ m/$re/);
|
||||
syswrite($client{$c}{fd},
|
||||
($client{$c}{inform} eq "timer" ? "$tn " : "") .
|
||||
"$defs{$dev}{TYPE} $dev $state\n");
|
||||
if($defs{$dev}{CHANGED}) { # It gets deleted sometimes (?)
|
||||
$max = int(@{$defs{$dev}{CHANGED}}); # can be enriched in the notifies
|
||||
foreach my $c (keys %client) { # Do client loop first, is cheaper
|
||||
next if(!$client{$c}{inform} || $client{$c}{inform} eq "raw");
|
||||
my $tn = TimeNow();
|
||||
if($attr{global}{mseclog}) {
|
||||
my ($seconds, $microseconds) = gettimeofday();
|
||||
$tn .= sprintf(".%03d", $microseconds/1000);
|
||||
}
|
||||
my $re = $client{$c}{informRegexp};
|
||||
for(my $i = 0; $i < $max; $i++) {
|
||||
my $state = $defs{$dev}{CHANGED}[$i];
|
||||
next if($re && $state !~ m/$re/);
|
||||
syswrite($client{$c}{fd},
|
||||
($client{$c}{inform} eq "timer" ? "$tn " : "") .
|
||||
"$defs{$dev}{TYPE} $dev $state\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ my %FW_hiddenroom; # hash of hidden rooms
|
||||
my $FW_longpoll;
|
||||
my $FW_inform;
|
||||
my $FW_XHR;
|
||||
my $FW_jsonp;
|
||||
#my $FW_encoding="ISO-8859-1";
|
||||
my $FW_encoding="UTF-8";
|
||||
|
||||
@@ -423,9 +424,14 @@ FW_AnswerCall($)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if($FW_XHR) {
|
||||
if($FW_XHR || $FW_jsonp) {
|
||||
$FW_RETTYPE = "text/plain; charset=$FW_encoding";
|
||||
FW_pO $FW_cmdret;
|
||||
if($FW_jsonp) {
|
||||
$FW_cmdret =~ s/'/\\'/g;
|
||||
FW_pO "$FW_jsonp('$FW_cmdret');";
|
||||
} else {
|
||||
FW_pO $FW_cmdret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -519,6 +525,7 @@ FW_digestCgi($)
|
||||
$FW_room = "";
|
||||
$FW_detail = "";
|
||||
$FW_XHR = undef;
|
||||
$FW_jsonp = undef;
|
||||
$FW_inform = undef;
|
||||
|
||||
%FW_webArgs = ();
|
||||
@@ -542,6 +549,7 @@ FW_digestCgi($)
|
||||
if($p eq "pos") { %FW_pos = split(/[=;]/, $v); }
|
||||
if($p eq "data") { $FW_data = $v; }
|
||||
if($p eq "XHR") { $FW_XHR = 1; }
|
||||
if($p eq "jsonp") { $FW_jsonp = $v; }
|
||||
if($p eq "inform") { $FW_inform = $v; }
|
||||
|
||||
}
|
||||
@@ -881,8 +889,7 @@ FW_showRoom()
|
||||
$txt = ReadingsVal($d, "measured-temp", "");
|
||||
$txt =~ s/ .*//;
|
||||
$txt = sprintf("%2.1f", int(2*$txt)/2) if($txt =~ m/[0-9.-]/);
|
||||
my @tv = map { ($_.".0", $_+0.5) } (5..30);
|
||||
shift(@tv); # 5.0 is not valid
|
||||
my @tv = split(" ", getAllSets("$d desired-temp"));
|
||||
$txt = int($txt*20)/$txt if($txt =~ m/^[0-9].$/);
|
||||
|
||||
FW_pO "<td>".
|
||||
@@ -1415,13 +1422,11 @@ FW_style($$)
|
||||
|
||||
my @fl = ("fhem.cfg");
|
||||
push(@fl, "");
|
||||
push(@fl, FW_fileList("$FW_dir/(.*.sh|.*Util.*)"));
|
||||
push(@fl, FW_fileList("$FW_dir/.*(sh|Util.*|cfg|holiday)"));
|
||||
push(@fl, "");
|
||||
push(@fl, FW_fileList("$FW_dir/.*.(css|svg)"));
|
||||
push(@fl, "");
|
||||
push(@fl, FW_fileList("$FW_dir/.*.gplot"));
|
||||
# push(@fl, "");
|
||||
# push(@fl, FW_fileList("$FW_dir/.*html"));
|
||||
|
||||
FW_pO $start;
|
||||
FW_pO "$msg<br><br>" if($msg);
|
||||
@@ -1599,7 +1604,6 @@ FW_showWeblink($$$$)
|
||||
my ($d, $v, $t, $buttons) = @_;
|
||||
|
||||
my $attr = AttrVal($d, "htmlattr", "");
|
||||
|
||||
if($t eq "link") {
|
||||
FW_pO "<a href=\"$v\" $attr>$d</a>"; # no FW_pH, want to open extra browser
|
||||
|
||||
@@ -1633,7 +1637,7 @@ FW_showWeblink($$$$)
|
||||
|
||||
my @va = split(":", $v, 3);
|
||||
if(@va != 3 || !$defs{$va[0]} || !$defs{$va[0]}{currentlogfile}) {
|
||||
FW_pO "Broken definition: $v<br>";
|
||||
FW_pO "Broken definition for $d: $v<br>";
|
||||
|
||||
} else {
|
||||
if($va[2] eq "CURRENT") {
|
||||
|
||||
@@ -40,27 +40,30 @@ SVG_render($$$$$$$)
|
||||
return "" if(!defined($dp));
|
||||
my $th = 16; # "Font" height
|
||||
my ($x, $y) = (($SVG_ss ? 2 : 3)*$th, 1.2*$th); # Rect offset
|
||||
my %conf; # gnuplot file settings
|
||||
|
||||
######################
|
||||
# Convert the configuration to a "readable" form -> array to hash
|
||||
my %conf; # gnuplot file settings
|
||||
map { chomp; my @a=split(" ",$_, 3);
|
||||
if($a[0] && $a[0] eq "set") { $conf{$a[1]} = $a[2]; } } @{$confp};
|
||||
if($a[0] && $a[0] eq "set") { $conf{lc($a[1])} = $a[2]; } } @{$confp};
|
||||
|
||||
my $ps = "800,400";
|
||||
$ps = $1 if($conf{terminal} =~ m/.*size[ ]*([^ ]*)/);
|
||||
$conf{title} = "" if(!defined($conf{title}));
|
||||
$conf{title} =~ s/'//g;
|
||||
|
||||
my ($ow,$oh) = split(",", $ps); # Original width
|
||||
my ($w, $h) = ($ow-2*$x, $oh-2*$y); # Rect size
|
||||
|
||||
# Html Header
|
||||
######################
|
||||
# Html Header
|
||||
FW_pO '<?xml version="1.0" encoding="UTF-8"?>';
|
||||
FW_pO '<!DOCTYPE svg>';
|
||||
FW_pO '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" '.
|
||||
'xmlns:xlink="http://www.w3.org/1999/xlink" >';
|
||||
|
||||
my $prf = AttrVal($FW_wname, "stylesheetPrefix", "");
|
||||
FW_pO "<style type=\"text/css\"><![CDATA[";
|
||||
FW_pO "<style lType=\"text/css\"><![CDATA[";
|
||||
if(open(FH, "$FW_dir/${prf}svg_style.css") ||
|
||||
open(FH, "$FW_dir/svg_style.css")) {
|
||||
FW_pO join("", <FH>);
|
||||
@@ -70,15 +73,18 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
FW_pO "]]></style>";
|
||||
|
||||
######################
|
||||
# gradient definitions
|
||||
if(open(FH, "$FW_dir/${prf}svg_defs.svg") ||
|
||||
open(FH, "$FW_dir/svg_defs.svg")) { # gradient definitions
|
||||
open(FH, "$FW_dir/svg_defs.svg")) {
|
||||
FW_pO join("", <FH>);
|
||||
close(FH);
|
||||
} else {
|
||||
Log 0, "Can't open $FW_dir/svg_defs.svg"
|
||||
}
|
||||
|
||||
# Background
|
||||
######################
|
||||
# Draw the background
|
||||
FW_pO "<rect width =\"$ow\" height=\"$oh\" class=\"background\"/>";
|
||||
# Rectangle
|
||||
FW_pO "<rect x=\"$x\" y=\"$y\" width =\"$w\" height =\"$h\" rx=\"8\" ry=\"8\" ".
|
||||
@@ -91,6 +97,7 @@ SVG_render($$$$$$$)
|
||||
FW_pO "<text id=\"svg_title\" x=\"$off1\" y=\"$off2\" " .
|
||||
"class=\"title\" text-anchor=\"middle\">$title</text>";
|
||||
|
||||
######################
|
||||
# Copy and Paste labels, hidden by default
|
||||
FW_pO "<text id=\"svg_paste\" x=\"" . ($ow-$x) . "\" y=\"$off2\" " .
|
||||
"onclick=\"parent.svg_paste(evt)\" " .
|
||||
@@ -100,6 +107,8 @@ SVG_render($$$$$$$)
|
||||
"class=\"copy\" text-anchor=\"end\"> </text>";
|
||||
|
||||
|
||||
######################
|
||||
# Left and right labels
|
||||
my $t = ($conf{ylabel} ? $conf{ylabel} : "");
|
||||
$t =~ s/"//g;
|
||||
if(!$SVG_ss) {
|
||||
@@ -114,25 +123,30 @@ SVG_render($$$$$$$)
|
||||
"class=\"y2label\" transform=\"rotate(270,$off1,$off2)\">$t</text>";
|
||||
}
|
||||
|
||||
# Digest axes/title/type from $plot (gnuplot) and draw the line-titles
|
||||
my (@axes,@ltitle,@type,@linestyle,@linewidth);
|
||||
my $i;
|
||||
$i = 0; $plot =~ s/ axes (\w+)/$axes[$i++]=$1/gse;
|
||||
$i = 0; $plot =~ s/ title '([^']*)'/$ltitle[$i++]=$1/gse;
|
||||
$i = 0; $plot =~ s/ with (\w+)/$type[$i++]=$1/gse;
|
||||
$i = 0; $plot =~ s/ ls (\d+)/$linestyle[$i++]=$1/gse;
|
||||
$i = 0; $plot =~ s/ lw (\d+)/$linewidth[$i++]=$1/gse;
|
||||
######################
|
||||
# Digest axes/title/etc from $plot (gnuplot) and draw the line-titles
|
||||
my (@lAxis,@lTitle,@lType,@lStyle,@lWidth);
|
||||
my ($i, $pTemp);
|
||||
$pTemp = $plot; $i = 0; $pTemp =~ s/ axes (\w+)/$lAxis[$i++]=$1/gse;
|
||||
$pTemp = $plot; $i = 0; $pTemp =~ s/ title '([^']*)'/$lTitle[$i++]=$1/gse;
|
||||
$pTemp = $plot; $i = 0; $pTemp =~ s/ with (\w+)/$lType[$i++]=$1/gse;
|
||||
$pTemp = $plot; $i = 0; $pTemp =~ s/ ls (\w+)/$lStyle[$i++]=$1/gse;
|
||||
$pTemp = $plot; $i = 0; $pTemp =~ s/ lw (\w+)/$lWidth[$i++]=$1/gse;
|
||||
|
||||
for my $i (0..int(@type)-1) { # axes is optional
|
||||
$axes[$i] = "x1y2" if(!$axes[$i]);
|
||||
for my $i (0..int(@lType)-1) { # lAxis is optional
|
||||
$lAxis[$i] = "x1y2" if(!$lAxis[$i]);
|
||||
$lStyle[$i] = "class=\"". (defined($lStyle[$i]) ? $lStyle[$i] : "l$i") . "\"";
|
||||
$lWidth[$i] = (defined($lWidth[$i]) ? "style=\"stroke-width:$lWidth[$i]\"" :"");
|
||||
}
|
||||
|
||||
($off1,$off2) = ($ow-$x-$th, $y+$th);
|
||||
|
||||
|
||||
for my $i (0..int(@ltitle)-1) {
|
||||
######################
|
||||
# Plot caption (title)
|
||||
for my $i (0..int(@lTitle)-1) {
|
||||
my $j = $i+1;
|
||||
my $t = $ltitle[$i];
|
||||
my $t = $lTitle[$i];
|
||||
my $desc = "";
|
||||
if(defined($data{"min$j"}) && $data{"min$j"} ne "undef") {
|
||||
$desc = sprintf("%s: Min:%g Max:%g Last:%g",
|
||||
@@ -140,11 +154,11 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
FW_pO "<text title=\"$desc\" ".
|
||||
"onclick=\"parent.svg_labelselect(evt)\" line_id=\"line_$i\" " .
|
||||
"x=\"$off1\" y=\"$off2\" text-anchor=\"end\" class=\"l" .
|
||||
(defined($linestyle[$i]) ? $linestyle[$i] : $i) . "\">$t</text>";
|
||||
"x=\"$off1\" y=\"$off2\" text-anchor=\"end\" $lStyle[$i]>$t</text>";
|
||||
$off2 += $th;
|
||||
}
|
||||
|
||||
######################
|
||||
# Loop over the input, digest dates, calculate min/max values
|
||||
my ($fromsec, $tosec);
|
||||
$fromsec = time_to_sec($from) if($from ne "0"); # 0 is special
|
||||
@@ -168,7 +182,7 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
$dpoff = $ndpoff+1;
|
||||
if($l =~ m/^#/) {
|
||||
my $a = $axes[$idx];
|
||||
my $a = $lAxis[$idx];
|
||||
if(defined($a)) {
|
||||
$hmin{$a} = $min if(!defined($hmin{$a}) || $hmin{$a} > $min);
|
||||
$hmax{$a} = $max if(!defined($hmax{$a}) || $hmax{$a} < $max);
|
||||
@@ -213,6 +227,7 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# Compute & draw vertical tics, grid and labels
|
||||
my $ddur = ($tosec-$fromsec)/86400;
|
||||
my ($first_tag, $tag, $step, $tstep, $aligntext, $aligntics);
|
||||
@@ -230,6 +245,7 @@ SVG_render($$$$$$$)
|
||||
$aligntext = 2; $aligntics = 2;
|
||||
}
|
||||
|
||||
######################
|
||||
# First the tics
|
||||
$off2 = $y+4;
|
||||
my ($off3, $off4) = ($y+$h-4, $y+$h);
|
||||
@@ -242,6 +258,7 @@ SVG_render($$$$$$$)
|
||||
FW_pO "<polyline points=\"$off1,$off3 $off1,$off4\"/>";
|
||||
}
|
||||
|
||||
######################
|
||||
# then the text and the grid
|
||||
$off1 = $x;
|
||||
$off2 = $y+$h+$th;
|
||||
@@ -259,6 +276,7 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# Left and right axis tics / text / grid
|
||||
$hmin{x1y1}=$hmin{x1y2}, $hmax{x1y1}=$hmax{x1y2} if(!defined($hmin{x1y1}));
|
||||
$hmin{x1y2}=$hmin{x1y1}, $hmax{x1y2}=$hmax{x1y1} if(!defined($hmin{x1y2}));
|
||||
@@ -340,9 +358,10 @@ SVG_render($$$$$$$)
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# Second loop over the data: draw the measured points
|
||||
for my $idx (0..int(@hdx)-1) {
|
||||
my $a = $axes[$idx];
|
||||
for(my $idx=$#hdx; $idx >= 0; $idx--) {
|
||||
my $a = $lAxis[$idx];
|
||||
|
||||
next if(!defined($a));
|
||||
$min = $hmin{$a};
|
||||
@@ -353,30 +372,32 @@ SVG_render($$$$$$$)
|
||||
next if(!defined($dxp));
|
||||
|
||||
my $yh = $y+$h;
|
||||
my $tl = $ltitle[$idx] ? $ltitle[$idx] : "";
|
||||
#my $dec = int(log($hmul*3)/log(10)); # Some perl implementations do not have log()
|
||||
my $tl = $lTitle[$idx] ? $lTitle[$idx] : "";
|
||||
#my $dec = int(log($hmul*3)/log(10)); # perl can be compiled without log() !
|
||||
my $dec = length(sprintf("%d",$hmul*3))-1;
|
||||
$dec = 0 if($dec < 0);
|
||||
my $js_helpers = "id=\"line_$idx\" decimals=\"$dec\" ".
|
||||
my $attributes = "id=\"line_$idx\" decimals=\"$dec\" ".
|
||||
"x_off=\"$fromsec\" x_min=\"$x\" x_mul=\"$tmul\" ".
|
||||
"y_h=\"$yh\" y_min=\"$min\" y_mul=\"$hmul\" title=\"$tl\" ".
|
||||
"onclick=\"parent.svg_click(evt)\"";
|
||||
"onclick=\"parent.svg_click(evt)\" $lWidth[$idx] $lStyle[$idx]";
|
||||
my $isFill = ($lStyle[$idx] =~ m/fill/);
|
||||
|
||||
my ($lx, $ly) = (-1,-1);
|
||||
if($type[$idx] eq "points" ) {
|
||||
if($lType[$idx] eq "points" ) {
|
||||
|
||||
foreach my $i (0..int(@{$dxp})-1) {
|
||||
my ($x1, $y1) = (int($x+$dxp->[$i]),
|
||||
int($y+$h-($dyp->[$i]-$min)*$hmul));
|
||||
next if($x1 == $lx && $y1 == $ly);
|
||||
$ly = $x1; $ly = $y1;
|
||||
$ret = sprintf(" %d,%d %d,%d %d,%d %d,%d %d,%d",
|
||||
$x1-3,$y1, $x1,$y1-3, $x1+3,$y1, $x1,$y1+3, $x1-3,$y1);
|
||||
FW_pO "<polyline $js_helpers points=\"$ret\" class=\"l$idx\"/>";
|
||||
}
|
||||
foreach my $i (0..int(@{$dxp})-1) {
|
||||
my ($x1, $y1) = (int($x+$dxp->[$i]),
|
||||
int($y+$h-($dyp->[$i]-$min)*$hmul));
|
||||
next if($x1 == $lx && $y1 == $ly);
|
||||
$ly = $x1; $ly = $y1;
|
||||
$ret = sprintf(" %d,%d %d,%d %d,%d %d,%d %d,%d",
|
||||
$x1-3,$y1, $x1,$y1-3, $x1+3,$y1, $x1,$y1+3, $x1-3,$y1);
|
||||
FW_pO "<polyline $attributes points=\"$ret\"/>";
|
||||
}
|
||||
|
||||
} elsif($type[$idx] eq "steps" || $type[$idx] eq "fsteps" ) {
|
||||
} elsif($lType[$idx] eq "steps" || $lType[$idx] eq "fsteps" ) {
|
||||
|
||||
$ret .= sprintf(" %d,%d", $x+$dxp->[0], $y+$h) if($isFill && @{$dxp});
|
||||
if(@{$dxp} == 1) {
|
||||
my $y1 = $y+$h-($dyp->[0]-$min)*$hmul;
|
||||
$ret .= sprintf(" %d,%d %d,%d %d,%d %d,%d",
|
||||
@@ -387,16 +408,19 @@ SVG_render($$$$$$$)
|
||||
my ($x2, $y2) = ($x+$dxp->[$i], $y+$h-($dyp->[$i] -$min)*$hmul);
|
||||
next if(int($x2) == $lx && int($y1) == $ly);
|
||||
$lx = int($x2); $ly = int($y2);
|
||||
if($type[$idx] eq "steps") {
|
||||
if($lType[$idx] eq "steps") {
|
||||
$ret .= sprintf(" %d,%d %d,%d %d,%d", $x1,$y1, $x2,$y1, $x2,$y2);
|
||||
} else {
|
||||
$ret .= sprintf(" %d,%d %d,%d %d,%d", $x1,$y1, $x1,$y2, $x2,$y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
FW_pO "<polyline $js_helpers points=\"$ret\" class=\"l$idx\"/>";
|
||||
$ret .= sprintf(" %d,%d", $lx, $y+$h) if($isFill && $lx > -1);
|
||||
|
||||
} elsif($type[$idx] eq "histeps" ) {
|
||||
FW_pO "<polyline $attributes points=\"$ret\"/>";
|
||||
|
||||
} elsif($lType[$idx] eq "histeps" ) {
|
||||
$ret .= sprintf(" %d,%d", $x+$dxp->[0], $y+$h) if($isFill && @{$dxp});
|
||||
if(@{$dxp} == 1) {
|
||||
my $y1 = $y+$h-($dyp->[0]-$min)*$hmul;
|
||||
$ret .= sprintf(" %d,%d %d,%d %d,%d %d,%d",
|
||||
@@ -411,22 +435,21 @@ SVG_render($$$$$$$)
|
||||
$x1,$y1, ($x1+$x2)/2,$y1, ($x1+$x2)/2,$y2, $x2,$y2);
|
||||
}
|
||||
}
|
||||
FW_pO "<polyline $js_helpers points=\"$ret\" class=\"l$idx\"/>";
|
||||
$ret .= sprintf(" %d,%d", $lx, $y+$h) if($isFill && $lx > -1);
|
||||
FW_pO "<polyline $attributes points=\"$ret\"/>";
|
||||
|
||||
} else { # lines and everything else
|
||||
foreach my $i (0..int(@{$dxp})-1) {
|
||||
my ($x1, $y1) = (int($x+$dxp->[$i]),
|
||||
int($y+$h-($dyp->[$i]-$min)*$hmul));
|
||||
next if($x1 == $lx && $y1 == $ly);
|
||||
$ret .= sprintf(" %d,%d", $x1, $y+$h) if($i == 0 && $isFill);
|
||||
$lx = $x1; $ly = $y1;
|
||||
$ret .= sprintf(" %d,%d", $x1, $y1);
|
||||
}
|
||||
$ret .= sprintf(" %d,%d", $lx, $y+$h) if($isFill && $lx > -1);
|
||||
|
||||
FW_pO "<polyline $js_helpers points=\"$ret\" style=\"stroke-width:" .
|
||||
(defined($linewidth[$idx]) ? $linewidth[$idx] : 1) .
|
||||
"\" class=\"l" .
|
||||
(defined($linestyle[$idx]) ? $linestyle[$idx] : $idx) . "\"/>";
|
||||
|
||||
FW_pO "<polyline $attributes points=\"$ret\"/>";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 5.5 KiB |
@@ -1,7 +1,43 @@
|
||||
<!-- will be included in each svg plot -->
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<linearGradient id="gr_bg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFFFF7; stop-opacity:1"/>
|
||||
<stop offset="100%" style="stop-color:#A7A7A7; stop-opacity:1"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_0" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f00; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#f88; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#291; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#8f7; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_2" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#00f; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#88f; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_3" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f0f; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#f8f; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_4" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ff0; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#ff8; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_5" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0ff; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#8ff; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_6" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#000; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#ccc; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
</defs>
|
||||
|
||||
@@ -5,28 +5,27 @@ text { font-family:Arial, Helvetica, sans-serif; font-size:12px; fill:#CCCCCC;}
|
||||
text.title {font-family:Arial, Helvetica, sans-serif; font-size:16px; fill:#CCCCCC;}
|
||||
text.copy { text-decoration:underline; stroke:none; fill:blue;}
|
||||
text.paste { text-decoration:underline; stroke:none; fill:blue;}
|
||||
text.l0 { text-decoration:underline; stroke:none; fill:red;}
|
||||
text.l1 { text-decoration:underline; stroke:none; fill:green;}
|
||||
text.l2 { text-decoration:underline; stroke:none; fill:blue;}
|
||||
text.l3 { text-decoration:underline; stroke:none; fill:magenta;}
|
||||
text.l4 { text-decoration:underline; stroke:none; fill:brown;}
|
||||
text.l5 { text-decoration:underline; stroke:none; fill:black;}
|
||||
text.l6 { text-decoration:underline; stroke:none; fill:olive;}
|
||||
text.l7 { text-decoration:underline; stroke:none; fill:gray;}
|
||||
text.l8 { text-decoration:underline; stroke:none; fill:yellow;}
|
||||
|
||||
polyline { stroke:black; fill:none; }
|
||||
.border { stroke:black; fill:url(#grad1);}
|
||||
.border { stroke:black; fill:url(#gr_bg);}
|
||||
.vgrid { stroke:gray; stroke-dasharray:2,6;}
|
||||
.hgrid { stroke:gray; stroke-dasharray:2,6;}
|
||||
.pasted { stroke:black; stroke-dasharray:1,1;}
|
||||
|
||||
.l0 { stroke:red;}
|
||||
.l1 { stroke:green;}
|
||||
.l2 { stroke:blue;}
|
||||
.l3 { stroke:magenta;}
|
||||
.l4 { stroke:brown;}
|
||||
.l5 { stroke:black;}
|
||||
.l6 { stroke:olive;}
|
||||
.l7 { stroke:gray;}
|
||||
.l8 { stroke:yellow;}
|
||||
.l0 { stroke:red; } text.l0 { stroke:none; fill:red; }
|
||||
.l1 { stroke:green; } text.l1 { stroke:none; fill:green; }
|
||||
.l2 { stroke:blue; } text.l2 { stroke:none; fill:blue; }
|
||||
.l3 { stroke:magenta; } text.l3 { stroke:none; fill:magenta; }
|
||||
.l4 { stroke:brown; } text.l4 { stroke:none; fill:brown; }
|
||||
.l5 { stroke:black; } text.l5 { stroke:none; fill:black; }
|
||||
.l6 { stroke:olive; } text.l6 { stroke:none; fill:olive; }
|
||||
.l7 { stroke:gray; } text.l7 { stroke:none; fill:gray; }
|
||||
.l8 { stroke:yellow; } text.l8 { stroke:none; fill:yellow; }
|
||||
|
||||
.l0fill{ stroke:#f00; fill:url(#gr_0); } text.l0fill{ stroke:none; fill:#f00; }
|
||||
.l1fill{ stroke:#291; fill:url(#gr_1); } text.l1fill{ stroke:none; fill:#291; }
|
||||
.l2fill{ stroke:#00f; fill:url(#gr_2); } text.l2fill{ stroke:none; fill:#00f; }
|
||||
.l3fill{ stroke:#f0f; fill:url(#gr_3); } text.l3fill{ stroke:none; fill:#f0f; }
|
||||
.l4fill{ stroke:#ff0; fill:url(#gr_4); } text.l4fill{ stroke:none; fill:#ff0; }
|
||||
.l5fill{ stroke:#0ff; fill:url(#gr_5); } text.l5fill{ stroke:none; fill:#0ff; }
|
||||
.l6fill{ stroke:#000; fill:url(#gr_6); } text.l6fill{ stroke:none; fill:#000; }
|
||||
|
||||
@@ -21,7 +21,7 @@ set yrange [0:]
|
||||
#FileLog 4:WW_Isttemperatur:0:
|
||||
#FileLog 4:Brenner_Laufzeit1_Minuten\x3a:0:delta-h
|
||||
|
||||
plot "<grep WW_Isttemperatur <IN>" using 1:4 axes x1y2 title 'WW-Temp' with lines,\
|
||||
plot "<grep WW_Isttemperatur <IN>" using 1:4 axes x1y2 ls l0 title 'WW-Temp' with lines,\
|
||||
"<grep Brenner_Laufzeit1_Minuten: <IN> | perl -ane '\
|
||||
@a = split(\"[_:]\", $F[0]);\
|
||||
if(defined($lh) && $lh ne $a[1])\
|
||||
@@ -29,4 +29,4 @@ plot "<grep WW_Isttemperatur <IN>" using 1:4 axes x1y2 title 'WW-Temp' with line
|
||||
if($lv) { $hv += ($F[3]-$lv); }\
|
||||
$lh = $a[1]; $ld = $a[0]; $lv = $F[3];\
|
||||
END { printf(\"${ld}_$lh:30:00 %f\n\", $hv) }'"\
|
||||
using 1:2 axes x1y1 title 'Runtime/h (Min)' with histeps
|
||||
using 1:2 axes x1y1 ls l6fill title 'Runtime/h (Min)' with histeps
|
||||
|
||||
@@ -3,6 +3,7 @@ var old_title;
|
||||
var old_sel;
|
||||
var svgdoc;
|
||||
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var to;
|
||||
|
||||
// Base64 encode the xy points (12 bit x, 12 bit y).
|
||||
function
|
||||
@@ -92,23 +93,43 @@ svg_paste(evt)
|
||||
d.documentElement.appendChild(o);
|
||||
}
|
||||
|
||||
|
||||
function
|
||||
showOtherLines(d, lid, currval, maxval)
|
||||
{
|
||||
for(var i=0; i < 9; i++) {
|
||||
var id="line_"+i;
|
||||
var el = d.getElementById(id);
|
||||
if(el && id != lid) {
|
||||
var h = parseFloat(el.getAttribute("y_h"));
|
||||
el.setAttribute("transform", "translate(0,"+h*(1-currval)+") "+
|
||||
"scale(1,"+currval+")");
|
||||
}
|
||||
}
|
||||
if(currval != maxval) {
|
||||
currval += (currval<maxval ? 0.02 : -0.02);
|
||||
currval = Math.round(currval*100)/100;
|
||||
to=setTimeout(function(){showOtherLines(d,lid,currval,maxval)},10);
|
||||
}
|
||||
}
|
||||
|
||||
function
|
||||
svg_labelselect(evt)
|
||||
{
|
||||
var d = evt.target.ownerDocument;
|
||||
var sel = d.getElementById(evt.target.getAttribute("line_id"));
|
||||
var lid = evt.target.getAttribute("line_id");
|
||||
var sel = d.getElementById(lid);
|
||||
var tl = d.getElementById("svg_title");
|
||||
var cp = d.getElementById("svg_copy");
|
||||
var ps = d.getElementById("svg_paste");
|
||||
|
||||
clearTimeout(to);
|
||||
if(old_sel == sel) {
|
||||
sel.setAttribute("stroke-width", 1);
|
||||
old_sel = null;
|
||||
tl.firstChild.nodeValue = old_title;
|
||||
cp.firstChild.nodeValue = " ";
|
||||
ps.firstChild.nodeValue = " ";
|
||||
showOtherLines(d, lid, 0, 1);
|
||||
|
||||
} else {
|
||||
if(old_sel == null)
|
||||
@@ -122,6 +143,7 @@ svg_labelselect(evt)
|
||||
cp.firstChild.nodeValue = "Copy";
|
||||
ps.firstChild.nodeValue = (get_cookie()==""?" ":"Paste");
|
||||
}
|
||||
showOtherLines(d, lid, 1, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,43 @@
|
||||
<!-- will be included in each svg plot -->
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<linearGradient id="gr_bg" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFFFF7; stop-opacity:1"/>
|
||||
<stop offset="100%" style="stop-color:#FFFFC7; stop-opacity:1"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_0" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f00; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#f88; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#291; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#8f7; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_2" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#00f; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#88f; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_3" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#f0f; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#f8f; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_4" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#ff0; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#ff8; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_5" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0ff; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#8ff; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="gr_6" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#000; stop-opacity:.6"/>
|
||||
<stop offset="100%" style="stop-color:#ccc; stop-opacity:.4"/>
|
||||
</linearGradient>
|
||||
|
||||
</defs>
|
||||
|
||||
@@ -4,28 +4,27 @@ text { font-family:Times; font-size:12px; }
|
||||
text.title { font-size:16px; }
|
||||
text.copy { text-decoration:underline; stroke:none; fill:blue; }
|
||||
text.paste { text-decoration:underline; stroke:none; fill:blue; }
|
||||
text.l0 { text-decoration:underline; stroke:none; fill:red; }
|
||||
text.l1 { text-decoration:underline; stroke:none; fill:green; }
|
||||
text.l2 { text-decoration:underline; stroke:none; fill:blue; }
|
||||
text.l3 { text-decoration:underline; stroke:none; fill:magenta; }
|
||||
text.l4 { text-decoration:underline; stroke:none; fill:brown; }
|
||||
text.l5 { text-decoration:underline; stroke:none; fill:black; }
|
||||
text.l6 { text-decoration:underline; stroke:none; fill:olive; }
|
||||
text.l7 { text-decoration:underline; stroke:none; fill:gray; }
|
||||
text.l8 { text-decoration:underline; stroke:none; fill:yellow; }
|
||||
|
||||
polyline { stroke:black; fill:none; }
|
||||
.border { stroke:black; fill:url(#grad1); }
|
||||
.border { stroke:black; fill:url(#gr_bg); }
|
||||
.vgrid { stroke:gray; stroke-dasharray:2,6; }
|
||||
.hgrid { stroke:gray; stroke-dasharray:2,6; }
|
||||
.pasted { stroke:black; stroke-dasharray:1,1; }
|
||||
|
||||
.l0 { stroke:red; }
|
||||
.l1 { stroke:green; }
|
||||
.l2 { stroke:blue; }
|
||||
.l3 { stroke:magenta; }
|
||||
.l4 { stroke:brown; }
|
||||
.l5 { stroke:black; }
|
||||
.l6 { stroke:olive; }
|
||||
.l7 { stroke:gray; }
|
||||
.l8 { stroke:yellow; }
|
||||
.l0 { stroke:red; } text.l0 { stroke:none; fill:red; }
|
||||
.l1 { stroke:green; } text.l1 { stroke:none; fill:green; }
|
||||
.l2 { stroke:blue; } text.l2 { stroke:none; fill:blue; }
|
||||
.l3 { stroke:magenta; } text.l3 { stroke:none; fill:magenta; }
|
||||
.l4 { stroke:brown; } text.l4 { stroke:none; fill:brown; }
|
||||
.l5 { stroke:black; } text.l5 { stroke:none; fill:black; }
|
||||
.l6 { stroke:olive; } text.l6 { stroke:none; fill:olive; }
|
||||
.l7 { stroke:gray; } text.l7 { stroke:none; fill:gray; }
|
||||
.l8 { stroke:yellow; } text.l8 { stroke:none; fill:yellow; }
|
||||
|
||||
.l0fill{ stroke:#f00; fill:url(#gr_0); } text.l0fill{ stroke:none; fill:#f00; }
|
||||
.l1fill{ stroke:#291; fill:url(#gr_1); } text.l1fill{ stroke:none; fill:#291; }
|
||||
.l2fill{ stroke:#00f; fill:url(#gr_2); } text.l2fill{ stroke:none; fill:#00f; }
|
||||
.l3fill{ stroke:#f0f; fill:url(#gr_3); } text.l3fill{ stroke:none; fill:#f0f; }
|
||||
.l4fill{ stroke:#ff0; fill:url(#gr_4); } text.l4fill{ stroke:none; fill:#ff0; }
|
||||
.l5fill{ stroke:#0ff; fill:url(#gr_5); } text.l5fill{ stroke:none; fill:#0ff; }
|
||||
.l6fill{ stroke:#000; fill:url(#gr_6); } text.l6fill{ stroke:none; fill:#000; }
|
||||
|
||||
@@ -29,5 +29,5 @@ set ylabel "Humidity (%)"
|
||||
#FileLog 6:H\x3a:0:
|
||||
|
||||
plot \
|
||||
"< awk '/T:/ {print $1, $4}' <IN>" using 1:2 axes x1y2 title 'Temperature' with lines,\
|
||||
"< awk '/H:/ {print $1, $6}' <IN>" using 1:2 axes x1y1 title 'Humidity' with lines
|
||||
"< awk '/T:/ {print $1, $4}' <IN>" using 1:2 ls l0 axes x1y2 title 'Temperature' with lines,\
|
||||
"< awk '/H:/ {print $1, $6}' <IN>" using 1:2 ls l2fill axes x1y1 title 'Humidity' with lines
|
||||
|
||||
@@ -24,7 +24,7 @@ set yrange [0:]
|
||||
#FileLog 10:IR\x3a:0:delta-h
|
||||
#FileLog 10:IR\x3a:0:delta-d
|
||||
|
||||
plot "<IN>" using 1:4 axes x1y2 title 'Temperature' with lines,\
|
||||
plot "<IN>" using 1:4 axes x1y2 ls l0 title 'Temperature' with lines,\
|
||||
"<grep -v avg_ <IN> | perl -ane '\
|
||||
@a = split(\"[_:]\", $F[0]);\
|
||||
if(defined($lh) && $lh ne $a[1])\
|
||||
@@ -32,7 +32,7 @@ plot "<IN>" using 1:4 axes x1y2 title 'Temperature' with lines,\
|
||||
if($lv) { $hv += ($F[9]-$lv); }\
|
||||
$lh = $a[1]; $ld = $a[0]; $lv = $F[9];\
|
||||
END { printf(\"${ld}_$lh:30:00 %f\n\", $hv) }'"\
|
||||
using 1:2 axes x1y1 title 'Rain/h' with histeps,\
|
||||
using 1:2 axes x1y1 ls l1fill title 'Rain/h' with histeps,\
|
||||
"<grep -v avg_ <IN> | perl -ane '\
|
||||
@a = split(\"[_]\", $F[0]);\
|
||||
if(defined($ld) && $ld ne $a[0]) {\
|
||||
@@ -40,4 +40,4 @@ plot "<IN>" using 1:4 axes x1y2 title 'Temperature' with lines,\
|
||||
if($lv) { $dv += ($F[9]-$lv); }\
|
||||
$ld = $a[0]; $lv = $F[9];\
|
||||
END {printf(\"${ld}_12:00:00 %f\n\", $dv)}'"\
|
||||
using 1:2 axes x1y1 title 'Rain/day' with histeps
|
||||
using 1:2 axes x1y1 ls l2 title 'Rain/day' with histeps
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#################################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2008-2009
|
||||
# (c) 2008-2012
|
||||
# Copyright: Dr. Olaf Droegehorn
|
||||
# o.droegehorn@dhs-computertechnik.de
|
||||
# www.dhs-computertechnik.de
|
||||
@@ -52,8 +52,8 @@ sub FHEMRENDERER_setAttr($$);
|
||||
sub FHEMRENDERER_parseXmlList($);
|
||||
sub FHEMRENDERER_render($);
|
||||
sub FHEMRENDERER_fatal($);
|
||||
sub pF($@);
|
||||
sub pO(@);
|
||||
sub FHEMRENDERER_pF($@);
|
||||
sub FHEMRENDERER_pO(@);
|
||||
#sub FHEMRENDERER_zoomLink($$$$);
|
||||
sub FHEMRENDERER_calcWeblink($$);
|
||||
|
||||
@@ -221,7 +221,7 @@ FHEMRENDERER_Get($@)
|
||||
if($t eq "fileplot") {
|
||||
my @va = split(":", $v, 3);
|
||||
if(@va != 3 || !$__devs{$va[0]}{INT}{currentlogfile}) {
|
||||
pO "<td>Broken definition: $v</a></td>";
|
||||
FHEMRENDERER_pO "<td>Broken definition: $v</a></td>";
|
||||
} else {
|
||||
if($va[2] eq "CURRENT") {
|
||||
$__devs{$va[0]}{INT}{currentlogfile}{VAL} =~ m,([^/]*)$,;
|
||||
@@ -295,7 +295,7 @@ FHEMRENDERER_parseXmlList($)
|
||||
%__types = ();
|
||||
$__title = "";
|
||||
|
||||
foreach my $l (split("\n", fC("xmllist"))) {
|
||||
foreach my $l (split("\n", FHEMRENDERER_fC("xmllist"))) {
|
||||
|
||||
####### Device
|
||||
if($l =~ m/^\t\t<(.*) name="(.*)" state="(.*)" sets="(.*)" attrs="(.*)">/){
|
||||
@@ -428,7 +428,7 @@ FHEMRENDERER_render($)
|
||||
|
||||
my ($f,$t)=($__devs{$d}{from}, $__devs{$d}{to});
|
||||
|
||||
my @path = split(" ", fC("get $d $file $FHEMRENDERER_tmpfile$wl $f $t " .
|
||||
my @path = split(" ", FHEMRENDERER_fC("get $d $file $FHEMRENDERER_tmpfile$wl $f $t " .
|
||||
join(" ", @filelog)));
|
||||
my $i = 0;
|
||||
$plot =~ s/\".*?using 1:[^ ]+ /"\"$path[$i++]\" using 1:2 "/gse;
|
||||
@@ -466,7 +466,7 @@ FHEMRENDERER_render($)
|
||||
# $f = 0 if(!$f); # From the beginning of time...
|
||||
# $t = 9 if(!$t); # till the end
|
||||
#
|
||||
# my $ret = fC("get $d $file INT $f $t " . join(" ", @filelog));
|
||||
# my $ret = FHEMRENDERER_fC("get $d $file INT $f $t " . join(" ", @filelog));
|
||||
# SVG_render($file, $__plotsize, $f, $t, \@data, $internal_data, $plot);
|
||||
#
|
||||
# open (FH, ">$FHEMRENDERER_tmpfile$wl.svg");
|
||||
@@ -482,13 +482,13 @@ sub
|
||||
FHEMRENDERER_fatal($)
|
||||
{
|
||||
my ($msg) = @_;
|
||||
pO "<html><body>$msg</body></html>";
|
||||
FHEMRENDERER_pO "<html><body>$msg</body></html>";
|
||||
}
|
||||
|
||||
##################
|
||||
# print formatted
|
||||
sub
|
||||
pF($@)
|
||||
FHEMRENDERER_pF($@)
|
||||
{
|
||||
my $fmt = shift;
|
||||
$__RET .= sprintf $fmt, @_;
|
||||
@@ -497,7 +497,7 @@ pF($@)
|
||||
##################
|
||||
# print output
|
||||
sub
|
||||
pO(@)
|
||||
FHEMRENDERER_pO(@)
|
||||
{
|
||||
$__RET .= shift;
|
||||
}
|
||||
@@ -505,7 +505,7 @@ pO(@)
|
||||
##################
|
||||
# fhem command
|
||||
sub
|
||||
fC($)
|
||||
FHEMRENDERER_fC($)
|
||||
{
|
||||
my ($cmd) = @_;
|
||||
#Log 0, "Calling $cmd";
|
||||
|
||||
@@ -1,189 +1,185 @@
|
||||
Description of the 02_FHEMRENDERER Module:
|
||||
|
||||
(c) Olaf Droegehorn
|
||||
o.droegehorn@dhs-computertechnik.de
|
||||
www.dhs-computertechnik.de
|
||||
|
||||
#################################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2008-2009
|
||||
# Copyright: Dr. Olaf Droegehorn
|
||||
# All rights reserved
|
||||
#
|
||||
# This script free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||
#
|
||||
# This script is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
#################################################################################
|
||||
|
||||
Versions:
|
||||
|
||||
V1.0: Initial Version
|
||||
V1.1: Bugfix: Enabled multiple RENERER Instances
|
||||
|
||||
General Description:
|
||||
|
||||
The FHEMRENDERER module is intended to render (draw) graphics based on the FHEM Log-Files.
|
||||
This can be done either based on a timer (used in the module) or based on a direct call of GET.
|
||||
The rendered graphics will be stored in a pre-defined directory with a predefined prefix of the files.
|
||||
|
||||
The FHEMRENDERER uses attributes to control the behaviour:
|
||||
plotmode gnuplot
|
||||
plotsize 800,200
|
||||
refresh 00:10:00
|
||||
room Unsorted
|
||||
status off
|
||||
tmpfile /tmp/
|
||||
multiprocess off
|
||||
|
||||
These attributes have the following meaning:
|
||||
plotmode, plotsize: Control gnuplot and the desired output
|
||||
refresh: type HH:MM:SS, is the time-interval in which the re-rendering is done
|
||||
status: Tells if the timer-based re-rendering is on/off
|
||||
tmpfile: Is the path (and prefix, if given) of the graphic-files, that will be rendered
|
||||
multiprocess: You can set "on" or "off". If multiprocess is "on" the time-scheduled renderings will be done in a
|
||||
mutli-process manner, and FHEM will not be blocked by the rendering process.
|
||||
|
||||
NOTE: The timer-based rendering renders ONLY those fileplots, for which you have defined a WebLink !
|
||||
See WebLink for more details on how to define.
|
||||
|
||||
NOTE: At the moment the renderer supports only GNUPLOT, meaning gnuplot is used to draw the graphics. The supported
|
||||
modes are (gnuplot and gnuplot-scroll).
|
||||
|
||||
|
||||
Supported commands are:
|
||||
|
||||
define/set/get/attributes
|
||||
|
||||
|
||||
|
||||
DEFINE
|
||||
|
||||
define <name> <type> <type-specific>
|
||||
|
||||
Define a device. You need devices if you want to manipulate them (e.g. set on/off).
|
||||
Use "define <name> ?" to get a list of possible types.
|
||||
|
||||
Type FHEMRENDERER
|
||||
|
||||
define <name> FHEMRENDERER [global]
|
||||
|
||||
This defines a new "device", that is of type FHEMRENDERER. The option 'global' can be used if needed for sorting reasons.
|
||||
Otherwise this option has no real meaning for FHEMRENDERER.
|
||||
|
||||
As a side-effect of defining this "device" the following attributes will be set for this "device":
|
||||
plotmode gnuplot
|
||||
plotsize 800,200
|
||||
refresh 00:10:00
|
||||
room Unsorted
|
||||
status off
|
||||
tmpfile /tmp/
|
||||
multiprocess off
|
||||
|
||||
|
||||
NOTE: The Logfile will report (with LogLevel 2) that the FHEMRENDERER has been defined.
|
||||
|
||||
Examples:
|
||||
define renderer FHEMRENDERER
|
||||
|
||||
SET
|
||||
|
||||
set <name> <type-specific>
|
||||
|
||||
Set parameters of a device / send signals to a device. You can get a list of possible commands by
|
||||
set <name> ?
|
||||
|
||||
Type FHEMRENDERER:
|
||||
set FHEMRENDERER on/off
|
||||
|
||||
This switches the timer-based rendering on/off. The attribute 'status' will be modified accordingly.
|
||||
NOTE: only WebLink based graphics will be rendered.
|
||||
|
||||
GET
|
||||
get <name> <type-specific>
|
||||
|
||||
Ask a value directly from the device, and wait for an answer. In general, you can get a list of possible commands by
|
||||
get <device> ?
|
||||
|
||||
Type FHEMRENDERER:
|
||||
get FHEMRENDERER [[file-name] device type logfile [pos=zoom=XX&off=YYY]]
|
||||
|
||||
the get function supports different sets of arguments:
|
||||
Arguments:
|
||||
NONE: all WebLink based FilePlots will be rerendered
|
||||
The resulting filename will be '<attr-tmpfile><weblinkname>.png'
|
||||
THREE: '<device> <type> <logfile>'
|
||||
In this case only one specific graphic will be rendered:
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The resulting filename will be '<attr-tmpfile>logfile.png'
|
||||
FOUR: '<file-name> <device> <type> <logfile>'
|
||||
In this case only one specific graphic will be rendered:
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The resulting filename will be '<attr-tmpfile><file-name>.png'
|
||||
FIVE: '<file-name> <device> <type> <logfile> pos=zoom=XX&off=YYY'
|
||||
In this case only one specific graphic will be rendered assuming that plotmode is 'gnuplot-scroll':
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The 'zoom' will be either qday/day/week/month/year (same as used in FHEMWEB).
|
||||
The offset 'off' is either 0 (then the second part can be omitted, or -1/-2.... to jump back in time.
|
||||
The resulting filename will be '<attr-tmpfile><file-name>.png'
|
||||
|
||||
NOTE: If you want to use zoom AND offset then you have to concatenate via '&' !!
|
||||
|
||||
NOTE: combinations are possible in limited ranges:
|
||||
meaning: you can add the 'pos=zoom=XX&off=YY' to any of the first three sets.
|
||||
This may e.g. result in rendering all WebLinks with a specific zoom or offset
|
||||
(if you just pass the 'pos=zoom=xx&off=yy' parameter);
|
||||
|
||||
Any rendered image (one or all WebLinks) will be stored in <attr-tmpfile> followed by a filename'.png'. The filename will be
|
||||
either derived (from weblink-name or logfile-name) or, for single files, can be assigend.
|
||||
|
||||
Examples:
|
||||
get FHEMRENDERER
|
||||
get FHEMRENDERER pos=zoom=week&off=-1
|
||||
get FHEMRENDERER azlog fht az-2008-38.log pos=zoom=0
|
||||
get FHEMRENDERER livingroomgraphics wzlog fht wz-2008-38.log pos=zoom=0
|
||||
|
||||
|
||||
ATTR
|
||||
|
||||
attr <name> <attrname> <value>
|
||||
|
||||
Set an attribute to something defined by define.
|
||||
Use "attr <name> ?" to get a list of possible attributes.
|
||||
|
||||
Type FHEMRENDERER:
|
||||
attr FHEMRENDERER <attrname> <value>
|
||||
|
||||
Attributes: <Values>
|
||||
plotmode gnuplot / gnuplot-scroll
|
||||
plotsize Dimension of graphic e.g. 800,200
|
||||
refresh Timer-Interval for rerendering (HH:MM:SS)
|
||||
status Status of the Timer (off/on)
|
||||
tmpfile Path and prefix of for the rendered graphics (e.g. /tmp/)
|
||||
multiprocess on / off
|
||||
|
||||
|
||||
|
||||
|
||||
Installation:
|
||||
Copy the file pgm5/02_FHEMRENDERER.pm to the installed FHEM directory.
|
||||
This gives you a graphic rendering engine (gnuplot & gnuplot-scroll at the moment), which can be configured to renderer images in intervals.
|
||||
|
||||
The *.gplot files should be reused from the built-in FHEMWEB and should reside in the installed FHEM directory. Here we don't provide specific *.gplot files as the mechanisms are exactly the same.
|
||||
|
||||
If you want to have access to plotted logs, then make sure that gnuplot is installed and set the logtype for the FileLog device (see commandref.html and example/04_log).
|
||||
Copy the file contrib/99_weblink.pm to the installed FHEM directory.
|
||||
|
||||
Description of the 02_FHEMRENDERER Module:
|
||||
|
||||
(c) Olaf Droegehorn
|
||||
o.droegehorn@dhs-computertechnik.de
|
||||
www.dhs-computertechnik.de
|
||||
|
||||
#################################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2008-2011
|
||||
# Copyright: Dr. Olaf Droegehorn
|
||||
# All rights reserved
|
||||
#
|
||||
# This script free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||
#
|
||||
# This script is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
#################################################################################
|
||||
|
||||
Versions:
|
||||
|
||||
V1.0: Initial Version
|
||||
V1.1: Bugfix: Enabled multiple RENERER Instances
|
||||
V1.2: Bugfix: Corrected Function-Names to avoid collision with PGM2
|
||||
|
||||
General Description:
|
||||
|
||||
The FHEMRENDERER module is intended to render (draw) graphics based on the FHEM Log-Files.
|
||||
This can be done either based on a timer (used in the module) or based on a direct call of GET.
|
||||
The rendered graphics will be stored in a pre-defined directory with a predefined prefix of the files.
|
||||
|
||||
The FHEMRENDERER uses attributes to control the behaviour:
|
||||
plotmode gnuplot
|
||||
plotsize 800,200
|
||||
refresh 00:10:00
|
||||
room Unsorted
|
||||
status off
|
||||
tmpfile /tmp/
|
||||
|
||||
These attributes have the following meaning:
|
||||
plotmode, plotsize: Control gnuplot and the desired output
|
||||
refresh: type HH:MM:SS, is the time-interval in which the re-rendering is done
|
||||
status: Tells if the timer-based re-rendering is on/off
|
||||
tmpfile: Is the path (and prefix, if given) of the graphic-files, that will be rendered
|
||||
|
||||
NOTE: The timer-based rendering renders ONLY those fileplots, for which you have defined a WebLink !
|
||||
See WebLink for more details on how to define.
|
||||
|
||||
NOTE: At the moment the renderer supports only GNUPLOT, meaning gnuplot is used to draw the graphics. The supported
|
||||
modes are (gnuplot and gnuplot-scroll).
|
||||
|
||||
|
||||
Supported commands are:
|
||||
|
||||
define/set/get/attributes
|
||||
|
||||
|
||||
|
||||
DEFINE
|
||||
|
||||
define <name> <type> <type-specific>
|
||||
|
||||
Define a device. You need devices if you want to manipulate them (e.g. set on/off).
|
||||
Use "define <name> ?" to get a list of possible types.
|
||||
|
||||
Type FHEMRENDERER
|
||||
|
||||
define <name> FHEMRENDERER [global]
|
||||
|
||||
This defines a new "device", that is of type FHEMRENDERER. The option 'global' can be used if needed for sorting reasons.
|
||||
Otherwise this option has no real meaning for FHEMRENDERER.
|
||||
|
||||
As a side-effect of defining this "device" the following attributes will be set for this "device":
|
||||
plotmode gnuplot
|
||||
plotsize 800,200
|
||||
refresh 00:10:00
|
||||
room Unsorted
|
||||
status off
|
||||
tmpfile /tmp/
|
||||
|
||||
|
||||
NOTE: The Logfile will report (with LogLevel 2) that the FHEMRENDERER has been defined.
|
||||
|
||||
Examples:
|
||||
define renderer FHEMRENDERER
|
||||
|
||||
SET
|
||||
|
||||
set <name> <type-specific>
|
||||
|
||||
Set parameters of a device / send signals to a device. You can get a list of possible commands by
|
||||
set <name> ?
|
||||
|
||||
Type FHEMRENDERER:
|
||||
set FHEMRENDERER on/off
|
||||
|
||||
This switches the timer-based rendering on/off. The attribute 'status' will be modified accordingly.
|
||||
NOTE: only WebLink based graphics will be rendered.
|
||||
|
||||
GET
|
||||
get <name> <type-specific>
|
||||
|
||||
Ask a value directly from the device, and wait for an answer. In general, you can get a list of possible commands by
|
||||
get <device> ?
|
||||
|
||||
Type FHEMRENDERER:
|
||||
get FHEMRENDERER [[file-name] device type logfile [pos=zoom=XX&off=YYY]]
|
||||
|
||||
the get function supports different sets of arguments:
|
||||
Arguments:
|
||||
NONE: all WebLink based FilePlots will be rerendered
|
||||
The resulting filename will be '<attr-tmpfile><weblinkname>.png'
|
||||
THREE: '<device> <type> <logfile>'
|
||||
In this case only one specific graphic will be rendered:
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The resulting filename will be '<attr-tmpfile>logfile.png'
|
||||
FOUR: '<file-name> <device> <type> <logfile>'
|
||||
In this case only one specific graphic will be rendered:
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The resulting filename will be '<attr-tmpfile><file-name>.png'
|
||||
FIVE: '<file-name> <device> <type> <logfile> pos=zoom=XX&off=YYY'
|
||||
In this case only one specific graphic will be rendered assuming that plotmode is 'gnuplot-scroll':
|
||||
A graphic will be rendered for 'device', where device is a FileLog, based on the type 'type' based on the given 'logfile'
|
||||
The 'zoom' will be either qday/day/week/month/year (same as used in FHEMWEB).
|
||||
The offset 'off' is either 0 (then the second part can be omitted, or -1/-2.... to jump back in time.
|
||||
The resulting filename will be '<attr-tmpfile><file-name>.png'
|
||||
|
||||
NOTE: If you want to use zoom AND offset then you have to concatenate via '&' !!
|
||||
|
||||
NOTE: combinations are possible in limited ranges:
|
||||
meaning: you can add the 'pos=zoom=XX&off=YY' to any of the first three sets.
|
||||
This may e.g. result in rendering all WebLinks with a specific zoom or offset
|
||||
(if you just pass the 'pos=zoom=xx&off=yy' parameter);
|
||||
|
||||
Any rendered image (one or all WebLinks) will be stored in <attr-tmpfile> followed by a filename'.png'. The filename will be
|
||||
either derived (from weblink-name or logfile-name) or, for single files, can be assigend.
|
||||
|
||||
Examples:
|
||||
get FHEMRENDERER
|
||||
get FHEMRENDERER pos=zoom=week&off=-1
|
||||
get FHEMRENDERER azlog fht az-2008-38.log pos=zoom=0
|
||||
get FHEMRENDERER livingroomgraphics wzlog fht wz-2008-38.log pos=zoom=0
|
||||
|
||||
|
||||
ATTR
|
||||
|
||||
attr <name> <attrname> <value>
|
||||
|
||||
Set an attribute to something defined by define.
|
||||
Use "attr <name> ?" to get a list of possible attributes.
|
||||
|
||||
Type FHEMRENDERER:
|
||||
attr FHEMRENDERER <attrname> <value>
|
||||
|
||||
Attributes: <Values>
|
||||
plotmode gnuplot / gnuplot-scroll
|
||||
plotsize Dimension of graphic e.g. 800,200
|
||||
refresh Timer-Interval for rerendering (HH:MM:SS)
|
||||
status Status of the Timer (off/on)
|
||||
tmpfile Path and prefix of for the rendered graphics (e.g. /tmp/)
|
||||
|
||||
|
||||
|
||||
|
||||
Installation:
|
||||
Copy the file pgm5/02_FHEMRENDERER.pm to the installed FHEM directory.
|
||||
This gives you a graphic rendering engine (gnuplot & gnuplot-scroll at the moment), which can be configured to renderer images in intervals.
|
||||
|
||||
The *.gplot files should be reused from the built-in FHEMWEB and should reside in the installed FHEM directory. Here we don't provide specific *.gplot files as the mechanisms are exactly the same.
|
||||
|
||||
If you want to have access to plotted logs, then make sure that gnuplot is installed and set the logtype for the FileLog device (see commandref.html and example/04_log).
|
||||
Copy the file contrib/99_weblink.pm to the installed FHEM directory.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Description of the pgm5 webfrontend:
|
||||
#################################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2008-2009
|
||||
# (c) 2008-2012
|
||||
# Copyright: Dr. Olaf Droegehorn
|
||||
# All rights reserved
|
||||
#
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#################################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2008-2009
|
||||
# (c) 2008-2012
|
||||
# Copyright: Dr. Olaf Droegehorn
|
||||
# All rights reserved
|
||||
#
|
||||
@@ -572,6 +572,9 @@ showRoom()
|
||||
# Print the table headers
|
||||
my $t = $type;
|
||||
$t = "EM" if($t =~ m/^EM.*$/);
|
||||
if (!(($t eq "FS20") || ($t eq "IT") || ($t eq "FHT") || ($t eq "FileLog") || ($t eq "at") || ($t eq "notify") || ($t eq "KS300") || ($t eq "FHZ") || ($t eq "FHEMWEB") || ($t eq "EM") || ($t eq "FHEMRENDERER") || ($t eq "weblink"))) {
|
||||
$t = "_internal_";
|
||||
}
|
||||
print " <table class=\"$t\" summary=\"List of $type devices\">\n";
|
||||
|
||||
if($type eq "FS20") {
|
||||
@@ -579,6 +582,11 @@ showRoom()
|
||||
print "<th colspan=\"2\">Set to</th>";
|
||||
print "</tr>\n";
|
||||
}
|
||||
if($type eq "IT") {
|
||||
print " <tr><th>IT dev.</th><th>State</th>";
|
||||
print "<th colspan=\"2\">Set to</th>";
|
||||
print "</tr>\n";
|
||||
}
|
||||
if($type eq "FHT") {
|
||||
print " <tr><th>FHT dev.</th><th>Measured</th>";
|
||||
print "<th>Set to</th>";
|
||||
@@ -607,7 +615,7 @@ showRoom()
|
||||
|
||||
my $v = $devs{$d}{state};
|
||||
|
||||
if($type eq "FS20") {
|
||||
if(($type eq "FS20") || ($type eq "IT")) {
|
||||
|
||||
my $v = $devs{$d}{state};
|
||||
my $iv = $v;
|
||||
@@ -651,7 +659,7 @@ showRoom()
|
||||
$v = int($v*20)/$v if($v =~ m/^[0-9].$/);
|
||||
print $q->hidden("arg.$d", "desired-temp");
|
||||
print $q->hidden("dev.$d", $d);
|
||||
print "<td>" .
|
||||
print "<td align=\"center\">" .
|
||||
$q->popup_menu(-name=>"val.$d", -values=>\@tv, -default=>$v) .
|
||||
$q->submit(-name=>"cmd.$d", -value=>"set") . "</td>";
|
||||
|
||||
|
||||
BIN
webfrontend/pgm5/icons/IT.off.gif
Normal file
|
After Width: | Height: | Size: 609 B |
BIN
webfrontend/pgm5/icons/IT.on.gif
Normal file
|
After Width: | Height: | Size: 377 B |
@@ -1,44 +1,50 @@
|
||||
body { color: black; background: #FFFFD7; }
|
||||
|
||||
table { -moz-border-radius:8px; }
|
||||
|
||||
table.room { border:thin solid; width: 100%; background: #D7FFFF; }
|
||||
table.room tr.sel { background: #A0FFFF; }
|
||||
|
||||
table.at { border:thin solid; width: 100%; background: #FFFFC0; }
|
||||
table.at tr.odd { background: #FFFFD7; }
|
||||
|
||||
table.notify { border:thin solid; width: 100%; background: #D7D7A0; }
|
||||
table.notify tr.odd { background: #FFFFC0; }
|
||||
|
||||
table.FileLog { border:thin solid; width: 100%; background: #FFC0C0; }
|
||||
table.FileLog tr.odd { background: #FFD7D7; }
|
||||
|
||||
table._internal_ { border:thin solid; width: 100%; background: #C0C0C0; }
|
||||
table._internal_ tr.odd { background: #D7D7D7; }
|
||||
|
||||
table.FS20 { border:thin solid; width: 100%; background: #C0FFFF; }
|
||||
table.FS20 tr.odd { background: #D7FFFF; }
|
||||
|
||||
table.FHT { border:thin solid; width: 100%; background: #FFC0C0; }
|
||||
table.FHT tr.odd { background: #FFD7D7; }
|
||||
|
||||
table.KS300 { border:thin solid; width: 100%; background: #C0FFC0; }
|
||||
table.KS300 tr.odd { background: #A7FFA7; }
|
||||
|
||||
table.FHZ { border:thin solid; width: 100%; background: #C0C0C0; }
|
||||
table.FHZ tr.odd { background: #D7D7D7; }
|
||||
|
||||
table.EM { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.EM tr.odd { background: #F0F0F0; }
|
||||
|
||||
table.FHEMWEB { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.FHEMWEB tr.odd { background: #F0F0F0; }
|
||||
|
||||
table.FHEMRENDERER { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.FHEMRENDERER tr.odd { background: #F0F0F0; }
|
||||
|
||||
#hdr { position:absolute; top:10px; left:10px; }
|
||||
#left { position:absolute; top:50px; left:10px; width:130px; }
|
||||
#right { position:absolute; top:50px; left:160px;
|
||||
right:10px; bottom:10px; overflow:auto; }
|
||||
body { color: black; background: #FFFFD7; }
|
||||
|
||||
table { -moz-border-radius:8px; }
|
||||
table { border:thin solid; width: 100%; background: #EAEAEA; }
|
||||
table tr.odd { background: #F5F5F5; }
|
||||
|
||||
|
||||
table.room { border:thin solid; width: 100%; background: #D7FFFF; }
|
||||
table.room tr.sel { background: #A0FFFF; }
|
||||
|
||||
table.at { border:thin solid; width: 100%; background: #FFFFC0; }
|
||||
table.at tr.odd { background: #FFFFD7; }
|
||||
|
||||
table.notify { border:thin solid; width: 100%; background: #D7D7A0; }
|
||||
table.notify tr.odd { background: #FFFFC0; }
|
||||
|
||||
table.FileLog { border:thin solid; width: 100%; background: #FFC0C0; }
|
||||
table.FileLog tr.odd { background: #FFD7D7; }
|
||||
|
||||
table._internal_ { border:thin solid; width: 100%; background: #EAEAEA; }
|
||||
table._internal_ tr.odd { background: #F5F5F5; }
|
||||
|
||||
table.FS20 { border:thin solid; width: 100%; background: #C0FFFF; }
|
||||
table.FS20 tr.odd { background: #D7FFFF; }
|
||||
|
||||
table.IT { border:thin solid; width: 100%; background: #C0FFFF; }
|
||||
table.IT tr.odd { background: #D7FFFF; }
|
||||
|
||||
table.FHT { border:thin solid; width: 100%; background: #FFC0C0; }
|
||||
table.FHT tr.odd { background: #FFD7D7; }
|
||||
|
||||
table.KS300 { border:thin solid; width: 100%; background: #C0FFC0; }
|
||||
table.KS300 tr.odd { background: #A7FFA7; }
|
||||
|
||||
table.FHZ { border:thin solid; width: 100%; background: #C0C0C0; }
|
||||
table.FHZ tr.odd { background: #D7D7D7; }
|
||||
|
||||
table.EM { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.EM tr.odd { background: #F0F0F0; }
|
||||
|
||||
table.FHEMWEB { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.FHEMWEB tr.odd { background: #F0F0F0; }
|
||||
|
||||
table.FHEMRENDERER { border:thin solid; width: 100%; background: #E0E0E0; }
|
||||
table.FHEMRENDERER tr.odd { background: #F0F0F0; }
|
||||
|
||||
#hdr { position:absolute; top:10px; left:10px; }
|
||||
#left { position:absolute; top:50px; left:10px; width:130px; }
|
||||
#right { position:absolute; top:50px; left:160px;
|
||||
right:10px; bottom:10px; overflow:auto; }
|
||||
|
||||