53 Commits

Author SHA1 Message Date
Marc Hoppe
776a4e28c5 ... 2012-01-29 18:44:41 +01:00
Marc Hoppe
0c772d4224 Merge remote-tracking branch 'vdr/eo_baseid' into enocean
Conflicts:
	FHEM/10_EnOcean.pm
2012-01-29 18:37:28 +01:00
Marc Hoppe
06da7b1621 Merge branch 'master' into enocean 2012-01-29 18:17:20 +01:00
Marc Hoppe
4bdf589e21 EnOcean: if there is an 'attr' 'subId' use it for sending data to an Actuator
There is no subType 'dimmer' and 'dimmctrl' anymore these are replaced by 'eltakoDimmer'.
 The commands supported are 'dimto' and 'teach'
 The readings which will be set are 'state' and 'value'
2012-01-29 18:00:45 +01:00
Marc Hoppe
2e97eb1b45 00_TCM: Read Base-Id of Tcm on start 2012-01-28 22:05:16 +01:00
odroegehorn
0191306700 Corrected ON/OFF Codes for InterTechno
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1222 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-26 01:02:11 +00:00
rudolfkoenig
aeae590475 HM-LC-SW4-DR confirmed by fhem-hm-knecht
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1221 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-25 07:01:22 +00:00
hotmaz
673898864c do not allow setting values for dummy values
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1220 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-24 19:53:16 +00:00
klassm
cce69b1c55 CUL_FHTTK bugfix
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1219 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-24 19:09:45 +00:00
klassm
6277e551a2 CUL_FHTTK previous state
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1218 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-24 17:45:57 +00:00
odroegehorn
618ed19f2a Corrected weblinks
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1217 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-24 10:26:47 +00:00
odroegehorn
dd17d99373 Added support for InterTechno Module + added tables for unkown/new devices
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1216 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-24 10:17:38 +00:00
real-wusel
a6e70be3c5 Support for Telldus TellStick
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1215 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 21:36:46 +00:00
rudolfkoenig
8a5ef48724 Some animation added
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1214 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 21:07:25 +00:00
to-be
ec94aa34ec Solarview: now also reading IDC*, UDC* and grid*
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1213 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 18:54:35 +00:00
odroegehorn
fb240641f7 Added documentation for InterTechno Module
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1212 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 18:20:15 +00:00
odroegehorn
f9553f3ec8 InterTechno Module
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1211 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 18:19:34 +00:00
real-wusel
8d3a7853ed Initial support for sispmctl V3 (new output format)
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1210 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 17:12:01 +00:00
rudolfkoenig
ee7f23de10 fwmodpath documented
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1209 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-23 14:01:34 +00:00
klassm
76b3c3a2a1 WOL one more bugfix for refreshing the state
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1208 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 16:14:15 +00:00
klassm
00880e422a WOL module status did not work on FritzBoxes
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1207 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 15:55:35 +00:00
borisneubert
36bddf5725 fix of fix for CHANGED reading handling in 59_Weather.pm
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1206 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 15:54:51 +00:00
klassm
0cb0232351 update WOL log levels
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1205 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 12:22:25 +00:00
rudolfkoenig
2b62b6128f Better setgid by Christopher
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1204 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 12:21:05 +00:00
rudolfkoenig
941263cb0e First version
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1203 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 09:08:47 +00:00
rudolfkoenig
ce2cb19e5b SVG/WOL
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1202 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 08:50:48 +00:00
rudolfkoenig
d5983fc22e Wake on Lan module from Matthias K.
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1201 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-22 08:50:17 +00:00
rudolfkoenig
3e948fdaae Dark-Style fixes
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1200 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 12:29:14 +00:00
rudolfkoenig
dd7b32827a Documenting svg-style
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1199 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 12:14:26 +00:00
rudolfkoenig
2f84ace39c Typo
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1198 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 12:13:21 +00:00
borisneubert
4660d062e9 Readings update via CHANGED to fix a side effect from changes in DoTrigger from Oct 2011.
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1197 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 11:59:15 +00:00
rudolfkoenig
8ac3a56234 Fill function added, linestyle fixed
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1196 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 09:47:01 +00:00
rudolfkoenig
98d03d4cbf Better debug output for broken definition
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1195 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 09:44:52 +00:00
rudolfkoenig
3c30fb995b Delay for structure send
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1194 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-21 09:43:50 +00:00
rudolfkoenig
b14869a491 desired-temp list moved from FHEMWEB to the source module.
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1193 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-20 18:12:56 +00:00
rudolfkoenig
4d20a21753 typo
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1192 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-20 17:13:00 +00:00
rudolfkoenig
6366ccd411 Kai's diff tuned and added
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1191 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-19 19:10:05 +00:00
to-be
eeaaaec733 Small fix in 70_SolarView.pm
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1190 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-18 13:28:49 +00:00
to-be
1fbe536aff Small fix in 70_SolarView.pm
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1189 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-18 12:17:39 +00:00
to-be
1305adabbd Added new Modul 70_SolarView.pm to contrib.
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1188 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-18 10:34:03 +00:00
rudolfkoenig
fa35186208 This file got lost somewhere
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1187 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-13 18:48:41 +00:00
rudolfkoenig
6bee66c4a5 jsonp
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1186 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-11 21:45:19 +00:00
rudolfkoenig
1d4c6ba5bd cfg and holiday added to Edit Files (request by MichaS)
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1185 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-11 21:06:39 +00:00
rudolfkoenig
87502bdf70 HM-CC-TC desired-temp
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1184 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-08 19:11:40 +00:00
rudolfkoenig
00c4f39773 chown is always done now
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1183 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 15:12:26 +00:00
rudolfkoenig
12db1fa3ba fb7390 fixes/tests
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1182 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 12:18:10 +00:00
rudolfkoenig
5f0d12bbef Bugfix for multi-channel HM autocreate
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1181 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 12:17:29 +00:00
odroegehorn
3f4c2893a2 Fixed Issues with PGM2 (Function-Names)
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1180 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 12:07:47 +00:00
rudolfkoenig
0a5f244905 usb tries to flash unflashed devices
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1179 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 11:32:13 +00:00
rudolfkoenig
f489fb1173 Smaller files (Christoph B)
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1178 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 09:01:09 +00:00
rudolfkoenig
8be181ebad Bugfix: Error message in inform when multi-channel hm device is created
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1177 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 09:00:32 +00:00
borisneubert
24437a97bc added { $currlogfile } to attr global logfile documentation
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1176 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 08:59:01 +00:00
rudolfkoenig
31ee2e5a1f get needs to check for filename change too
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@1175 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-01-07 08:19:15 +00:00
50 changed files with 10627 additions and 8710 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ sub EnOcean_Initialize($);
sub EnOcean_Parse($$);
sub EnOcean_Set($@);
sub EnOcean_MD15Cmd($$$);
sub EnOcean_GetMyDeviceId($);
my %rorgname = ("F6"=>"switch", # RPS
"D5"=>"contact", # 1BS
@@ -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
View 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -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 "&lt;path-of-the-stick&gt;/fhem/startfhem" >> /var/flash/debug.cfg
echo "&lt;path-of-the-stick&gt;/fhem/startfhem" > /var/flash/debug.cfg
<li>Start fhem manually with "&lt;path-of-the-stick&gt;/fhem/startfhem"
</ul>
</ul>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 247 KiB

View File

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

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

View File

@@ -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") {

View File

@@ -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\"/>";
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ Description of the pgm5 webfrontend:
#################################################################################
# Copyright notice
#
# (c) 2008-2009
# (c) 2008-2012
# Copyright: Dr. Olaf Droegehorn
# All rights reserved
#

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

View File

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