2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2024-11-22 09:49:50 +00:00

FHEMWEB_JS_UMBAU: integrate it with the trunk

git-svn-id: https://svn.fhem.de/fhem/trunk@7496 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2015-01-10 16:54:23 +00:00
parent 912b31ae50
commit 32f37c18fe
29 changed files with 1840 additions and 1290 deletions

View File

@ -1,24 +1,35 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: fhemweb.js rewrite based on jQuery, single-widget-implementation
- feature: SVG: multiple sources allowed, Plot-Editor
- feature: textfield-long and knob widgets
- added: some new icons by Rampler
- feature: PRESENCE: new reading "presence" which contains the current (or last known)
presence state (can be "absent" or "present")
- bugfix: 70_Jabber.pm: hardening XML::Stream Process() call and fix of ssl_verify
- feature: readingsGroup: allow devspec :FILTER= expressions in device selection
- feature: PRESENCE: new reading "presence" which contains the current (or
last known) presence state (can be "absent" or "present")
- bugfix: 70_Jabber.pm: hardening XML::Stream Process() call and fix of
ssl_verify
- feature: readingsGroup: allow devspec :FILTER= expressions in device
selection
- added: 73_km200.pm for the Buderus KM200 heating controller (Sailor)
- feature: 70_XBMC: added command 'connect' to connect instantly
- change: FB_CALLMONITOR: use standard file read/write function to support use of configDb
- bugfix: FB_CALLMONITOR: fix phonebook file read when using configDb (Forum #30244)
- feature: 70_XBMC: added commands: openmovieid, openepisodeid, addon, jsonraw (thanks to siggi85)
- change: FB_CALLMONITOR: use standard file read/write function to support
use of configDb
- bugfix: FB_CALLMONITOR: fix phonebook file read when using configDb (Forum
#30244)
- feature: 70_XBMC: added commands: openmovieid, openepisodeid, addon,
jsonraw (thanks to siggi85)
- fix: 70_XBMC: made fork attribute to close file handles correctly
- feature: 70_XBMC: added mechanism to detect disconnects (TCP)
- fix: 66_ECMD: avoid reading from a closed connection in ECMD_READ
- feature: 70_PIONEERAVR: readings for currentAlbum etc., more internals (network settings, moved some from readings to internals), new attributes volumeLimit & volumeLimitStraight
- feature: 70_PIONEERAVR: readings for currentAlbum etc., more internals
(network settings, moved some from readings to internals), new attributes
volumeLimit & volumeLimitStraight
- added: some new icons (hourglass, frost, sani_heating_level_XX)
- fix: sani_heating_boost (possibility to colorize)
- feature: FB_CALLMONITOR: add remote phonebook lookup via telnet connection
to FritzBox (JoWiemann).
- bugfix: 70_PIONEERAVR & 71_PIONEERAVRZONE: fixed not working set-extensions (on-for-timer,...)
- bugfix: 70_PIONEERAVR & 71_PIONEERAVRZONE: fixed not working set-extensions
(on-for-timer,...)
- feature: fheminfo: report third-party modules
- feature: 99_Utils.pm: add getUniqueID, getKeyValue, setKeyValue
- feature: SMARTMON: additional parameters for smartctl
@ -33,7 +44,8 @@
- feature: HUEDevice: allow ct presets in webCmd
new subTypes extcolordimer and ctdimer
start support for Lightify bulbs
- added: SONOS and SONOSPLAYER to support Sonos Multiroom Audiosystems (Reinerlein)
- added: SONOS and SONOSPLAYER to support Sonos Multiroom Audiosystems
(Reinerlein)
- change: 64_ESA2000.pm: add batterystate
- added: 42_SMARTMON: Frontend to smartctl (maintainer: hexenmeister)
- feature: 70_PushNotifier added line break in Messages (xusader)
@ -43,7 +55,8 @@
- bugfix: FB_CALLMONITOR: fixing not working company numbers
reverse search for search.ch
- bugfix: 70_PushNotifier repair set function (xusader)
- bugfix: PRESENCE: fixing not working timer, when using set [...] statusRequest
- bugfix: PRESENCE: fixing not working timer, when using set [...]
statusRequest
- bugfix: FB_CALLMONITOR: fixing reverse search for klicktel.de
- feature: new module 52_I2C_MCP342x.pm added (klausw)
- feature: SYSMON: read cpu temp on FritzBox
@ -57,7 +70,8 @@
- feature: new module 98_logProxy.pm added (justme1968)
- change: 66_ECMD: ReadyFn added (fixes issue under Windows)
- change: 02_RSS: use a GUID in RSS; urlq source for img command
- feature: 70_PushNotifier improve usebility, configuration without cURL (xusader)
- feature: 70_PushNotifier improve usebility, configuration without cURL
(xusader)
- bugfix: SYSMON: prevent empty line im log by userReadings
- feature: 10_IT empfang (by bjoernh)
- bugfix: PRESENCE: fix race condition, when delete disabled attribute and

View File

@ -16,6 +16,7 @@ sub FW_answerCall($);
sub FW_dev2image($;$);
sub FW_devState($$@);
sub FW_digestCgi($);
sub FW_directNotify($$);
sub FW_doDetail($);
sub FW_fatal($);
sub FW_fileList($);
@ -187,11 +188,7 @@ FHEMWEB_Initialize($)
closedir(DH);
}
$data{webCmdFn}{slider} = "FW_sliderFn";
$data{webCmdFn}{timepicker} = "FW_timepickerFn";
$data{webCmdFn}{noArg} = "FW_noArgFn";
$data{webCmdFn}{textField} = "FW_textFieldFn";
$data{webCmdFn}{"~dropdown"}= "FW_dropdownFn"; # Should be the last
$data{webCmdFn}{"~"} = "FW_widgetFallbackFn"; # Should be the last
if($init_done) { # reload workaround
foreach my $pe ("fhemSVG", "openautomation", "default") {
@ -531,7 +528,9 @@ FW_answerCall($)
$ldir = "$FW_dir/pgm2" if($dir eq "css" || $dir eq "js"); # FLOORPLAN compat
$ldir = "$attr{global}{modpath}/docs" if($dir eq "docs");
if(-r "$ldir/$file.$ext") { # no return for FLOORPLAN
# pgm2 check is for jquery-ui images
my $static = ($ext =~ m/(css|js|png|jpg)/i || $dir =~ m/^pgm2/);
if(-r "$ldir/$file.$ext" || $static) { # no return for FLOORPLAN
return FW_serveSpecial($file, $ext, $ldir, ($arg =~ m/nocache/) ? 0 : 1);
}
$arg = "/$dir/$ofile";
@ -617,7 +616,6 @@ FW_answerCall($)
}
return 0;
}
##############################
# FHEMWEB extensions (FLOORPLOAN, SVG_WriteGplot, etc)
my $FW_contentFunc;
@ -696,18 +694,26 @@ FW_answerCall($)
FW_pO "<meta http-equiv=\"refresh\" content=\"$rf\">" if($rf);
}
########################
# CSS
my $cssTemplate = "<link href=\"$FW_ME/%s\" rel=\"stylesheet\"/>";
FW_pO sprintf($cssTemplate, "pgm2/style.css");
my @cssFiles = split(" ", AttrVal($FW_wname, "CssFiles", ""));
map { FW_pO sprintf($cssTemplate, $_); } @cssFiles;
FW_pO sprintf($cssTemplate, "pgm2/jquery-ui.min.css");
map { FW_pO sprintf($cssTemplate, $_); }
split(" ", AttrVal($FW_wname, "CssFiles", ""));
########################
# JavaScripts
my $jsTemplate = '<script type="text/javascript" src="%s"></script>';
FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/jquery.min.js");
FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/jquery-ui.min.js");
########################
# FW Extensions
my $jsTemplate = '<script type="text/javascript" src="%s"></script>';
if(defined($data{FWEXT})) {
foreach my $k (sort keys %{$data{FWEXT}}) {
my $h = $data{FWEXT}{$k};
next if($h !~ m/HASH/ || !$h->{SCRIPT});
next if($h !~ m/HASH/ || !$h->{SCRIPT} || $h->{SCRIPT} =~ m+pgm2/jquery+);
my $script = $h->{SCRIPT};
$script = ($script =~ m,^/,) ? "$FW_ME$script" : "$FW_ME/pgm2/$script";
FW_pO sprintf($jsTemplate, $script);
@ -715,21 +721,18 @@ FW_answerCall($)
}
#######################
# Other JavaScripts
FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/svg.js") if($FW_plotmode eq "SVG");
# Other JavaScripts + their Attributes
map { FW_pO sprintf($jsTemplate, "$FW_ME/pgm2/$_") } @FW_fhemwebjs;
$jsTemplate = '<script attr=\'%s\' type="text/javascript" src="%s"></script>';
map {
my $n = $_; $n =~ s+.*/++; $n =~ s/.js$//; $n =~ s/fhem_//; $n .= "Param";
FW_pO sprintf($jsTemplate, AttrVal($FW_wname, $n, ""), "$FW_ME/$_");
} split(" ", AttrVal($FW_wname, "JavaScripts", ""));
my $onload = AttrVal($FW_wname, "longpoll", 1) ?
"onload=\"FW_delayedStart()\"" : "";
my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
FW_pO "</head>\n<body generated=\"".(time()-1)
."\" name=\"$t\" $csrf $onload>";
my $gen = 'generated="'.(time()-1).'"';
my $lp = 'longpoll="'.AttrVal($FW_wname,"longpoll",1).'"';
FW_pO "</head>\n<body name=\"$t\" $gen $lp $csrf>";
if($FW_activateInform) {
$cmd = "style eventMonitor $FW_activateInform";
@ -921,7 +924,7 @@ FW_makeTable($$$@)
} else {
if( $title eq "Attributes" ) {
FW_pO "<td><div class=\"dname\">".
"<a onClick='FW_querySetSelected(\"sel.attr$name\",\"$n\")'>".
"<a onClick='FW_querySetSelected(\"sel_attr$name\",\"$n\")'>".
"$n</a></div></td>";
} else {
FW_pO "<td><div class=\"dname\">$n</div></td>";
@ -978,7 +981,7 @@ FW_makeTable($$$@)
##############################
# Used only for set or attr lists.
sub
FW_makeSelect($$$$)
FW_detailSelect($$$$)
{
my ($d, $cmd, $list,$class) = @_;
return if(!$list || $FW_hiddenroom{input});
@ -987,8 +990,9 @@ FW_makeSelect($$$$)
my $selEl = (defined($al[0]) ? $al[0] : " ");
$selEl = $1 if($list =~ m/([^ ]*):slider,/); # promote a slider if available
$selEl = "room" if($list =~ m/room:/);
$list =~ s/"/&quot;/g;
FW_pO "<div class='makeSelect'>";
FW_pO "<div class='makeSelect' dev=\"$d\" cmd=\"$cmd\" list=\"$list\">";
FW_pO "<form method=\"$FW_formmethod\" ".
"action=\"$FW_ME$FW_subdir\" autocomplete=\"off\">";
FW_pO FW_hidden("detail", $d);
@ -996,12 +1000,8 @@ FW_makeSelect($$$$)
FW_pO FW_hidden("dev.$cmd$d", $d);
FW_pO FW_submit("cmd.$cmd$d", $cmd, $class);
FW_pO "<div class=\"$class downText\">&nbsp;$d&nbsp;</div>";
FW_pO FW_select("sel.$cmd$d","arg.$cmd$d",\@al, $selEl, $class,
"FW_selChange(this.options[selectedIndex].text,'$list','val.$cmd$d')");
FW_pO FW_select("sel_$cmd$d","arg.$cmd$d",\@al, $selEl, $class);
FW_pO FW_textfield("val.$cmd$d", 30, $class);
# Initial setting
FW_pO "<script type=\"text/javascript\">" .
"FW_selChange('$selEl','$list','val.$cmd$d')</script>";
FW_pO "</form></div>";
}
@ -1037,12 +1037,8 @@ FW_doDetail($)
use strict "refs";
}
FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME\">";
FW_pO FW_hidden("detail", $d);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_makeSelect($d, "set", FW_widgetOverride($d, getAllSets($d)), "set");
FW_makeSelect($d, "get", FW_widgetOverride($d, getAllGets($d)), "get");
FW_detailSelect($d, "set", FW_widgetOverride($d, getAllSets($d)), "set");
FW_detailSelect($d, "get", FW_widgetOverride($d, getAllGets($d)), "get");
FW_makeTable("Internals", $d, $h);
FW_makeTable("Readings", $d, $h->{READINGS});
@ -1057,7 +1053,7 @@ FW_doDetail($)
$attrList = FW_widgetOverride($d, $attrList);
$attrList =~ s/\\/\\\\/g;
$attrList =~ s/'/\\'/g;
FW_makeSelect($d, "attr", $attrList,"attr");
FW_detailSelect($d, "attr", $attrList,"attr");
FW_makeTable("Attributes", $d, $attr{$d}, "deleteattr");
## dependent objects
@ -1070,7 +1066,6 @@ FW_doDetail($)
push(@dob, $dn);
}
}
FW_pO "</form>";
FW_makeTableFromArray("Probably associated with", "assoc", @dob,);
FW_pO "</td></tr></table>";
@ -1078,6 +1073,7 @@ FW_doDetail($)
FW_pH "cmd=style iconFor $d", "Select icon";
FW_pH "cmd=style showDSI $d", "Extend devStateIcon";
FW_pH "$FW_ME/docs/commandref.html#${t}", "Device specific help";
FW_pH "cmd=delete $d", "Delete this device ($d)" if($d ne "global");
FW_pO "<br><br>";
FW_pO "</div>";
@ -1300,7 +1296,7 @@ FW_showRoom()
$FW_hiddengroup{$r} = 1;
}
FW_pO "<form method=\"$FW_formmethod\" ".
FW_pO "<form method=\"$FW_formmethod\" ". # Why do we need a form here?
"action=\"$FW_ME\" autocomplete=\"off\">";
FW_pO "<div id=\"content\" room=\"$FW_room\">";
FW_pO "<table class=\"roomoverview\">"; # Need for equal width of subtables
@ -1358,7 +1354,7 @@ FW_showRoom()
$icon = FW_makeImage($icon,$icon,"icon") . "&nbsp;" if($icon);
if($FW_hiddenroom{detail}) {
FW_pO "<td><div class=\"col1\">$icon$devName</div></td>";
FW_pO "<td><div class=\"col1\">$icon$devName</div></td>" if(!$usuallyAtEnd{$d});
} else {
FW_pH "detail=$d", "$icon$devName", 1, "col1" if(!$usuallyAtEnd{$d});
}
@ -1506,8 +1502,10 @@ FW_returnFileAsStream($$$$$)
}
if(!open(FH, $path)) {
Log3 $FW_wname, 2, "FHEMWEB $FW_wname $path: $!";
FW_pO "<div id=\"content\">$path: $!</div>";
Log3 $FW_wname, 4, "FHEMWEB $FW_wname $path: $!";
TcpServer_WriteBlocking($FW_chash,
"HTTP/1.1 404 Not Found\r\n".
"Content-Length:0\r\n\r\n");
FW_closeConn($FW_chash);
return 0;
}
@ -1569,12 +1567,12 @@ FW_hidden($$)
sub
FW_select($$$$$@)
{
my ($id, $n, $va, $def, $class, $jSelFn) = @_;
my ($id, $name, $valueArray, $selected, $class, $jSelFn) = @_;
$jSelFn = ($jSelFn ? "onchange=\"$jSelFn\"" : "");
$id = ($id ? "id=\"$id\" informId=\"$id\"" : "");
my $s = "<select $jSelFn $id name=\"$n\" class=\"$class\">";
foreach my $v (@{$va}) {
if(defined($def) && $v eq $def) {
my $s = "<select $jSelFn $id name=\"$name\" class=\"$class\">";
foreach my $v (@{$valueArray}) {
if(defined($selected) && $v eq $selected) {
$s .= "<option selected=\"selected\" value='$v'>$v</option>\n";
} else {
$s .= "<option value='$v'>$v</option>\n";
@ -1750,14 +1748,16 @@ FW_style($$)
my $filePath = FW_fileNameToPath($fileName);
$FW_data =~ s/\r//g;
my $err = FileWrite({FileName=>$filePath, ForceType=>$forceType}, split("\n", $FW_data));
my $err = FileWrite({FileName=>$filePath, ForceType=>$forceType},
split("\n", $FW_data));
if($err) {
FW_pO "<div id=\"content\">$filePath: $!</div>";
return;
}
my $ret = FW_fC("rereadcfg") if($filePath eq $attr{global}{configfile});
$ret = FW_fC("reload $fileName") if($fileName =~ m,\.pm$,);
$ret = ($ret ? "<h3>ERROR:</h3><b>$ret</b>" : "Saved the file $fileName to $forceType");
$ret = ($ret ? "<h3>ERROR:</h3><b>$ret</b>" :
"Saved the file $fileName to $forceType");
FW_style("style list", $ret);
$ret = "";
@ -2201,6 +2201,16 @@ FW_makeEdit($$$)
}
sub
FW_longpollInfo($$$)
{
my ($dev, $state, $html) = @_;
$dev =~ s/([\\"])/\\$1/g;
$state =~ s/([\\"])/\\$1/g;
$html =~ s/([\\"])/\\$1/g;
return "[\"$dev\",\"$state\",\"$html\"]";
}
sub
FW_roomStatesForInform($$)
{
@ -2219,7 +2229,7 @@ FW_roomStatesForInform($$)
my ($allSet, $cmdlist, $txt) = FW_devState($dn, "", \%extPage);
if($defs{$dn} && $defs{$dn}{STATE} && $defs{$dn}{TYPE} ne "weblink") {
push @data, "$dn<<$defs{$dn}{STATE}<<$txt";
push @data, FW_longpollInfo($dn, $defs{$dn}{STATE}, $txt);
}
}
my $data = join("\n", map { s/\n/ /gm; $_ } @data)."\n";
@ -2265,7 +2275,7 @@ FW_Notify($$)
if( !$modules{$defs{$dn}{TYPE}}{FW_atPageEnd} ) {
my ($allSet, $cmdlist, $txt) = FW_devState($dn, "", \%extPage);
($FW_wname, $FW_ME, $FW_ss, $FW_tp, $FW_subdir) = @old;
push @data, "$dn<<$dev->{STATE}<<$txt";
push @data, FW_longpollInfo($dn, $dev->{STATE}, $txt);
}
#Add READINGS
@ -2277,8 +2287,8 @@ FW_Notify($$)
next; #ignore 'set' commands
}
my ($readingName,$readingVal) = split(": ",$events->[$i],2);
push @data, "$dn-$readingName<<$readingVal<<$readingVal";
push @data, "$dn-$readingName-ts<<$tn<<$tn";
push @data, FW_longpollInfo("$dn-$readingName",$readingVal,$readingVal);
push @data, FW_longpollInfo("$dn-$readingName-ts", $tn, $tn);
}
}
}
@ -2310,6 +2320,24 @@ FW_Notify($$)
return undef;
}
sub
FW_directNotify($$) # Notify without the event overhead (Forum #31293)
{
my ($dev, $msg) = @_;
foreach my $ntfy (values(%defs)) {
next if(!$ntfy->{TYPE} ||
$ntfy->{TYPE} ne "FHEMWEB" ||
!$ntfy->{inform} ||
!$ntfy->{inform}{devices}{$dev});
if(!addToWritebuffer($ntfy, FW_longpollInfo($dev, $msg, "")."\n")){
my $name = $ntfy->{NAME};
Log3 $name, 4, "Closing connection $name due to full buffer in FW_Notify";
TcpServer_Close($ntfy);
delete($defs{$name});
}
}
}
###################
# Compute the state (==second) column
sub
@ -2371,16 +2399,7 @@ FW_devState($$@)
}
$link .= "&room=$room";
}
if(AttrVal($FW_wname, "longpoll", 1)) {
$txt = "<a onClick=\"FW_cmd('$FW_ME$FW_subdir?XHR=1&$link')\">$txt</a>";
} elsif($FW_ss || $FW_tp) {
$txt ="<a onClick=\"location.href='$FW_ME$FW_subdir?$link$rf$FW_CSRF'\">$txt</a>";
} else {
$txt = "<a href=\"$FW_ME$FW_subdir?$link$rf$FW_CSRF\">$txt</a>";
}
$txt = "<a href=\"$FW_ME$FW_subdir?$link$rf$FW_CSRF\">$txt</a>";
}
my $style = AttrVal($d, "devStateStyle", "");
@ -2488,124 +2507,31 @@ FW_htmlEscape($)
###########################
# Widgets START
sub
FW_sliderFn($$$$$)
FW_widgetFallbackFn()
{
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
return undef if($values !~ m/^slider,([-\d.]*),([-\d.]*),([-\d.]*)(,1)?$/);
return "" if($cmd =~ m/ /); # webCmd pct 30 should generate a link
my ($min,$stp, $max, $flt) = ($1, $2, $3, $4);
$flt = ($flt ? 1 : 0);
my $srf = $FW_room ? "&room=$FW_room" : "";
my $cv = ReadingsVal($d, $cmd, Value($d));
my $id = ($cmd eq "state") ? "" : "-$cmd";
$cmd = "" if($cmd eq "state");
$cv =~ s/.*?([.\-\d]+).*/$1/; # get first number
$cv = 0 if($cv !~ m/\d/);
return "<td colspan='2'>".
"<div class='slider' id='slider.$d$id' min='$min' stp='$stp' ".
"max='$max' cmd='$FW_ME?cmd=set $d $cmd %$srf' flt='$flt'>".
"<div class='handle'>$min</div>".
"</div>".
"<script type=\"text/javascript\">".
"FW_sliderCreate(document.getElementById('slider.$d$id'),'$cv');".
"</script>".
"</td>";
}
# webCmd "temp 30" should remain text
# noArg is needed for fhem.cfg.demo / Cinema
return "" if(!$values || $values eq "noArg");
sub
FW_noArgFn($$$$$)
{
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
return undef if($values !~ m/^noArg$/);
return "";
}
sub
FW_timepickerFn()
{
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
return undef if($values ne "time");
return "" if($cmd =~ m/ /); # webCmd on-for-timer 30 should generate a link
my $srf = $FW_room ? "&room=$FW_room" : "";
my $cv = ReadingsVal($d, $cmd, Value($d));
$cmd = "" if($cmd eq "state");
my $c = "\"$FW_ME?cmd=set $d $cmd %$srf\"";
return "<td colspan='2'>".
"<input name='time.$d' value='$cv' type='text' readonly size='5'>".
"<input type='button' value='+' onclick='FW_timeCreate(this,$c)'>".
"</td>";
}
sub
FW_dropdownFn()
{
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
return "" if($cmd =~ m/ /); # webCmd temp 30 should generate a link
my @tv = split(",", $values);
# Hack: eventmap (translation only) should not result in a
# dropdown. eventMap/webCmd/etc handling must be cleaned up.
if(@tv > 1) {
my $txt;
if($cmd eq "desired-temp" || $cmd eq "desiredTemperature") {
$txt = ReadingsVal($d, $cmd, 20);
$txt =~ s/ .*//; # Cut off Celsius
$txt = sprintf("%2.1f", int(2*$txt)/2) if($txt =~ m/[0-9.-]/);
} else {
$txt = ReadingsVal($d, $cmd, Value($d));
$txt =~ s/$cmd //;
my($reading) = split( ' ', $cmd, 2 );
my $current;
if($cmd eq "desired-temp" || $cmd eq "desiredTemperature") {
$current = ReadingsVal($d, $cmd, 20);
$current =~ s/ .*//; # Cut off Celsius
$current = sprintf("%2.1f", int(2*$current)/2) if($current =~ m/[0-9.-]/);
} else {
$current = ReadingsVal($d, $reading, undef);
if( !defined($current) ) {
$reading = 'state';
$current = Value($d);
}
my $fpname = $FW_wname;
$fpname =~ s/.*floorplan\/(\w+)$/$1/; #allow usage of attr fp_setbutton
my $readng = ($cmd eq "state" ? "" : "$cmd"." ");
# TODO in case of running in a floorplan split $FW_wname to get name of
# webInstance. Actually in floorplan the dropdown will refresh the page
# always independently from setting in corresponding web instance, cause
# statement if( AttrVal($FW_wname, "longpoll", 0) == 1) will always fail.
my $selFunct="";
if( AttrVal($FW_wname, "longpoll", 0) == 1) {
$selFunct = "FW_cmd('$FW_ME?XHR=1&cmd.$d=set $d $readng '+ ".
"this.options[this.selectedIndex].value+ ' &room=$FW_room')";
} else {
$selFunct = "window.location = addcsrf('$FW_ME?cmd.$d=set $d $readng '+".
"this.options[this.selectedIndex].value+ ' &room=$FW_room')";
}
my $fwsel;
$fwsel = ($cmd eq "state" ? "" : "$cmd&nbsp;") .
FW_select("$d-$cmd","val.$d", \@tv, $txt,"dropdown","$selFunct");
return "<td colspan='2'>$fwsel</td>";
$current =~ s/$cmd //;
}
return undef;
return "<td><div class='fhemWidget' cmd='$cmd' reading='$reading' ".
"dev='$d' arg='$values' current='$current'></div></td>";
}
sub
FW_textFieldFn($$$$)
{
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
my @args = split("[ \t]+", $cmd);
return undef if($values !~ m/^textField$/);
return "" if($cmd =~ m/ /);
my $srf = $FW_room ? "&room=$FW_room" : "";
my $cv = ReadingsVal($d, $cmd, "");
my $id = ($cmd eq "state") ? "" : "-$cmd";
my $c = "$FW_ME?XHR=1&cmd=setreading $d $cmd %$srf";
return '<td align="center">'.
"<div>$cmd:<input id='textField.$d$id' type='text' value='$cv' ".
"onChange='textField_setText(this,\"$c\")'></div>".
'</td>';
}
# Widgets END
###########################
@ -3148,18 +3074,23 @@ FW_widgetOverride($$)
<li>if the modifier is ":time", then a javascript driven timepicker is
displayed.</li>
<li>if the modifier is ":textField", an input field is displayed.</li>
<li>if the modifier is ":textField-long", is like textField, but upon
clicking on the input field a textArea (60x25) will be opened.</li>
<li>if the modifier is of the form
":slider,&lt;min&gt;,&lt;step&gt;,&lt;max&gt;[,1]", then a
javascript driven slider is displayed. The optional ,1 at the end
avoids the rounding of floating-point numbers.</li>
<li>if the modifier is of the form ":multiple,val1,val2,...", then
multiple values can be selected and own values can be written, the
result is comma separated.</li>
<li>if the modifier is of the form ":multiple-strict,val1,val2,...", then
multiple values can be selected and no new values can be added, the
result is comma separated.</li>
<li>if the modifier is of the form ":multiple-strict,val1,val2,...",
then multiple values can be selected and no new values can be
added, the result is comma separated.</li>
<li>if the modifier is of the form ":knob,min:1,max:100,...", then
the jQuery knob widget will be displayed. The parameters are
specified as a comma separated list of key:value pairs, where key
does not have to contain the "data-" prefix.</li>
<li>else a dropdown with all the modifier values is displayed</li>
</ul>
If this attribute is specified for a FHEMWEB instance, then it is
@ -3167,6 +3098,7 @@ FW_widgetOverride($$)
<ul>
attr FS20dev widgetOverride on-till:time<br>
attr WEB widgetOverride room:textField<br>
attr dimmer widgetOverride dim:knob,min:1,max:100,step:1,linecap:round<br>
</ul>
</li>
<br>
@ -3714,13 +3646,17 @@ FW_widgetOverride($$)
vorgesehene Widgets aendern kann.
<ul>
<li>Ist der Modifier ":noArg", wird kein weiteres Eingabefeld
angezeigt.</li>
angezeigt.</li>
<li>Ist der Modifier ":time", wird ein in Javaskript geschreibenes
Zeitauswahlmen&uuml; angezeigt.</li>
Zeitauswahlmen&uuml; angezeigt.</li>
<li>Ist der Modifier ":textField", wird ein Eingabefeld
angezeigt.</li>
angezeigt.</li>
<li>Ist der Modified ":textField-long" ist wie textField, aber beim
Click im Eingabefeld ein Dialog mit einer HTML textarea
(60x25) wird ge&ouml;ffnet.</li>
<li>Ist der Modifier in der Form
":slider,&lt;min&gt;,&lt;step&gt;,&lt;max&gt;[,1]", so wird ein in
@ -3735,8 +3671,15 @@ FW_widgetOverride($$)
Mehrfachauswahl m&ouml;glich, es k&ouml;nnen jedoch keine neuen
Werte definiert werden. Das Ergebnis ist Komma-separiert.</li>
<li>In allen anderen F&auml;llen erscheint ein Dropdown mit allen
Modifier Werten.</li>
<li>Ist der Modifier ":knob,min:1,max:100,...", dass ein
jQuery knob Widget wird angezeigt. Die Parameter werden als eine
Komma separierte Liste von Key:Value Paaren spezifiziert, wobei das
data- Pr&auml;fix entf&auml;llt. </li>
<li>In allen anderen F&auml;llen (oder falls der Modified explizit
mit :select anfaegt) erscheint ein HTML select mit allen Modifier
Werten.</li>
</ul>
Falls das Attribut f&uuml;r eine WEB Instanz gesetzt wurde, dann wird
es bei allen von diesem Web-Instan angezeigten Ger&auml;ten angewendet.
@ -3744,6 +3687,7 @@ FW_widgetOverride($$)
<ul>
attr FS20dev widgetOverride on-till:time<br>
attr WEB widgetOverride room:textField<br>
attr dimmer widgetOverride dim:knob,min:1,max:100,step:1,linecap:round<br>
</ul>
</li><br>

View File

@ -273,23 +273,18 @@ return "<br>Timespec wizard:".
</tr><tr class="even"><td>Timespec</td>
<td><input type="text" id="aw_pts"></td>
</tr><tr class="even"><td>Timespec</td>
<td><input type="text" readonly id="aw_ts" size="5">
<input type='button' value='+' id="aw_tsb"></td>
<td><input type="text" name="aw_ts" id="aw_ts" size="5"></td>
</tr>
</tr><tr class="even">
<td colspan="2"><input type="button" id="aw_md" value="Modify"></td>
</tr>
</table>
<script type="text/javascript">
loadScript("pgm2/jquery.min.js", atDetails);
function
atDetails()
{
var t=$("#atWizard"), ip=$(t).attr("ip"), ts=$(t).attr("ts");
FW_replaceWidget("#aw_ts", "aw_ts", ["time"], "12:00");
$("[informid=aw_ts] input[type=text]").attr("id", "aw_ts");
function tsClick() {
FW_timeCreate(this, function(val) { $("#aw_tsb").click(tsClick) })
}
function ipClick() {
var c = $("#aw_ip").prop("checked");
$("#aw_ts").closest("tr").css("display", !c ? "table-row" : "none");
@ -300,7 +295,6 @@ return "<br>Timespec wizard:".
$("#aw_ip").prop("checked", ip);
$("#aw_ts").val(ip ? "12:00" : ts);
$("#aw_pts").val(ip ? ts : 'sunset()');
$("#aw_tsb").click(tsClick);
$("#aw_ip").change(ipClick);
ipClick();
$("#aw_md").click(function(){

View File

@ -951,8 +951,8 @@ FileLog_sampleDataFn($$$$$)
for(my $r=0; $r < $max; $r++) {
my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":::"), 4);
my $ret = "";
$ret .= SVG_sel("par_${r}_0", $colnums, $f[0]);
$ret .= SVG_sel("par_${r}_1", $colregs, $f[1]);
$ret .= SVG_sel("par_${r}_0", $colnums, $f[0], undef, "svgColumn");
$ret .= SVG_sel("par_${r}_1", $colregs, $f[1], undef, "svgRegexp");
$ret .= SVG_txt("par_${r}_2", "", $f[2], 1);
$ret .= SVG_txt("par_${r}_3", "", $f[3], 6);
push @htmlArr, $ret;

View File

@ -32,7 +32,7 @@ sub SVG_calcOffsets($$);
sub SVG_doround($$$);
sub SVG_fmtTime($$);
sub SVG_pO($);
sub SVG_readgplotfile($$);
sub SVG_readgplotfile($$$);
sub SVG_render($$$$$$$$$;$$);
sub SVG_showLog($);
sub SVG_substcfg($$$$$$);
@ -40,8 +40,12 @@ sub SVG_time_align($$);
sub SVG_time_to_sec($);
sub SVG_openFile($$$);
sub SVG_doShowLog($$$$;$$);
sub SVG_getData($$$$$);
sub SVG_sel($$$;$$);
my %SVG_devs; # hash of from/to entries per device
my $SVG_id=0;
#####################################
sub
@ -50,7 +54,8 @@ SVG_Initialize($)
my ($hash) = @_;
$hash->{DefFn} = "SVG_Define";
$hash->{AttrList} = "fixedoffset fixedrange startDate plotsize nrAxis label title plotfunction";
$hash->{AttrList} = "fixedoffset fixedrange startDate plotsize nrAxis ".
"label title plotfunction";
$hash->{SetFn} = "SVG_Set";
$hash->{FW_summaryFn} = "SVG_FwFn";
$hash->{FW_detailFn} = "SVG_FwFn";
@ -133,6 +138,21 @@ jsSVG_getAttrs($)
} keys %{$attr{$d}});
}
sub
SVG_getplotsize($)
{
my ($d) = @_;
return $FW_webArgs{plotsize} ?
$FW_webArgs{plotsize} : AttrVal($d,"plotsize",$FW_plotsize);
}
sub
SVG_isEmbed($)
{
return (AttrVal($FW_wname, "plotEmbed",
$FW_userAgent !~ m/(iPhone|iPad|iPod).*OS (8|9)/));
}
##################
sub
SVG_FwFn($$$$)
@ -141,6 +161,11 @@ SVG_FwFn($$$$)
my $hash = $defs{$d};
my $ret = "";
if(!$pageHash || !$pageHash->{jsLoaded}) {
$ret .= "<script type='text/javascript' src='$FW_ME/pgm2/svg.js'></script>";
$pageHash->{jsLoaded} = 1 if($pageHash);
}
if(AttrVal($FW_wname, "plotmode", "SVG") eq "jsSVG") {
my @d=split(":",$defs{$d}{DEF});
@ -182,11 +207,10 @@ SVG_FwFn($$$$)
"&amp;pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
my ($w, $h) = split(",", SVG_getplotsize($d));
$ret .= "<div class=\"SVGplot SVG_$d\">";
if(AttrVal($FW_wname, "plotEmbed",
$FW_userAgent !~ m/(iPhone|iPad|iPod).*OS (8|9)/)) {
if(SVG_isEmbed($FW_wname)) {
$ret .= "<embed src=\"$arg\" type=\"image/svg+xml\" " .
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
@ -236,12 +260,12 @@ SVG_txt($$$$)
}
sub
SVG_sel($$$@)
SVG_sel($$$;$$)
{
my ($v,$l,$c,$fnData) = @_;
my ($v,$l,$c,$fnData,$class) = @_;
my @al = split(",",$l);
$c =~ s/\\x3a/:/g if($c);
return FW_select($v,$v,\@al,$c, "set", $fnData);
return FW_select(undef,$v,\@al,$c, $class?$class:"set", $fnData);
}
############################
@ -255,12 +279,10 @@ SVG_PEdit($$$$)
return "" if( $pe eq 'never' );
my $ld = $defs{$d}{LOGDEVICE};
my $ldt = $defs{$ld}{TYPE};
my $gp = "$FW_gplotdir/$defs{$d}{GPLOTFILE}.gplot";
my $pm = AttrVal($d,"plotmode",$FW_plotmode);
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($d, $gp);
my ($err, $cfg, $plot, $srcDesc) = SVG_readgplotfile($d, $gp, $pm);
my %conf = SVG_digestConf($cfg, $plot);
my $ret = "<br>";
@ -280,8 +302,6 @@ SVG_PEdit($$$$)
"action=\"$FW_ME/SVG_WriteGplot\">";
$ret .= "Plot Editor";
$ret .= FW_hidden("detail", $d); # go to detail after save
$ret .= FW_hidden("gplotName", $gp);
$ret .= FW_hidden("logdevicetype", $ldt);
if(defined($FW_pos{zoom}) && defined($FW_pos{off})) { # for showData
$ret .= FW_hidden("pos", "zoom=$FW_pos{zoom};off=$FW_pos{off}");
}
@ -314,22 +334,39 @@ SVG_PEdit($$$$)
$ret .= "</tr>";
my $max = @{$conf{lType}}+1;
my ($desc, $htmlArr, $example) = ("Spec", undef, "");
if($modules{$ldt}{SVG_sampleDataFn}) {
no strict "refs";
($desc, $htmlArr, $example) =
&{$modules{$ldt}{SVG_sampleDataFn}}($ld, $flog, $max,\%conf, $FW_wname);
use strict "refs";
} else {
my @htmlArr;
@htmlArr = map { SVG_txt("par_${_}_0","",$flog->[$_] ? $flog->[$_]:"",20) }
(0..$max-1);
$htmlArr = \@htmlArr;
}
my ($desc, $cnt) = ("Spec", 0);
my (@srcHtml, @paramHtml, @exampleHtml, @revIdx);
my @srcNames = grep { $modules{$defs{$_}{TYPE}}{SVG_sampleDataFn} }
sort keys %defs;
$ret .= "<tr class=\"odd\"><td>Diagramm label</td>";
$ret .= "<td>$desc</td>";
$ret .=" <td>Y-Axis,Plot-Type,Style,Width</td></tr>";
foreach my $src (@{$srcDesc->{order}}) {
my $lmax = $srcDesc->{src}{$src}{idx}+1;
my $fn = $modules{$defs{$src}{TYPE}}{SVG_sampleDataFn};
my @argArr = split(" ", $srcDesc->{src}{$src}{arg});
if($fn) {
no strict "refs";
my ($ldesc, $paramHtml, $example) =
&{$fn}($src, \@argArr, $lmax,\%conf, $FW_wname);
use strict "refs";
$desc = $ldesc;
push @paramHtml, @{$paramHtml};
map { push @exampleHtml, $example } (0..$lmax-1);
} else {
push @paramHtml, map { SVG_txt("par_${_}_0","",$_,20) } @argArr;
map { push @exampleHtml, "" } (0..$lmax-1);
}
push @srcHtml,
map { FW_select(undef,"src_$_",\@srcNames,$src,"svgSrc");} (0..$lmax-1);
map { push @revIdx,$srcDesc->{rev}{$cnt}{$_}; } (0..$lmax-1);
$cnt++;
}
# Last, empty line
push @revIdx,int(@revIdx);
push @srcHtml, $srcHtml[0];
push @paramHtml, $paramHtml[0];
push @exampleHtml, $exampleHtml[0];
my @lineStyles;
if(SVG_openFile($FW_cssdir,
@ -338,33 +375,46 @@ SVG_PEdit($$$$)
close(FH);
}
my $r = 0;
$ret .= "<tr class=\"odd\"><td>Diagramm label, Source</td>";
$ret .= "<td>$desc</td>";
$ret .=" <td>Y-Axis,Plot-Type,Style,Width</td></tr>";
my ($r, $example, @output) = (0, "");
for($r=0; $r < $max; $r++) {
$ret .= "<tr class=\"".(($r&1)?"odd":"even")."\"><td>";
$ret .= SVG_txt("title_${r}", "", !$conf{lTitle}[$r]&&$r<($max-1) ?
"notitle" : $conf{lTitle}[$r], 12);
$ret .= "</td><td>";
$ret .= $htmlArr->[$r] if($htmlArr && @{$htmlArr} > $r);
$ret .= "</td><td>";
my $v = $conf{lAxis}[$r];
$ret .= SVG_sel("axes_${r}", "left,right",
my $idx = $revIdx[$r];
$example .= "<div class='ex ex_$idx' style='display:".($idx?"none":"block").
"'>$exampleHtml[$r]</div>";
my $o = "<tr row='$idx' class=\"".(($r&1)?"odd":"even")."\"><td>";
$o .= SVG_txt("title_$idx", "", !$conf{lTitle}[$idx]&&$idx<($max-1) ?
"notitle" : $conf{lTitle}[$idx], 12);
my $sh = $srcHtml[$r]; $sh =~ s/src_\d+/src_$idx/g;
$o .= $sh;
$o .= "</td><td>";
my $ph = $paramHtml[$r]; $ph =~ s/par_\d+_/par_${idx}_/g;
$o .= $ph;
$o .= "</td><td>";
my $v = $conf{lAxis}[$idx];
$o .= SVG_sel("axes_${idx}", "left,right",
($v && $v eq "x1y1") ? "left" : "right");
$ret .= SVG_sel("type_${r}", "lines,points,steps,fsteps,histeps,bars",
$conf{lType}[$r]);
my $ls = $conf{lStyle}[$r];
$o .= SVG_sel("type_${idx}", "lines,points,steps,fsteps,histeps,bars",
$conf{lType}[$idx]);
my $ls = $conf{lStyle}[$idx];
if($ls) {
$ls =~ s/class=//g;
$ls =~ s/"//g;
}
$ret .= SVG_sel("style_${r}", join(",", @lineStyles), $ls);
my $lw = $conf{lWidth}[$r];
$o .= SVG_sel("style_$idx", join(",", @lineStyles), $ls);
my $lw = $conf{lWidth}[$idx];
if($lw) {
$lw =~ s/.*stroke-width://g;
$lw =~ s/"//g;
}
$ret .= SVG_sel("width_${r}", "0.2,0.5,1,1.5,2,3,4", ($lw ? $lw : 1));
$ret .= "</td></tr>";
$o .= SVG_sel("width_$idx", "0.2,0.5,1,1.5,2,3,4", ($lw ? $lw : 1));
$o .= "</td></tr>";
$output[$idx] = $o;
}
$ret .= join("", @output);
$ret .= "<tr class=\"".(($r++&1)?"odd":"even")."\"><td colspan=\"3\">";
$ret .= "Example lines for input:<br>$example</td></tr>";
@ -374,6 +424,32 @@ SVG_PEdit($$$$)
"</td></tr>";
$ret .= "</table></form>";
my $sl = "$FW_ME/SVG_WriteGplot?detail=$d&showFileLogData=1";
if(defined($FW_pos{zoom}) && defined($FW_pos{off})) {
$sl .= "&pos=zoom=$FW_pos{zoom};off=$FW_pos{off}";
}
$ret .= <<'EOF';
<script type="text/javascript">
var sel = "table.plotEditor tr[row] ";
$(sel+"input,"+sel+"select").focus(function(){
var row = $(this).closest("tr").attr("row");
$("table.plotEditor div.ex").css("display","none");
$("table.plotEditor div.ex_"+row).css("display","block");
});
$("table.plotEditor input[name=title_0]").focus();
$("table.plotEditor input[name=showFileLogData]").click(function(e){
e.preventDefault();
EOF
$ret .=
"FW_cmd('$sl', function(arg){" .<<'EOF';
FW_okDialog(arg);
});
});
</script>
EOF
return $ret;
}
##################
@ -439,6 +515,7 @@ SVG_zoomLink($$$)
}
# Debugging: show the data received from GET
sub
SVG_showData()
{
@ -446,17 +523,15 @@ SVG_showData()
my $hash = $defs{$wl};
my ($d, $gplotfile, $file) = split(":", $hash->{DEF});
$gplotfile = "$FW_gplotdir/$gplotfile.gplot";
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($wl, $gplotfile);
my $pm = AttrVal($d,"plotmode",$FW_plotmode);
my ($err, $cfg, $plot, $srcDesc) = SVG_readgplotfile($wl, $gplotfile, $pm);
if($err) {
$FW_RET=$err;
return 1;
}
SVG_calcOffsets($d, $wl);
my ($f,$t)=($SVG_devs{$d}{from}, $SVG_devs{$d}{to});
my $cmd = "get $d $file - $f $t " . join(" ", @{$flog});
my $ret = FW_fC($cmd, 1);
$ret =~ s/\n/<br>/gs;
$FW_RET = "$cmd<br><br>$ret";
$FW_RET = SVG_getData($d, $SVG_devs{$d}{from}, $SVG_devs{$d}{to}, $srcDesc,1);
$FW_RET =~ s/\n/<br>/gs;
return 1;
}
@ -480,7 +555,8 @@ SVG_WriteGplot($)
}
return 0 if(!$maxLines);
my $fName = $FW_webArgs{gplotName};
my $wlName = $FW_webArgs{detail};
my $fName = "$FW_gplotdir/$defs{$wlName}{GPLOTFILE}.gplot";
return if(!$fName);
my @rows;
@ -501,7 +577,6 @@ SVG_WriteGplot($)
push @rows, "set y2range $FW_webArgs{y2range}" if($FW_webArgs{y2range});
push @rows, "";
my $ld = $FW_webArgs{logdevicetype};
my @plot;
for(my $i=0; $i <= $maxLines; $i++) {
next if(!$FW_webArgs{"title_$i"});
@ -512,7 +587,8 @@ SVG_WriteGplot($)
join(":", map { $v[$_] =~ s/:/\\x3a/g if($_<$#v); $v[$_] } 0..$#v) :
$v[0];
push @rows, "#$ld $r";
my $src = $FW_webArgs{"src_$i"};
push @rows, "#$src $r";
push @plot, "\"<IN>\" using 1:2 axes ".
($FW_webArgs{"axes_$i"} eq "right" ? "x1y2" : "x1y1").
($FW_webArgs{"title_$i"} eq "notitle" ? " notitle" :
@ -536,31 +612,51 @@ SVG_WriteGplot($)
return 0;
}
#######################################################
# srcDesc:
# - {all} : space separated plot arguments, in the file order, without devname
# - {order}: unique name of the devs (FileLog,etc) in the .gplot order
# - {src}{X}: hash (X is an order element), consisting of
# {arg}: plot arguments for one dev, space separated
# {idx}: number of lines requested from the same source
# {num}: number or this src in the order array
# - {rev}{orderIdx}{localIdx} = N: reverse lookup of the plot argument index,
# using {src}{X}{num} as orderIdx and {src}{X}{idx} as localIdx
sub
SVG_readgplotfile($$)
SVG_readgplotfile($$$)
{
my ($wl, $gplot_pgm) = @_;
my ($wl, $gplot_pgm, $plotmode) = @_;
############################
# Read in the template gnuplot file. Digest the #FileLog lines. Replace
# the plot directive with our own, as we offer a file for each line
my (@filelog, @data, $plot);
my (%srcDesc, @data, $plot);
my $ld = $defs{$wl}{LOGDEVICE}
if($defs{$wl} && $defs{$wl}{LOGDEVICE});
my $ldType = $defs{$defs{$wl}{LOGDEVICE}}{TYPE}
if($defs{$wl} && $defs{$wl}{LOGDEVICE} && $defs{$defs{$wl}{LOGDEVICE}});
$ldType = $defs{$wl}{TYPE}
if(!$ldType && $defs{$wl});
if($ld && $defs{$ld});
if(!$ldType && $defs{$wl}) {
$ldType = $defs{$wl}{TYPE};
$ld = $wl;
}
my ($err, @svgplotfile) = FileRead($gplot_pgm);
return ("$err", undef) if($err);
my ($plotfnCnt, $srcNum) = (0,0);
my @empty;
$srcDesc{all} = "";
$srcDesc{order} = \@empty;
foreach my $l (@svgplotfile) {
$l = "$l\n" unless $l =~ m/\n$/;
my $plotfn = undef;
if($l =~ m/^#$ldType (.*)$/) {
$plotfn = $1;
Log 3, "$wl: space is not allowed in $ldType definition: $plotfn"
if($plotfn =~ m/\s/);
my ($src, $plotfn) = (undef, undef);
if($l =~ m/^#([^ ]*) (.*)$/) {
if($1 eq $ldType) {
$src = $ld; $plotfn = $2;
} elsif($defs{$1}) {
$src = $1; $plotfn = $2;
}
} elsif($l =~ "^plot" || $plot) {
$plot .= $l;
} else {
@ -568,6 +664,8 @@ SVG_readgplotfile($$)
}
if($plotfn) {
Log 3, "$wl: space is not allowed in $ldType definition: $plotfn"
if($plotfn =~ m/\s/);
my $specval = AttrVal($wl, "plotfunction", undef);
if ($specval) {
my @spec = split(" ",$specval);
@ -577,11 +675,22 @@ SVG_readgplotfile($$)
$spec_count++;
}
}
push(@filelog, $plotfn);
my $p = $srcDesc{src}{$src};
if(!$p) {
$p = { arg => $plotfn, idx=>0, num=>$srcNum++ };
$srcDesc{src}{$src} = $p;
push(@{$srcDesc{order}}, $src);
} else {
$p->{arg} .= " $plotfn";
$p->{idx}++;
}
$srcDesc{rev}{$p->{num}}{$p->{idx}} = $plotfnCnt++;
$srcDesc{all} .= " $plotfn";
}
}
return (undef, \@data, $plot, \@filelog);
return (undef, \@data, $plot, \%srcDesc);
}
sub
@ -592,9 +701,6 @@ SVG_substcfg($$$$$$)
# interpret title and label as a perl command and make
# to all internal values e.g. $value.
my $oll = $attr{global}{verbose};
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
my $ldt = $defs{$defs{$wl}{LOGDEVICE}}{TYPE}
if($defs{$wl} && $defs{$wl}{LOGDEVICE});
$ldt = "" if(!defined($ldt));
@ -605,17 +711,17 @@ SVG_substcfg($$$$$$)
my $fileesc = $file;
$fileesc =~ s/\\/\\\\/g; # For Windows, by MarkusRR
my $title = AttrVal($wl, "title", "\"$fileesc\"");
my $allowed = AttrVal($FW_wname,"allowedCommands",undef);
$title = AnalyzeCommand(undef, "{ $title }");
$title = AnalyzeCommand(undef, "{ $title }", $allowed);
my $label = AttrVal($wl, "label", undef);
my @g_label;
if ($label) {
@g_label = split("::",$label);
foreach (@g_label) {
$_ = AnalyzeCommand(undef, "{ $_ }");
$_ = AnalyzeCommand(undef, "{ $_ }", $allowed);
}
}
$attr{global}{verbose} = $oll;
my $gplot_script = join("", @{$cfg});
$gplot_script .= $plot if(!$splitret);
@ -623,7 +729,7 @@ SVG_substcfg($$$$$$)
$gplot_script =~ s/<OUT>/$tmpfile/g;
$gplot_script =~ s/<IN>/$file/g;
my $ps = AttrVal($wl,"plotsize",$FW_plotsize);
my $ps = SVG_getplotsize($wl);
$gplot_script =~ s/<SIZE>/$ps/g;
$gplot_script =~ s/<TL>/$title/g;
@ -816,10 +922,9 @@ SVG_doShowLog($$$$;$$)
{
my ($wl, $d, $type, $file, $styleW, $styleH) = @_;
my $pm = AttrVal($wl,"plotmode",$FW_plotmode);
my $gplot_pgm = "$FW_gplotdir/$type.gplot";
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($wl, $gplot_pgm);
my ($err, $cfg, $plot, $srcDesc) = SVG_readgplotfile($wl, $gplot_pgm, $pm);
if($err || !$defs{$d}) {
my $msg = ($defs{$d} ? "Cannot read $gplot_pgm" : "No Logdevice $d");
Log3 $FW_wname, 1, $msg;
@ -851,8 +956,8 @@ SVG_doShowLog($$$$;$$)
# Read the data from the filelog
my $oll = $attr{global}{verbose};
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
my @path = split(" ", FW_fC("get $d $file $tmpfile $f $t " .
join(" ", @{$flog})));
my @path = split(" ",
FW_fC("get $d $file $tmpfile $f $t $srcDesc->{all}"));
$attr{global}{verbose} = $oll;
# replace the path with the temporary filenames of the filelog output
@ -878,8 +983,8 @@ SVG_doShowLog($$$$;$$)
my ($f,$t)=($SVG_devs{$d}{from}, $SVG_devs{$d}{to});
my $oll = $attr{global}{verbose};
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
my @path = split(" ", FW_fC("get $d $file $tmpfile $f $t " .
join(" ", @{$flog})));
my @path = split(" ",
FW_fC("get $d $file $tmpfile $f $t $srcDesc->{all}"));
$attr{global}{verbose} = $oll;
# replace the path with the temporary filenames of the filelog output
@ -915,8 +1020,7 @@ SVG_doShowLog($$$$;$$)
$f = 0 if(!$f); # From the beginning of time...
$t = 9 if(!$t); # till the end
Log3 $FW_wname, 5,
"plotcommand: get $d $file INT $f $t " . join(" ", @{$flog});
Log3 $FW_wname, 5, "plotcommand: get $d $file INT $f $t ".$srcDesc->{all};
$FW_RETTYPE = "image/svg+xml";
@ -929,10 +1033,10 @@ SVG_doShowLog($$$$;$$)
close(CFH);
} else {
FW_fC("get $d $file INT $f $t " . join(" ", @{$flog}), 1);
my $da = SVG_getData($wl, $f, $t, $srcDesc, 0); # substcfg needs it(!)
($cfg, $plot) = SVG_substcfg(1, $wl, $cfg, $plot, $file, "<OuT>");
my $ret = SVG_render($wl, $f, $t, $cfg,
$internal_data, $plot, $FW_wname, $FW_cssdir, $flog,
my $ret = SVG_render($wl, $f, $t, $cfg, $da,
$plot, $FW_wname, $FW_cssdir, $srcDesc,
$styleW, $styleH);
$internal_data = "";
FW_pO $ret;
@ -950,6 +1054,58 @@ SVG_doShowLog($$$$;$$)
}
sub
SVG_getData($$$$$)
{
my ($d, $f,$t,$srcDesc,$showData) = @_;
my (@da, $ret, @vals);
my @keys = ("min","max","avg","cnt","currval","mindate","maxdate","lastraw");
foreach my $src (@{$srcDesc->{order}}) {
my $s = $srcDesc->{src}{$src};
my $fname = ($src eq $defs{$d}{LOGDEVICE} ? $defs{$d}{LOGFILE} : "CURRENT");
my $cmd = "get $src $fname INT $f $t ".$s->{arg};
FW_fC($cmd);
if($showData) {
$ret .= "\n$cmd\n\n";
$ret .= $$internal_data;
} else {
push(@da, $internal_data);
for(my $i = 0; $i<=$s->{idx}; $i++) {
my %h;
foreach my $k (@keys) {
$h{$k} = $data{$k.($i+1)};
}
push @vals, \%h;
}
}
}
# Reorder the $data{maxX} stuff
my ($min, $max) = (999999, -999999);
my $no = int(keys %{$srcDesc->{rev}});
for(my $oi = 0; $oi < $no; $oi++) {
my $nl = int(keys %{$srcDesc->{rev}{$oi}});
for(my $li = 0; $li < $nl; $li++) {
my $r = $srcDesc->{rev}{$oi}{$li}+1;
my $val = shift @vals;
foreach my $k (@keys) {
$min = $val->{$k}
if($k eq "min" && defined($val->{$k}) && $val->{$k} < $min);
$max = $val->{$k}
if($k eq "max" && defined($val->{$k}) && $val->{$k} > $max);
$data{"$k$r"} = $val->{$k};
}
}
}
$data{maxAll} = $max;
$data{minAll} = $min;
return $ret if($showData);
return \@da;
}
######################
# Convert the configuration to a "readable" form -> array to hash
@ -1051,17 +1207,16 @@ SVG_render($$$$$$$$$;$$)
my $from = shift; # e.g. 2008-01-01
my $to = shift; # e.g. 2009-01-01
my $confp = shift; # lines from the .gplot file, w/o FileLog and plot
my $dp = shift; # pointer to data (one string)
my $da = shift; # data pointer array
my $plot = shift; # Plot lines from the .gplot file
my $parent_name = shift; # e.g. FHEMWEB instance name
my $parent_dir = shift; # FW_dir
my $flog = shift; # #FileLog lines, as array pointer
my $srcDesc = shift; # #FileLog lines, as array pointer
my $styleW = shift;
my $styleH = shift;
$SVG_RET="";
my $SVG_ss = AttrVal($parent_name, "smallscreen", 0);
return $SVG_RET if(!defined($dp) || $dp eq "");
my $nr_axis = AttrVal($parent_name,"nrAxis","1,1");
my ($nr_left_axis,$nr_right_axis,$use_left_axis,$use_right_axis) =
@ -1084,23 +1239,24 @@ SVG_render($$$$$$$$$;$$)
my $w = $ow-$nr_left_axis*$axis_width-$nr_right_axis*$axis_width;
my $h = $oh-2*$y; # Rect size
# Keep only the Filter part of the #FileLog
$flog = join(" ", map { my @a=split(":",$_);
$a[1]=~s/\.[^\.]*$//; $a[1]; } @{$flog});
$flog = AttrVal($parent_name, "longpollSVG", 0) ? "flog=\" $flog \"" : "";
my $filter = $srcDesc->{all};
$filter =~ s/[^: ]*:([^: ]):[^ ]*/$1/g;
$filter = AttrVal($parent_name, "longpollSVG", 0) ? "flog=\" $filter \"" : "";
my %dataIdx; # Build a reverse Index for the dataSource
######################
# Html Header
# SVG Header
my $svghdr = 'version="1.1" xmlns="http://www.w3.org/2000/svg" '.
'xmlns:xlink="http://www.w3.org/1999/xlink" '.
'id="SVGPLOT_'.(++$SVG_id).'"'.$filter;
if(!$styleW) {
SVG_pO '<?xml version="1.0" encoding="UTF-8"?>';
SVG_pO '<!DOCTYPE svg>';
SVG_pO '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" '.
'xmlns:xlink="http://www.w3.org/1999/xlink" '.$flog.'>';
SVG_pO "<svg $svghdr>";
} else {
SVG_pO '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" '.
'xmlns:xlink="http://www.w3.org/1999/xlink" '.
"style='width:${styleW}px; height:${styleH}px;' ".
'>';
SVG_pO "<svg $svghdr style='width:${styleW}px; height:${styleH}px;'>";
}
my $prf = AttrVal($parent_name, "stylesheetPrefix", "");
@ -1134,17 +1290,6 @@ SVG_render($$$$$$$$$;$$)
SVG_pO "<text id=\"svg_title\" x=\"$off1\" y=\"$off2\" " .
"class=\"title\" text-anchor=\"middle\">$title</text>";
######################
# Copy and Paste labels, hidden by default
SVG_pO "<text id=\"svg_paste\" x=\"" .
($ow-$axis_width-$nr_right_axis*$axis_width) . "\" y=\"$off2\" " .
"onclick=\"parent.svg_paste(evt)\" " .
"class=\"paste\" text-anchor=\"end\"> </text>";
SVG_pO "<text id=\"svg_copy\" x=\"" .
($ow-$nr_right_axis*$axis_width) . "\" y=\"$off2\" " .
"onclick=\"parent.svg_copy(evt)\" " .
"class=\"copy\" text-anchor=\"end\"> </text>";
######################
# Left label = ylabel and right label = y2label
if(!$SVG_ss) {
@ -1198,63 +1343,69 @@ SVG_render($$$$$$$$$;$$)
my ($dxp, $dyp) = (\(), \());
my ($d, $v, $ld, $lv) = ("","","","");
my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
while($dpoff < $dpl) { # using split instead is memory hog
my $ndpoff = index($$dp, "\n", $dpoff);
if($ndpoff == -1) {
$l = substr($$dp, $dpoff);
} else {
$l = substr($$dp, $dpoff, $ndpoff-$dpoff);
}
$dpoff = $ndpoff+1;
if($l =~ m/^#/) {
my $a = $conf{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);
for(my $dIdx=0; $dIdx<@{$da}; $dIdx++) {
my $lIdx = 0;
$idx = $srcDesc->{rev}{$dIdx}{$lIdx};
my $dp = $da->[$dIdx];
my ($dpl,$dpoff,$l) = (length($$dp), 0, "");
while($dpoff < $dpl) { # using split instead is memory hog
my $ndpoff = index($$dp, "\n", $dpoff);
if($ndpoff == -1) {
$l = substr($$dp, $dpoff);
} else {
$l = substr($$dp, $dpoff, $ndpoff-$dpoff);
}
($min, $max) = (99999999, -99999999);
$hdx[$idx] = $dxp; $hdy[$idx] = $dyp;
($dxp, $dyp) = (\(), \());
$idx++;
} elsif( $l =~ /^;/ ) { #allow ;special lines
if( $l =~ m/^;p (\S+)\s(\S+)/ ) {# point
my $xmul = $w/($xmax-$xmin);
my $x1;
if( $conf{xrange} ) {
$x1 = int(($1-$xmin)*$xmul);
} else {
$x1 = $x1;
$dpoff = $ndpoff+1;
if($l =~ m/^#/) {
my $a = $conf{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);
}
my $y1 = $2;
($min, $max) = (99999999, -99999999);
$hdx[$idx] = $dxp; $hdy[$idx] = $dyp;
($dxp, $dyp) = (\(), \());
$lIdx++;
$idx = $srcDesc->{rev}{$dIdx}{$lIdx};
last if(!$idx);
push @{$dxp}, $x1;
push @{$dyp}, $y1;
} elsif( $l =~ /^;/ ) { #allow ;special lines
if( $l =~ m/^;p (\S+)\s(\S+)/ ) {# point
my $xmul = $w/($xmax-$xmin);
my $x1;
if( $conf{xrange} ) {
$x1 = int(($1-$xmin)*$xmul);
} else {
$x1 = $x1;
}
my $y1 = $2;
} elsif( $conf{lType}[$idx] eq "lines" ) {
push @{$dxp}, undef;
push @{$dyp}, $l;
push @{$dxp}, $x1;
push @{$dyp}, $y1;
} elsif( $conf{lType}[$idx] eq "lines" ) {
push @{$dxp}, undef;
push @{$dyp}, $l;
}
} else {
($d, $v) = split(" ", $l);
$d = ($tmul ? int((SVG_time_to_sec($d)-$fromsec)*$tmul) : $d);
if($ld ne $d || $lv ne $v) { # Saves a lot on year zoomlevel
$ld = $d; $lv = $v;
push @{$dxp}, $d;
push @{$dyp}, $v;
$min = $v if($min > $v);
$max = $v if($max < $v);
}
}
} else {
($d, $v) = split(" ", $l);
$d = ($tmul ? int((SVG_time_to_sec($d)-$fromsec)*$tmul) : $d);
if($ld ne $d || $lv ne $v) { # Saves a lot on year zoomlevel
$ld = $d; $lv = $v;
push @{$dxp}, $d;
push @{$dyp}, $v;
$min = $v if($min > $v);
$max = $v if($max < $v);
}
last if($ndpoff == -1);
}
last if($ndpoff == -1);
}
$dxp = $hdx[0];
if(($dxp && int(@{$dxp}) < 2 && !$tosec) || # not enough data and no range...
if(($dxp && int(@{$dxp}) < 2 && !$tosec) || # not enough data and no range...
(!$tmul && !$dxp)) {
SVG_pO "</svg>";
return $SVG_RET;
@ -1392,6 +1543,7 @@ SVG_render($$$$$$$$$;$$)
my (%hstep,%htics,%axdrawn);
my $allowed = AttrVal($FW_wname,"allowedCommands",undef);
#-- yrange handling for axes x1y1..x1y8
for my $idx (0..7) {
my $a = "x1y".($idx+1);
@ -1399,10 +1551,15 @@ SVG_render($$$$$$$$$;$$)
my $yra="y".($idx+1)."range";
$yra="yrange" if ($yra eq "y1range");
#-- yrange is specified in plotfile
if($conf{$yra} && $conf{$yra} =~ /\[(.*):(.*)\]/) {
$hmin{$a} = $1 if($1 ne "");
$hmax{$a} = $2 if($2 ne "");
if($conf{$yra}) {
$conf{$yra} = AnalyzeCommand(undef, $1, $allowed)
if($conf{$yra} =~ /^({.*})$/);
if($conf{$yra} =~ /\[(.*):(.*)\]/) {
$hmin{$a} = $1 if($1 ne "");
$hmax{$a} = $2 if($2 ne "");
}
}
#-- tics handling
my $yt="y".($idx+1)."tics";
$yt="ytics" if ($yt eq"y1tics");
@ -1732,13 +1889,15 @@ SVG_render($$$$$$$$$;$$)
}
my $style = $conf{lStyle}[$i];
$style =~ s/class="/class="legend /;
SVG_pO "<text title=\"$desc\" ".
"onclick=\"parent.svg_labelselect(evt)\" line_id=\"line_$i\" " .
SVG_pO "<text title=\"$desc\" line_id=\"line_$i\" " .
"x=\"$txtoff1\" y=\"$txtoff2\" text-anchor=\"end\" $style>$t</text>";
$txtoff2 += $th;
}
my $fnName = SVG_isEmbed($FW_wname) ? "parent.window.svg_init" : "svg_init";
SVG_pO "<script type='text/javascript'>if(typeof $fnName == 'function') ".
"$fnName('SVGPLOT_$SVG_id')</script>";
SVG_pO "</svg>";
return $SVG_RET;
}
@ -1974,6 +2133,9 @@ plotAsPng(@)
set title &lt;L1&gt;<br></li>
</ul></li>
</ul>
The value minAll and maxAll (representing the minimum/maximum over all
values) is also available from the data hash.
</li>
<a name="title"></a>
@ -2025,6 +2187,9 @@ plotAsPng(@)
regexp switch.on, and "0" for the regexp switch.off.<br>
Write .gplot file again<br>
</ul></li>
<li>If the range is of the form {...}, then it will be evaluated with perl.
The result is a string, and must have the form [min:max]
</li>
</ul>
The visibility of the ploteditor can be configured with the FHEMWEB attribute
<a href="#ploteditor">ploteditor</a>.
@ -2142,7 +2307,7 @@ plotAsPng(@)
k&ouml;nnen ebenfalls die Werte der individuellen Kurve f&uuml;r min,
max, mindate, maxdate, avg, cnt, sum, currval (letzter Wert) und currdate
(letztes Datum) durch Zugriff der entsprechenden Werte &uuml;ber das
DataHash verwendet werden. Siehe untenstehendes Beispiel:<br>
data Hash verwendet werden. Siehe untenstehendes Beispiel:<br>
<ul>
<li>Beschriftunng der rechten und linken y-Achse:<br>
<ul>
@ -2164,6 +2329,8 @@ plotAsPng(@)
</ul>
</li>
</ul>
Die Werte minAll und maxAll (die das Minimum/Maximum aller Werte
repr&auml;sentieren) sind ebenfals im data hash vorhanden.
</li>
<a name="title"></a>
@ -2229,6 +2396,9 @@ plotAsPng(@)
.gplot-Datei erneut speichern<br>
</ul>
</li>
<li>Falls Range der Form {...} entspricht, dann wird sie als Perl -
Expression ausgewertet. Das Ergebnis muss in der Form [min:max] sein.
</li>
</ul>
Die sichtbarkeit des Plot-Editors kann mit dem FHEMWEB Attribut <a
href="#ploteditor">ploteditor</a> konfiguriert werden.

View File

@ -17,81 +17,9 @@ Color_Initialize()
sub
FHEM_colorpickerInit()
{
$data{webCmdFn}{colorpicker} = "FHEM_colorpickerFn";
$data{FWEXT}{colorpicker}{SCRIPT} = "/jscolor/jscolor.js";
}
sub
FHEM_colorpickerFn($$$$$)
{
#return undef;
my ($FW_wname, $d, $FW_room, $cmd, $values) = @_;
my @args = split("[ \t]+", $cmd);
my @value = split( ',', $values );
return undef if($values !~ m/^colorpicker,([^,]*)/);
my $mode = $1;
$mode = "RGB" if( !defined($mode) );
if( $mode eq "CT" ) {
if( !$args[1] && $data{webCmdFn}{slider} ) {
no strict "refs";
my $values = "slider,". join( ',', @value[2..4] );
my $htmlTxt = &{$data{webCmdFn}{slider}}($FW_wname, $d, $FW_room, $cmd, $values);
use strict "refs";
return $htmlTxt;
}
return undef if( !$args[1] );
}
my $trigger = $cmd; #default trigger is the event from the reading with the same name as the command
my $cv = ReadingsVal($d,$cmd,""); #get default value from this reading
if( !$cv ) { #if this reading does not exist ->
$trigger = "RGB"; # trigger name will be RGB
$cv = CommandGet("","$d $cmd"); # get default value from get command
}
$cmd = "" if($cmd eq "state");
my $srf = $FW_room ? "&room=$FW_room" : "";
if( $args[1] ) {
my $c = "cmd=set $d $cmd$srf";
if( $mode eq "CT" ) {
my $ct = $args[1];
my ($r, $g, $b) = Color::ct2rgb( $ct );
$args[1] = Color::rgb2hex( $r, $g, $b );
}
return '<td align="center">'.
"<div onClick=\"FW_cmd('$FW_ME?XHR=1&$c')\" style=\"width:32px;height:19px;".
'border:1px solid #fff;border-radius:8px;background-color:#'. $args[1] .';"></div>'.
'</td>' if( AttrVal($FW_wname, "longpoll", 1));
return '<td align="center">'.
"<a href=\"$FW_ME?$c\">".
'<div style="width:32px;height:19px;'.
'border:1px solid #fff;border-radius:8px;background-color:#'. $args[1] .';"></div>'.
'</a>'.
'</td>';
} elsif(AttrVal($d,"realtimePicker",0)) {
my $c = "$FW_ME?XHR=1&cmd=set $d $cmd %$srf";
my $ci = $c;
$ci = "$FW_ME?XHR=1&cmd=set $d $cmd % : transitiontime 0 : noUpdate$srf" if($defs{$d}->{TYPE} eq "HUEDevice");
return '<td align="center">'.
"<input maxlength='6' size='6' id='colorpicker.$d-$trigger' class=\"color {pickerMode:'$mode',pickerFaceColor:'transparent',pickerFace:3,pickerBorder:0,pickerInsetColor:'red',command:'$ci',onImmediateChange:'colorpicker_setColor(this)'}\" value='$cv' onChange='colorpicker_setColor(this,\"$mode\",\"$c\")'>".
'</td>';
} else {
my $c = "$FW_ME?XHR=1&cmd=set $d $cmd %$srf";
return '<td align="center">'.
"<input maxlength='6' size='6' id='colorpicker.$d-$trigger' class=\"color {pickerMode:'$mode',pickerFaceColor:'transparent',pickerFace:3,pickerBorder:0,pickerInsetColor:'red'}\" value='$cv' onChange='colorpicker_setColor(this,\"$mode\",\"$c\")'>".
'</td>';
}
}
my %dim_values = (
0 => "dim06%",
1 => "dim12%",

View File

@ -1,30 +1,30 @@
#Mon Aug 26 16:36:31 2013
#Sun Jan 4 16:24:11 2015
{
'lcCinema' => {
'Break' => {
'CeilingLight' => 'dim37%',
'ReadingLight' => 'dim37%'
},
'Cinema' => {
'Screen' => 'down',
'Projector' => 'on',
'CeilingLight' => 'off',
'ReadingLight' => 'off',
'TV' => 'off'
},
'WatchTV' => {
'Screen' => 'up',
'Projector' => 'off',
'CeilingLight' => 'off',
'ReadingLight' => 'dim12%',
'TV' => 'on'
},
'AllOff' => {
'Screen' => 'up',
'Projector' => 'off',
'CeilingLight' => 'off',
'ReadingLight' => 'off',
'TV' => 'off'
}
}
}
'lcCinema' => {
'Break' => {
'CeilingLight' => 'dim37%',
'ReadingLight' => 'dim37%'
},
'Cinema' => {
'CeilingLight' => 'off',
'Projector' => 'on',
'Screen' => 'down',
'ReadingLight' => 'off',
'TV' => 'off'
},
'WatchTV' => {
'CeilingLight' => 'off',
'Projector' => 'off',
'Screen' => 'up',
'ReadingLight' => 'dim12%',
'TV' => 'on'
},
'AllOff' => {
'CeilingLight' => 'off',
'Projector' => 'off',
'Screen' => 'up',
'ReadingLight' => 'off',
'TV' => 'off'
}
}
}

View File

@ -1,13 +1,17 @@
#Mon Feb 17 20:54:16 2014
#Sun Jan 4 16:24:11 2015
setstate Alarm on
setstate Alarm 2013-08-23 07:42:48 state on
setstate Alarm 2015-01-04 16:22:21 state on
setstate AllLights on
setstate AllLights 2013-08-25 14:23:20 LastDevice Office
setstate AllLights 2013-08-25 14:23:20 LastDevice_Abs Office
setstate AllLights 2013-08-25 14:23:20 state on
setstate AllLights 2015-01-04 16:22:04 LastDevice Alarm
setstate AllLights 2015-01-04 16:22:04 LastDevice_Abs Alarm
setstate AllLights 2015-01-04 16:22:04 state undefined
setstate AllResidentsAway active
setstate CT off
setstate CT 2015-01-03 01:10:50 ct 3703
setstate CT 2015-01-04 13:30:52 lastCmd off
setstate CT 2015-01-04 16:22:04 state off
setstate CeilingLight dim0%
setstate CeilingLight 2013-08-26 18:01:06 state off
setstate CeilingLight 2015-01-04 16:22:40 state off
setstate Cellar T: 21.1 H: 54.6
setstate Cellar 2013-08-13 08:00:48 DEVFAMILY WS300
setstate Cellar 2013-08-13 08:00:48 DEVTYPE S300TH
@ -26,7 +30,7 @@ setstate Garden 2013-08-13 15:03:17 israining no
setstate Garden 2013-08-13 15:03:17 rain 81.9
setstate Garden 2013-08-13 15:03:17 rain_raw 321
setstate Garden 2013-08-13 15:03:17 rain_raw_adj 321
setstate Garden 2014-02-17 20:54:12 state defined
setstate Garden 2015-01-04 16:19:04 state defined
setstate Garden 2013-08-13 15:03:17 temperature 18.3
setstate Garden 2013-08-13 15:03:17 tsecs 1376805797
setstate Garden 2013-08-13 15:03:17 unknown1 a
@ -34,36 +38,51 @@ setstate Garden 2013-08-13 15:03:17 unknown2 7
setstate Garden 2013-08-13 15:03:17 unknown3 1
setstate Garden 2013-08-13 15:03:17 wind 0.5
setstate Livingroom dim100%
setstate Livingroom 2013-08-23 07:57:21 state on
setstate Livingroom 2015-01-04 16:22:21 state on
setstate Log.Cellar active
setstate Log.Dewpoint active
setstate Log.Garden active
setstate Logfile active
setstate Office on
setstate Office 2013-08-25 14:23:20 state on
setstate Office 2015-01-04 16:22:21 state on
setstate Outdoor on
setstate Outdoor 2013-08-25 14:23:20 state on
setstate Outdoor 2015-01-04 16:22:21 state on
setstate Projector off
setstate Projector 2013-08-26 18:01:06 state off
setstate ReadingLight dim12%
setstate ReadingLight 2013-08-26 18:01:06 state dim12%
setstate Projector 2015-01-04 16:22:40 state off
setstate RGB off
setstate RGB 2015-01-04 12:55:47 hue 220
setstate RGB 2015-01-04 13:30:54 lastCmd off
setstate RGB 2015-01-04 12:55:47 rgb 0054FF
setstate RGB 2015-01-04 16:22:04 state off
setstate ReadingLight dim0%
setstate ReadingLight 2015-01-04 16:22:40 state off
setstate ResidentsComeHome active
setstate SVG_01_Garden initialized
setstate SVG_02_Cellar initialized
setstate SVG_03_Dewpoint initialized
setstate Screen up
setstate Screen 2013-08-26 18:01:06 state up
setstate TV on
setstate TV 2013-08-26 18:01:06 state on
setstate Screen 2015-01-04 16:22:40 state up
setstate TV off
setstate TV 2015-01-04 16:22:40 state off
setstate anyViews 2015-01-04 16:24:11 lockstate 0
setstate anyViews 2015-01-04 16:19:04 state Initialized
setstate anyViews_weblink initialized
setstate autocreate active
setstate colorInit 2015-01-04 16:19:04
setstate dew_all active
setstate eventTypes active
setstate global <no definition>
setstate outdoorNotifier active
setstate initialUsbCheck 2015-01-04 16:19:04
setstate lcCinema AllOff
setstate lcCinema 2015-01-04 16:22:40 state AllOff
setstate outdoorNotifier 2015-01-04 16:22:21
setstate rg_Guest1 none
setstate rg_Guest1 2014-02-16 13:59:50 durTimerAbsence 0
setstate rg_Guest1 2014-02-16 13:34:06 durTimerPresence 0
setstate rg_Guest1 2014-02-15 16:15:34 durTimerSleep 0
setstate rg_Guest1 2015-01-04 16:19:19 durTimerAbsence 00:00:00
setstate rg_Guest1 2015-01-04 16:19:19 durTimerAbsence_cr 0
setstate rg_Guest1 2015-01-04 16:19:19 durTimerPresence 00:00:00
setstate rg_Guest1 2015-01-04 16:19:19 durTimerPresence_cr 0
setstate rg_Guest1 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rg_Guest1 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rg_Guest1 2014-02-16 14:09:20 lastArrival -
setstate rg_Guest1 2014-02-16 14:09:29 lastAwake 0
setstate rg_Guest1 2014-02-16 14:09:20 lastDeparture 2014-02-16 14:09:20
@ -80,9 +99,12 @@ setstate rg_Guest1 2014-02-16 14:09:20 presence absent
setstate rg_Guest1 2014-02-16 14:09:20 state none
setstate rg_Guest1 2014-02-15 16:16:27 wayhome 0
setstate rg_Guest2 none
setstate rg_Guest2 2014-02-15 16:15:39 durTimerAbsence 0
setstate rg_Guest2 2014-02-16 13:34:05 durTimerPresence 0
setstate rg_Guest2 2014-02-15 16:15:39 durTimerSleep 0
setstate rg_Guest2 2015-01-04 16:19:19 durTimerAbsence 00:00:00
setstate rg_Guest2 2015-01-04 16:19:19 durTimerAbsence_cr 0
setstate rg_Guest2 2015-01-04 16:19:19 durTimerPresence 00:00:00
setstate rg_Guest2 2015-01-04 16:19:19 durTimerPresence_cr 0
setstate rg_Guest2 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rg_Guest2 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rg_Guest2 2014-02-16 14:01:22 lastArrival -
setstate rg_Guest2 2014-02-16 14:09:29 lastAwake 0
setstate rg_Guest2 2014-02-16 14:00:46 lastDeparture 2014-02-16 14:00:45
@ -99,8 +121,8 @@ setstate rg_Guest2 2014-02-16 14:00:46 presence absent
setstate rg_Guest2 2014-02-16 14:01:22 state none
setstate rg_Guest2 2014-02-15 16:16:29 wayhome 0
setstate rgr_Children home
setstate rgr_Children 2014-02-17 20:45:12 lastActivity home
setstate rgr_Children 2014-02-17 20:45:12 lastActivityBy Baby
setstate rgr_Children 2015-01-04 16:19:19 lastActivity gone
setstate rgr_Children 2015-01-04 16:19:19 lastActivityBy Son
setstate rgr_Children 2014-02-16 14:31:05 lastArrival 2014-02-16 14:31:05
setstate rgr_Children 2014-02-17 20:45:12 lastAwake 2014-02-17 20:45:12
setstate rgr_Children 2014-02-16 14:26:55 lastDeparture 2014-02-16 14:26:55
@ -110,10 +132,10 @@ setstate rgr_Children 2014-02-17 20:45:12 lastDurSleep 29:47:11
setstate rgr_Children 2014-02-16 14:58:01 lastSleep 2014-02-16 14:58:01
setstate rgr_Children 2014-02-17 20:45:12 lastState asleep
setstate rgr_Children 2014-02-16 14:31:05 presence present
setstate rgr_Children 2014-02-16 14:58:01 residentsAbsent 2
setstate rgr_Children 2015-01-04 16:19:19 residentsAbsent 0
setstate rgr_Children 2014-02-17 20:45:12 residentsAsleep 0
setstate rgr_Children 2014-02-16 14:06:48 residentsAwoken 0
setstate rgr_Children 2014-02-16 14:58:00 residentsGone 0
setstate rgr_Children 2015-01-04 16:19:19 residentsGone 2
setstate rgr_Children 2014-02-16 14:31:13 residentsGotosleep 0
setstate rgr_Children 2014-02-15 16:16:25 residentsGuests 0
setstate rgr_Children 2014-02-17 20:45:12 residentsHome 1
@ -144,8 +166,8 @@ setstate rgr_Guests 2014-02-16 14:09:20 residentsTotalPresent 0
setstate rgr_Guests 2014-02-15 16:16:27 residentsTotalWayhome 0
setstate rgr_Guests 2014-02-16 14:09:20 state none
setstate rgr_Parents home
setstate rgr_Parents 2014-02-17 20:45:19 lastActivity absent
setstate rgr_Parents 2014-02-17 20:45:19 lastActivityBy Father
setstate rgr_Parents 2015-01-04 16:19:19 lastActivity gone
setstate rgr_Parents 2015-01-04 16:19:19 lastActivityBy Father
setstate rgr_Parents 2014-02-16 14:16:17 lastArrival 2014-02-16 14:16:17
setstate rgr_Parents 2014-02-15 16:39:35 lastAwake 2014-02-15 16:39:35
setstate rgr_Parents 2014-02-16 14:16:16 lastDeparture 2014-02-16 14:16:16
@ -155,10 +177,10 @@ setstate rgr_Parents 2014-02-15 16:39:35 lastDurSleep 00:02:44
setstate rgr_Parents 2014-02-15 16:36:51 lastSleep 2014-02-15 16:36:51
setstate rgr_Parents 2014-02-16 14:16:17 lastState absent
setstate rgr_Parents 2014-02-16 14:16:17 presence present
setstate rgr_Parents 2014-02-17 20:45:19 residentsAbsent 1
setstate rgr_Parents 2015-01-04 16:19:19 residentsAbsent 0
setstate rgr_Parents 2014-02-16 13:31:53 residentsAsleep 0
setstate rgr_Parents 2014-02-16 13:31:53 residentsAwoken 0
setstate rgr_Parents 2014-02-17 20:45:19 residentsGone 0
setstate rgr_Parents 2015-01-04 16:19:19 residentsGone 1
setstate rgr_Parents 2014-02-15 16:36:51 residentsGotosleep 0
setstate rgr_Parents 2014-02-15 16:13:39 residentsGuests 0
setstate rgr_Parents 2014-02-16 14:26:52 residentsHome 1
@ -168,8 +190,8 @@ setstate rgr_Parents 2014-02-16 14:26:52 residentsTotalPresent 1
setstate rgr_Parents 2014-02-15 16:13:39 residentsTotalWayhome 0
setstate rgr_Parents 2014-02-16 14:16:17 state home
setstate rgr_Residents home
setstate rgr_Residents 2014-02-17 20:45:19 lastActivity absent
setstate rgr_Residents 2014-02-17 20:45:19 lastActivityBy Father
setstate rgr_Residents 2015-01-04 16:19:19 lastActivity gone
setstate rgr_Residents 2015-01-04 16:19:19 lastActivityBy Son
setstate rgr_Residents 2014-02-16 14:16:17 lastArrival 2014-02-16 14:16:17
setstate rgr_Residents 2014-02-16 14:02:48 lastAwake 2014-02-16 14:02:48
setstate rgr_Residents 2014-02-16 14:16:16 lastDeparture 2014-02-16 14:16:16
@ -179,10 +201,10 @@ setstate rgr_Residents 2014-02-16 14:02:48 lastDurSleep 00:00:00
setstate rgr_Residents 2014-02-16 14:02:48 lastSleep 2014-02-16 14:02:48
setstate rgr_Residents 2014-02-16 14:16:17 lastState absent
setstate rgr_Residents 2014-02-16 14:16:17 presence present
setstate rgr_Residents 2014-02-17 20:45:19 residentsAbsent 3
setstate rgr_Residents 2015-01-04 16:19:19 residentsAbsent 0
setstate rgr_Residents 2014-02-17 20:45:12 residentsAsleep 0
setstate rgr_Residents 2014-02-16 14:06:49 residentsAwoken 0
setstate rgr_Residents 2014-02-17 20:45:19 residentsGone 0
setstate rgr_Residents 2015-01-04 16:19:19 residentsGone 3
setstate rgr_Residents 2014-02-16 14:31:13 residentsGotosleep 0
setstate rgr_Residents 2014-02-16 14:09:20 residentsGuests 0
setstate rgr_Residents 2014-02-17 20:45:12 residentsHome 2
@ -192,9 +214,12 @@ setstate rgr_Residents 2014-02-16 14:58:01 residentsTotalPresent 2
setstate rgr_Residents 2014-02-15 16:13:39 residentsTotalWayhome 0
setstate rgr_Residents 2014-02-16 14:16:17 state home
setstate rr_Baby home
setstate rr_Baby 2014-02-16 14:30:21 durTimerAbsence 0
setstate rr_Baby 2014-02-17 20:44:42 durTimerPresence 1814
setstate rr_Baby 2014-02-17 20:45:12 durTimerSleep 0
setstate rr_Baby 2015-01-04 16:19:19 durTimerAbsence 00:00:00
setstate rr_Baby 2015-01-04 16:19:19 durTimerAbsence_cr 0
setstate rr_Baby 2015-01-04 16:23:19 durTimerPresence 7729:52:58
setstate rr_Baby 2015-01-04 16:23:19 durTimerPresence_cr 463793
setstate rr_Baby 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rr_Baby 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rr_Baby 2014-02-16 14:30:21 lastArrival 2014-02-16 14:30:21
setstate rr_Baby 2014-02-17 20:45:12 lastAwake 2014-02-17 20:45:12
setstate rr_Baby 2014-02-16 14:32:38 lastDeparture 0
@ -208,10 +233,13 @@ setstate rr_Baby 2014-02-17 20:45:12 mood calm
setstate rr_Baby 2014-02-16 14:30:21 presence present
setstate rr_Baby 2014-02-17 20:45:12 state home
setstate rr_Baby 2014-02-16 14:30:21 wayhome 0
setstate rr_Daughter absent
setstate rr_Daughter 2014-02-17 20:45:42 durTimerAbsence 1787
setstate rr_Daughter 2014-02-16 14:26:54 durTimerPresence 0
setstate rr_Daughter 2014-02-16 14:02:48 durTimerSleep 0
setstate rr_Daughter gone
setstate rr_Daughter 2015-01-04 16:23:19 durTimerAbsence 7729:25:20
setstate rr_Daughter 2015-01-04 16:23:19 durTimerAbsence_cr 463765
setstate rr_Daughter 2015-01-04 16:19:19 durTimerPresence 00:00:00
setstate rr_Daughter 2015-01-04 16:19:19 durTimerPresence_cr 0
setstate rr_Daughter 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rr_Daughter 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rr_Daughter 2014-02-16 14:57:58 lastArrival 2014-02-16 14:57:58
setstate rr_Daughter 2014-02-16 14:02:48 lastAwake 2014-02-16 14:02:48
setstate rr_Daughter 2014-02-16 14:57:59 lastDeparture 2014-02-16 14:57:59
@ -221,32 +249,38 @@ setstate rr_Daughter 2014-02-16 14:02:48 lastDurSleep 00:08:12
setstate rr_Daughter 2014-02-16 14:57:59 lastLocation home
setstate rr_Daughter 2014-02-16 14:57:59 lastMood calm
setstate rr_Daughter 2014-02-16 13:54:36 lastSleep 2014-02-16 13:54:36
setstate rr_Daughter 2014-02-16 14:57:59 lastState home
setstate rr_Daughter 2015-01-04 16:19:19 lastState absent
setstate rr_Daughter 2014-02-16 14:57:59 location underway
setstate rr_Daughter 2014-02-16 14:57:59 mood -
setstate rr_Daughter 2014-02-16 14:57:59 presence absent
setstate rr_Daughter 2014-02-16 14:57:59 state absent
setstate rr_Daughter 2015-01-04 16:19:19 state gone
setstate rr_Daughter 2014-02-16 13:46:26 wayhome 0
setstate rr_Father absent
setstate rr_Father 2014-02-17 20:45:19 durTimerAbsence 1818
setstate rr_Father 2014-02-16 14:26:52 durTimerPresence 0
setstate rr_Father 2014-02-16 13:46:02 durTimerSleep 0
setstate rr_Father gone
setstate rr_Father 2015-01-04 16:23:19 durTimerAbsence 7729:56:27
setstate rr_Father 2015-01-04 16:23:19 durTimerAbsence_cr 463796
setstate rr_Father 2015-01-04 16:19:19 durTimerPresence 00:00:00
setstate rr_Father 2015-01-04 16:19:19 durTimerPresence_cr 0
setstate rr_Father 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rr_Father 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rr_Father 2014-02-16 14:16:17 lastArrival 2014-02-16 14:16:17
setstate rr_Father 2014-02-16 14:26:52 lastDeparture 2014-02-16 14:26:52
setstate rr_Father 2014-02-16 14:16:17 lastDurAbsence 00:00:01
setstate rr_Father 2014-02-16 14:26:52 lastDurPresence 00:10:35
setstate rr_Father 2014-02-16 14:26:52 lastLocation home
setstate rr_Father 2014-02-16 14:26:52 lastMood calm
setstate rr_Father 2014-02-17 20:45:19 lastState gone
setstate rr_Father 2015-01-04 16:19:19 lastState absent
setstate rr_Father 2014-02-16 14:26:52 location underway
setstate rr_Father 2014-02-16 14:26:52 mood -
setstate rr_Father 2014-02-16 14:26:52 presence absent
setstate rr_Father 2014-02-17 20:45:19 state absent
setstate rr_Father 2015-01-04 16:19:19 state gone
setstate rr_Father 2014-02-16 13:46:02 wayhome 0
setstate rr_Mother home
setstate rr_Mother 2014-02-16 14:04:26 durTimerAbsence 0
setstate rr_Mother 2014-02-17 20:45:42 durTimerPresence 1829
setstate rr_Mother 2014-02-16 13:46:09 durTimerSleep 0
setstate rr_Mother 2015-01-04 16:19:19 durTimerAbsence 00:00:00
setstate rr_Mother 2015-01-04 16:19:19 durTimerAbsence_cr 0
setstate rr_Mother 2015-01-04 16:23:19 durTimerPresence 7730:07:02
setstate rr_Mother 2015-01-04 16:23:19 durTimerPresence_cr 463807
setstate rr_Mother 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rr_Mother 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rr_Mother 2014-02-16 14:16:17 lastArrival 2014-02-16 14:16:17
setstate rr_Mother 2014-02-16 14:16:16 lastDeparture 2014-02-16 14:16:16
setstate rr_Mother 2014-02-16 14:16:17 lastDurAbsence 00:00:01
@ -259,10 +293,13 @@ setstate rr_Mother 2014-02-16 14:16:17 mood calm
setstate rr_Mother 2014-02-16 14:16:17 presence present
setstate rr_Mother 2014-02-16 14:16:17 state home
setstate rr_Mother 2014-02-16 13:46:09 wayhome 0
setstate rr_Son absent
setstate rr_Son 2014-02-17 20:45:42 durTimerAbsence 1787
setstate rr_Son 2014-02-16 14:26:55 durTimerPresence 0
setstate rr_Son 2014-02-16 14:00:53 durTimerSleep 0
setstate rr_Son gone
setstate rr_Son 2015-01-04 16:23:19 durTimerAbsence 7729:25:18
setstate rr_Son 2015-01-04 16:23:19 durTimerAbsence_cr 463765
setstate rr_Son 2015-01-04 16:19:19 durTimerPresence 00:00:00
setstate rr_Son 2015-01-04 16:19:19 durTimerPresence_cr 0
setstate rr_Son 2015-01-04 16:19:19 durTimerSleep 00:00:00
setstate rr_Son 2015-01-04 16:19:19 durTimerSleep_cr 0
setstate rr_Son 2014-02-16 14:58:00 lastArrival 2014-02-16 14:58:00
setstate rr_Son 2014-02-16 14:06:44 lastAwake 2014-02-16 14:06:44
setstate rr_Son 2014-02-16 14:58:01 lastDeparture 2014-02-16 14:58:01
@ -272,12 +309,12 @@ setstate rr_Son 2014-02-16 14:06:44 lastDurSleep 00:00:11
setstate rr_Son 2014-02-16 14:58:01 lastLocation home
setstate rr_Son 2014-02-16 14:58:01 lastMood calm
setstate rr_Son 2014-02-16 14:06:33 lastSleep 2014-02-16 14:06:33
setstate rr_Son 2014-02-16 14:58:01 lastState home
setstate rr_Son 2015-01-04 16:19:19 lastState absent
setstate rr_Son 2014-02-16 14:58:01 location underway
setstate rr_Son 2014-02-16 14:58:01 mood -
setstate rr_Son 2014-02-16 14:58:01 presence absent
setstate rr_Son 2014-02-16 14:58:01 state absent
setstate rr_Son 2015-01-04 16:19:19 state gone
setstate rr_Son 2014-02-16 13:46:38 wayhome 0
setstate sunRise Next: 06:58:34
setstate sunSet Next: 18:20:46
setstate sunRise Next: 07:45:49
setstate sunSet Next: 17:14:06
setstate wlCinema initialized

View File

@ -73,7 +73,7 @@ attr Livingroom icon light_pendant_light
attr Livingroom model fs20di
attr Livingroom room Light
attr Livingroom webCmd dim
define AllLights structure Light Alarm Livingroom Office Outdoor
define AllLights structure Light Alarm Livingroom Office Outdoor CT RGB
attr AllLights devStateIcon undefined:light_question
attr AllLights group Structure
attr AllLights icon light_light
@ -164,7 +164,7 @@ attr ReadingLight eventMap off:dim0% on:dim100%
attr ReadingLight group Light
attr ReadingLight icon light_floor_lamp
attr ReadingLight room Cinema
attr ReadingLight webCmd on:off:dim
attr ReadingLight webCmd on:off:dim:dim 50
define wlCinema weblink htmlCode {LightScene_2html("lcCinema")}
attr wlCinema room Cinema
define lcCinema LightScene Projector Screen TV CeilingLight ReadingLight
@ -185,6 +185,7 @@ attr anyViews dashboard_tab1name Dashboard Demo
attr anyViews dashboard_tab1sorting t0c100,Light,true,518,129:t0c100,Home State,true,496,204:t0c0,Single Lights,true,522,209:t0c0,AV,true,221,170:
attr anyViews dashboard_tabcount 1
attr anyViews dashboard_width 80%
attr anyViews room hidden
define anyViews_weblink weblink htmlCode {DashboardAsHtml("anyViews")}
attr anyViews_weblink room DashboardRoom
@ -288,3 +289,29 @@ attr rr_Baby icon status_available
attr rr_Baby room Residents
attr rr_Baby sortby 0
attr rr_Baby webCmd state
define RGB readingsProxy RGB
attr RGB userattr Light Light_map structexclude
attr RGB Light AllLights
attr RGB alias RGB Light
attr RGB comment light with the ability to change RGB color
attr RGB devStateIcon {Color::devStateIcon("RGB","rgb","rgb","state")}
attr RGB group Color Lights
attr RGB room Light
attr RGB setFn {if( $CMD =~ m/on|off/ ) { $ARGS=$CMD;;$CMD = "state" } else {fhem ("setreading $DEVICE state on");;} if( $CMD =~ m/hue/ ) {my ($r,$g,$b) = Color::hsv2rgb($ARGS/360,1,1);; my $rgb = Color::rgb2hex( $r*255, $g*255, $b*255 );; fhem ("setreading $DEVICE rgb $rgb");;} if( $CMD =~ m/rgb/ && $ARGS =~ m/^(..)(..)(..)/ ) {my( $r, $g, $b ) = (hex($1)/255.0, hex($2)/255.0, hex($3)/255.0);; my ($h,$s,$v) = Color::rgb2hsv($r,$g,$b);; my $hue = int($h*359);; fhem ("setreading $DEVICE hue $hue");;} fhem ("setreading $DEVICE $CMD $ARGS");;return undef;;}
attr RGB setList on:noArg off:noArg rgb:colorpicker,RGB hue:colorpicker,HUE,0,1,359
attr RGB webCmd hue:rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:rgb ffffff:on:off
define colorInit notify global:INITIALIZED {use Color;;Color_Initialize()}
attr colorInit room hidden
define CT readingsProxy CT
attr CT userattr Light Light_map structexclude
attr CT Light AllLights
attr CT alias CT Light
attr CT comment light with the ability to change the color temperature
attr CT devStateIcon {Color::devStateIcon("CT","rgb","rgb","state")}
attr CT getFn { my ($r,$g,$b) = Color::ct2rgb( ReadingsVal($DEVICE,"ct",333) );; return (Color::rgb2hex($r,$g,$b), 1);; }
attr CT getList rgb:noArg
attr CT group Color Lights
attr CT room Light
attr CT setFn {if( $CMD =~ m/on|off/ ) { $ARGS=$CMD;;$CMD = "state" } else {fhem ("setreading $DEVICE state on");;} fhem ("setreading $DEVICE $CMD $ARGS");;return undef;;}
attr CT setList on:noArg off:noArg ct:colorpicker,CT,2000,1,6500
attr CT webCmd ct::ct 2040:ct 2630:ct 3703:ct 6250:on:off

View File

@ -37,13 +37,18 @@ div.block { border:1px solid gray; background: #F8F8E0; padding:0.7em; }
.makeSelect { display:inline; float:left; clear:left; }
select { margin-left:5px; margin-right:5px; }
.slider { float:left; width:250px; height:26px; }
.colorpicker_ct .slider { background: url(../jscolor/ct_background.svg); }
.colorpicker_hue .slider { background: url(../jscolor/hue_background.svg); }
.get,.set,.attr { margin-bottom:5px; float:left; }
select.svgSrc { width:100px; }
select.svgColumn { width:50px; }
select.svgRegexp { width:120px; }
.handle { position:relative; cursor:pointer; width:50px;
height:20px; line-height:20px;
-webkit-user-select:none; -moz-user-select:none; -user-select:none;
border:3px solid; color:#278727; text-align:center; }
.downText { margin-top:2px; }
.makeSelect .slider {background:#F0F0D8; border-radius:8px;} /* detail only */
.set .set { margin-bottom:2px; margin-top:3px; } /* timepicker */
pre { white-space: pre-wrap; }
@ -60,8 +65,23 @@ svg.on,svg.FS20_on { fill:orange; }
border-color:transparent; }
.rc_button img:active { border-color: gray; }
table#atWizard td:first-child { width: 240px; }
/* jQuery-UI mods */
div.ui-dialog { border:3px solid #278727; padding: 0.2em; }
div.ui-dialog div.ui-dialog-titlebar { display:none; }
div.ui-widget-content { background:#FFFFE7; }
#fwmenu {
position: absolute; z-index:1005;
text-align:left; max-width:600px;
font-weight: normal; font-size: 100%;
background:#FFFFE7; border:1px solid #278727;
}
#fwmenu li a { color:#278727; }
div#svgmarker {
position: absolute; z-index:1005; padding: 6px 10px;
text-align:left; max-width:600px;
color:#278727; background:#FFFFE7;
border:2px solid #278727; border-radius:4px;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +1,143 @@
function
FW_colorpickerUpdateLine(d)
{
var name = "colorpicker."+d[0];
el = document.getElementById(name);
if(el) {
if( d[1].length > 6 ) {
d[1] = d[1].slice(0,6);
}
el.color.fromString(d[1]);
}
}
function
colorpicker_setColor(el,mode,cmd)
{
var v = el.color;
if(mode==undefined) {
mode=el.pickerMode;
}
if(cmd==undefined) {
cmd=el.command;
}
if(v==undefined) {
v=el.toString();
}
if(mode=="HSV") {
v = (0x100 | Math.round(42*el.color.hsv[0])).toString(16).substr(1) +
(0x100 | Math.round(255*el.color.hsv[1])).toString(16).substr(1) +
(0x100 | Math.round(255*el.color.hsv[2])).toString(16).substr(1);
}
var req = new XMLHttpRequest();
var qcmd = addcsrf(cmd.replace('%',v));
req.open("GET", qcmd, true);
req.send(null);
if( 0 )
if(cmd)
document.location = cmd.replace('%',v);
}
//TODO: realtime picker
//
FW_widgets['colorpicker'] = {
updateLine:FW_colorpickerUpdateLine
createFn:FW_colorpickerCreate,
};
function
FW_colorpickerCreate(elName, devName, vArr, currVal, set, params, cmd)
{
if( 0 ) {
console.log( "elName: "+elName );
console.log( "devName: "+devName );
console.log( "vArr: "+vArr );
console.log( "currVal: "+currVal );
console.log( "set: "+set );
console.log( "params: "+params );
console.log( "cmd: "+cmd );
}
if(!vArr.length || vArr[0] != "colorpicker")
return undefined;
var mode = "RGB";
if( vArr.length >= 1 )
mode = vArr[1]
//console.log( "mode: "+mode );
if( params && params.length ) {
var color = params[0];
if( mode == "CT" )
color = colorpicker_ct2rgb(color);
var newEl = $('<div informID="###" style="width:32px;height:19px;border:1px solid #fff;border-radius:8px;background-color:#'+color+'" >').get(0);
$(newEl).click(function(arg) { cmd(params[0]) });
return newEl;
}
if( mode == "CT" ) {
var newEl = FW_createSlider(elName, devName, ["slider",vArr[2],vArr[3],vArr[4]], currVal, set, params, cmd);
$(newEl).addClass("colorpicker_ct");
return newEl;
} else if( mode == "HUE" ) {
var newEl = FW_createSlider(elName, devName, ["slider",vArr[2],vArr[3],vArr[4]], currVal, set, params, cmd);
$(newEl).addClass("colorpicker_hue");
return newEl;
}
if( currVal )
currVal = currVal.toUpperCase();
var newEl = $("<div style='display:inline-block'>").get(0);
$(newEl).append('<input type="text" id="colorpicker.'+ devName +'-'+set +'" maxlength="6" size="6">');
var inp = $(newEl).find("[type=text]");
var myPicker = new jscolor.color(inp.get(0),
{pickerMode:'RGB',pickerFaceColor:'transparent',pickerFace:3,pickerBorder:0,pickerInsetColor:'red'});
inp.get(0).color = myPicker;
if( currVal ) {
if( currVal.length > 6 ) currVal = currVal.slice(0,6);
myPicker.fromString(currVal);
}
if( elName )
$(inp).attr("name", elName);
if( cmd )
$(newEl).change(function(arg) { cmd( myPicker.toString() ) });
else
$(newEl).change(function(arg) { $(inp).attr("value", myPicker.toString() ) });
newEl.setValueFn = function(arg){ if( arg.length > 6 ) arg = arg.slice(0,6);
myPicker.fromString(arg); };
return newEl;
}
function
colorpicker_ct2rgb(ct)
{
// calculation from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code
if( ct > 1000 ) // kelvin -> mired
ct = 1000000/ct;
// adjusted by 1000K
var temp = (1000000/ct)/100 + 10;
var r = 0;
var g = 0;
var b = 0;
r = 255;
if( temp > 66 )
r = 329.698727446 * Math.pow((temp - 60), -0.1332047592);
if( r < 0 )
r = 0;
else if( r > 255 )
r = 255;
if( temp <= 66 )
g = 99.4708025861 * Math.log(temp) - 161.1195681661;
else
g = 288.1221695283 * Math.pow((temp - 60), -0.0755148492);
if( g < 0 )
g = 0;
else if( g > 255 )
g = 255;
b = 255;
if( temp <= 19 )
b = 0;
if( temp < 66 )
b = 138.5177312231 * Math.log(temp-10) - 305.0447927307;
r = Math.round( r );
g = Math.round( g );
b = Math.round( b );
if( b < 0 )
b = 0;
else if( b > 255 )
b = 255;
return colorpicker_rgb2hex(r,g,b);
}
function
colorpicker_rgb2hex(r,g,b) {
if( g !== undefined )
return Number(0x1000000 + r*0x10000 + g*0x100 + b).toString(16).substring(1);
else
return Number(0x1000000 + r[0]*0x10000 + r[1]*0x100 + r[2]).toString(16).substring(1);
}

View File

@ -0,0 +1,38 @@
// Wrapper for the jquery knob widget.
FW_widgets['knob'] = { createFn:FW_knobCreate, };
function
FW_knobCreate(elName, devName, vArr, currVal, set, params, cmd)
{
if(!vArr.length || vArr[0] != "knob" || (params && params.length))
return undefined;
var conf = {};
for(var i1=0; i1<vArr.length; i1++) {
var kv = vArr[i1].split(":");
conf[kv[0]] = kv[1];
}
currVal = (currVal == undefined) ?
conf.min : parseFloat(currVal.replace(/[^\d.\-]/g, ""));
if(!conf.width) conf.width=conf.height=100;
if(!conf.fgColor) conf.fgColor="#278727";
var newEl = $("<div style='display:inline-block'>").get(0);
$(newEl).append('<input type="text" id="knob.'+devName+'-'+set +'" >');
var inp = $(newEl).find("input");
if(elName)
$(inp).attr("name", elName);
for(c in conf)
$(inp).attr("data-"+c, conf[c]);
loadScript("pgm2/jquery.knob.min.js",
function() {
inp.knob({ 'release' : function(v){ if(cmd) cmd(v) } });
newEl.setValueFn = function(arg){ inp.val(arg);
inp.trigger('change'); };
newEl.setValueFn(currVal);
});
return newEl;
}

View File

@ -1,90 +0,0 @@
function
FW_multipleSelChange(name, devName, vArr)
{
if(vArr.length < 2 || (vArr[0] != "multiple" && vArr[0] != "multiple-strict"))
return undefined;
var o = new Object();
o.newEl = document.createElement('input');
o.newEl.type='text';
o.newEl.size=30;
o.qFn = 'FW_multipleSetSelected(qArg, "%")';
o.qArg = o.newEl;
o.newEl.setAttribute('onFocus', vArr[0] == "multiple-strict" ?
'FW_multipleSelect(this, true)' : 'FW_multipleSelect(this, false)');
o.newEl.setAttribute('allVals', vArr);
o.newEl.setAttribute('readonly', 'readonly');
return o;
}
function
FW_multipleSelect(el, strict)
{
loadLink("pgm2/jquery-ui.min.css");
loadScript("pgm2/jquery.min.js", function(){
loadScript("pgm2/jquery-ui.min.js", function() {
var sel = $(el).val().split(","), selObj={};
for(var i1=0; i1<sel.length; i1++)
selObj[sel[i1]] = 1;
var vArr = $(el).attr("allVals").replace(/#/g, " ").split(",");
var table = "";
for(var i1=1; i1<vArr.length; i1++) {
var v = vArr[i1];
table += '<tr>'+ // funny stuff for ios6 style, forum #23561
'<td><div class="checkbox"><input name="'+v+'" type="checkbox"'+
(selObj[v] ? " checked" : "")+'/>'+
'<label for="'+v+'"><span></span></label></div></td>'+
'<td><label for="' +v+'">'+v+'</label></td></tr>';
delete(selObj[v]);
}
var selArr=[];
for(var i1 in selObj)
selArr.push(i1);
$('body').append(
'<div id="multidlg" style="display:none">'+
'<table>'+table+'</table>'+(!strict ? '<input id="md_freeText" '+
'value="'+selArr.join(',')+'"/>' : '')+
'</div>');
$('#multidlg').dialog(
{ modal:true, closeOnEscape:false, maxHeight:$(window).height()*3/4,
buttons:[
{ text:"Cancel", click:function(){ $('#multidlg').remove(); }},
{ text:"OK", click:function(){
var res=[];
if($("#md_freeText").val())
res.push($("#md_freeText").val());
$("#multidlg table input").each(function(){
if($(this).prop("checked"))
res.push($(this).attr("name"));
});
$(el).val(res.join(","));
$('#multidlg').remove();
}}]});
});
});
return false;
}
function
FW_multipleSetSelected(el, val)
{
if(typeof el == 'string')
el = document.getElementById(el);
el.value=val;
}
FW_widgets['multiple'] = {
selChange:FW_multipleSelChange
};
FW_widgets['multiple-strict'] = {
selChange:FW_multipleSelChange
};

View File

@ -1,14 +0,0 @@
function
FW_noArgSelChange(name, devName, vArr)
{
if(vArr.length != 1 || vArr[0] != "noArg")
return undefined;
var o = new Object();
o.newEl = document.createElement('div');
return o;
}
FW_widgets['noArg'] = {
selChange:FW_noArgSelChange
};

View File

@ -1,153 +0,0 @@
/*************** SLIDER **************/
function
FW_sliderUpdateLine(d)
{
for(var k=0; k<2; k++) {
var name = "slider."+d[0];
if(k == 1)
name = name+"-"+d[1].replace(/[ \d].*$/,'');
el = document.getElementById(name);
if(el) {
var doSet = 1; // Only set the "state" slider in the detail view
if(el.parentNode.getAttribute("name") == "val.set"+d[0]) {
var el2 = document.getElementsByName("arg.set"+d[0])[0];
if(el2.nodeName.toLowerCase() == "select" &&
el2.options[el2.selectedIndex].value != "state")
doSet = 0;
}
if(doSet) {
var val = d[1].replace(/^.*?([.\-\d]+).*/g, "$1"); // get first number
if(!val.match(/[.\-\d]+/))
val = 0;
FW_sliderCreate(el, val);
}
}
}
}
function
FW_sliderCreate(slider, curr)
{
var sh = slider.firstChild;
var lastX=-1, offX=0, maxX=0, val;
var min = parseFloat(slider.getAttribute("min"));
var stp = parseFloat(slider.getAttribute("stp"));
var max = parseFloat(slider.getAttribute("max"));
var flt = parseFloat(slider.getAttribute("flt"));
var cmd = slider.getAttribute("cmd");
function
init()
{
maxX = slider.offsetWidth-sh.offsetWidth;
if(curr) {
offX += (curr-min)*maxX/(max-min);
sh.innerHTML = curr;
sh.setAttribute('style', 'left:'+offX+'px;');
}
}
init();
function
touchFn(e, fn)
{
e.preventDefault(); // Prevents Safari from scrolling!
if(e.touches == null || e.touches.length == 0)
return;
e.clientX = e.touches[0].clientX;
fn(e);
}
function
mouseDown(e)
{
var oldFn1 = document.onmousemove, oldFn2 = document.onmouseup,
oldFn3 = document.ontouchmove, oldFn4 = document.ontouchend;
if(maxX == 0)
init();
lastX = e.clientX;
function
mouseMove(e)
{
var diff = e.clientX-lastX; lastX = e.clientX;
offX += diff;
if(offX < 0) offX = 0;
if(offX > maxX) offX = maxX;
val = min+(offX/maxX * (max-min));
val = (flt ? Math.floor(val/stp)*stp :
Math.floor(Math.floor(val/stp)*stp));
sh.innerHTML = val;
sh.setAttribute('style', 'left:'+offX+'px;');
if(cmd && cmd.substring(0,3) == "js:") {
eval(cmd.substring(3).replace('%',val));
}
}
document.onmousemove = mouseMove;
document.ontouchmove = function(e) { touchFn(e, mouseMove); }
document.onmouseup = document.ontouchend = function(e)
{
document.onmousemove = oldFn1; document.onmouseup = oldFn2;
document.ontouchmove = oldFn3; document.ontouchend = oldFn4;
if(cmd) {
if(cmd.substring(0,3) != "js:")
if(typeof val != "undefined") {
if(typeof FW_pollConn != "undefined")
FW_cmd(cmd.replace('%',val)+"&XHR=1");
else
window.location = addcsrf(cmd.replace('%',val));
}
} else {
if(typeof val != "undefined")
slider.nextSibling.setAttribute('value', val);
}
};
};
sh.onselectstart = function() { return false; }
sh.onmousedown = mouseDown;
sh.ontouchstart = function(e) { touchFn(e, mouseDown); }
}
function
FW_sliderSelChange(name, devName, vArr)
{
if(vArr.length < 4 || vArr.length > 5 || vArr[0] != "slider")
return undefined;
var o = new Object();
var min=parseFloat(vArr[1]),
stp=parseFloat(vArr[2]),
max=parseFloat(vArr[3]),
flt=parseFloat(vArr[4]);
if(!flt) flt=0;
o.newEl = document.createElement('div');
o.newEl.innerHTML =
'<div class="slider" id="slider.'+devName+
'" min="'+min+'" stp="'+stp+'" max="'+max+'" flt="'+flt+
'"><div class="handle">'+min+'</div></div>'+
'<input type="hidden" name="'+name+'" value="'+min+'">';
FW_sliderCreate(o.newEl.firstChild, undefined);
o.qFn = 'FW_querySetSlider(qArg, "%")';
o.qArg = o.newEl.firstChild;
return o;
}
function
FW_querySetSlider(el, val)
{
val = val.replace(/[^\d.\-]/g, ""); // remove non numbers
FW_sliderCreate(el, val);
}
FW_widgets['slider'] = {
updateLine:FW_sliderUpdateLine,
selChange:FW_sliderSelChange
};

View File

@ -1,27 +0,0 @@
function
FW_svgUpdateDevs(devs)
{
// if matches, refresh the SVG by removing and readding the embed tag
var embArr = document.getElementsByTagName("embed");
for(var i = 0; i < embArr.length; i++) {
var svg = embArr[i].getSVGDocument();
if(!svg || !svg.firstChild || !svg.firstChild.nextSibling)
continue;
var flog = svg.firstChild.nextSibling.getAttribute("flog");
for(var j=0; j < devs.length; j++) {
if(flog !== null && flog.match(" "+devs[j]+" ")) {
var e = embArr[i];
var newE = document.createElement("embed");
for(var k=0; k<e.attributes.length; k++)
newE.setAttribute(e.attributes[k].name, e.attributes[k].value);
e.parentNode.insertBefore(newE, e);
e.parentNode.removeChild(e);
break;
}
}
}
}
FW_widgets['SVG'] = {
updateDevs:FW_svgUpdateDevs,
};

View File

@ -1,46 +0,0 @@
function
FW_textFieldUpdateLine(d)
{
var name = "textField."+d[0];
el = document.getElementById(name);
if(el)
el.value = d[1];
}
function
FW_textFieldSelChange(name, devName, vArr)
{
if(vArr.length != 1 || vArr[0] != "textField")
return undefined;
var o = new Object();
o.newEl = document.createElement('input');
o.newEl.type='text';
o.newEl.size=30;
o.qFn = 'FW_textFieldSetSelected(qArg, "%")';
o.qArg = o.newEl;
return o;
}
function
FW_textFieldSetSelected(el, val)
{
if(typeof el == 'string')
el = document.getElementById(el);
el.value=val;
}
function
textField_setText(el,cmd)
{
var v = el.value;
var req = new XMLHttpRequest();
var qcmd = addcsrf(cmd.replace('%',v));
req.open("GET", qcmd, true);
req.send(null);
}
FW_widgets['textField'] = {
updateLine:FW_textFieldUpdateLine,
selChange:FW_textFieldSelChange
};

View File

@ -1,69 +0,0 @@
function
FW_timeSet(el,name,val)
{
var el = el.parentNode.parentNode.firstChild;
var v = el.value.split(":");
v[name] = ''+val;
if(v[0].length < 2) v[0] = '0'+v[0];
if(v[1].length < 2) v[1] = '0'+v[1];
el.value = v[0]+":"+v[1];
el.setAttribute('value', el.value);
}
function
FW_timeCreate(el,cmd)
{
var par = el.parentNode;
var v = par.firstChild.value;
var brOff = par.innerHTML.indexOf("<br>");
if(brOff > 0) {
par.innerHTML = par.innerHTML.substring(0, brOff).replace('"-"','"+"');
if(cmd) {
if(typeof cmd == "function")
cmd(v);
else if(typeof FW_pollConn != "undefined")
FW_cmd(cmd.replace('%',v)+"&XHR=1");
else
window.location = addcsrf(cmd.replace('%',v));
}
return;
}
el.setAttribute('value', '-');
if(v.indexOf(":") < 0)
par.firstChild.value = v = "12:00";
var val = v.split(":");
for(var i = 0; i < 2; i++) {
par.appendChild(document.createElement('br'));
var sl = document.createElement('div');
sl.innerHTML = '<div class="slider" min="0" stp='+(i==0?1:5)+
' max='+(i==0?23:55)+
' cmd="js:FW_timeSet(slider,'+i+',%)"'+
'><div class="handle">'+val[i]+
'</div></div>';
par.appendChild(sl);
sl.setAttribute('class', par.getAttribute('class'));
FW_sliderCreate(sl.firstChild, val[i]);
}
}
function
FW_timeSelChange(name, devName, vArr)
{
if(vArr.length != 1 || vArr[0] != "time")
return undefined;
var o = new Object();
o.newEl = document.createElement('div');
o.newEl.innerHTML='<input name="'+name+'" type="text" size="5">'+
'<input type="button" value="+" onclick="FW_timeCreate(this)">';
return o;
}
FW_widgets['time'] = {
selChange:FW_timeSelChange
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

23
fhem/www/pgm2/jquery.knob.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,12 @@
var xmlns="http://www.w3.org/2000/svg";
var old_title;
var old_sel;
var svgdoc;
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var to;
"use strict";
var svgNS = "http://www.w3.org/2000/svg";
var svg_b64 ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var svg_initialized={};
// Base64 encode the xy points (12 bit x, 12 bit y).
function
compressPoints(pointList)
svg_compressPoints(pointList)
{
var i, x, y, lx = -1, ly, ret = "";
var pl_arr = pointList.replace(/^ */,'').split(/[, ]/);
@ -17,24 +16,24 @@ compressPoints(pointList)
if(pl_arr.length > 500 && lx != -1 && x-lx < 2) // Filter the data.
continue;
ret = ret+
b64.charAt((x&0xfc0)>>6)+
b64.charAt((x&0x3f))+
b64.charAt((y&0xfc0)>>6)+
b64.charAt((y&0x3f));
svg_b64.charAt((x&0xfc0)>>6)+
svg_b64.charAt((x&0x3f))+
svg_b64.charAt((y&0xfc0)>>6)+
svg_b64.charAt((y&0x3f));
lx = x; ly = y;
}
return ret;
}
function
uncompressPoints(cmpData)
svg_uncompressPoints(cmpData)
{
var i = 0, ret = "";
while(i < cmpData.length) {
var x = (b64.indexOf(cmpData.charAt(i++))<<6)+
b64.indexOf(cmpData.charAt(i++));
var y = (b64.indexOf(cmpData.charAt(i++))<<6)+
b64.indexOf(cmpData.charAt(i++));
var x = (svg_b64.indexOf(cmpData.charAt(i++))<<6)+
svg_b64.indexOf(cmpData.charAt(i++));
var y = (svg_b64.indexOf(cmpData.charAt(i++))<<6)+
svg_b64.indexOf(cmpData.charAt(i++));
ret += " "+x+","+y;
}
return ret;
@ -42,130 +41,245 @@ uncompressPoints(cmpData)
function
get_cookie()
svg_getcookie()
{
var c = parent.document.cookie;
var c = document.cookie;
if(c == null)
return "";
return [];
var results = c.match('fhemweb=(.*?)(;|$)' );
return (results ? unescape(results[1]) : "");
return (results ? unescape(results[1]).split(":") : []);
}
function
set_cookie(value)
svg_prepareHash(el)
{
parent.document.cookie="fhemweb="+escape(value);
}
function
svg_copy(evt)
{
var d = evt.target.ownerDocument;
var cp = d.getElementById("svg_copy");
cp.firstChild.nodeValue = " ";
set_cookie(old_sel.getAttribute("y_min")+":"+
old_sel.getAttribute("y_mul")+":"+
compressPoints(old_sel.getAttribute("points")));
}
function
svg_paste(evt)
{
var d = evt.target.ownerDocument;
var ps = d.getElementById("svg_paste");
ps.firstChild.nodeValue = " ";
var o=d.createElementNS(xmlns, "polyline");
o.setAttribute("class", "pasted");
var data = get_cookie().split(":", 3);
o.setAttribute("points", uncompressPoints(data[2]));
var h = parseFloat(old_sel.getAttribute("y_h"));
var ny_mul = parseFloat(data[1]);
var ny_min = parseInt(data[0]);
var y_mul = parseFloat(old_sel.getAttribute("y_mul"));
var y_min = parseInt(old_sel.getAttribute("y_min"));
var tr =
"translate(0,"+ (h/y_mul+y_min-h/ny_mul-ny_min)*y_mul +") "+
"scale(1, "+ (y_mul/ny_mul) +") ";
o.setAttribute("transform", tr);
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 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)
old_title = tl.firstChild.nodeValue;
else
old_sel.setAttribute("stroke-width", 1);
sel.setAttribute("stroke-width", 3);
old_sel = sel;
if(sel.getAttribute("points") != null) {
tl.firstChild.nodeValue = evt.target.getAttribute("title");
cp.firstChild.nodeValue = "Copy";
ps.firstChild.nodeValue = (get_cookie()==""?" ":"Paste");
}
showOtherLines(d, lid, 1, 0);
}
var obj = { y_mul:0,y_h:0,y_min:0, decimals:0, x_mul:0,x_off:0,x_min:0 };
for(var name in obj)
obj[name] = parseFloat($(el).attr(name));
return obj;
}
function
svg_click(evt)
{
var t=evt.target;
var y_mul = parseFloat(t.getAttribute("y_mul"));
var y_h = parseFloat(t.getAttribute("y_h"));
var y_min = parseFloat(t.getAttribute("y_min"));
var y_fx = parseFloat(t.getAttribute("decimals"));
var y_org = (((y_h-evt.clientY)/y_mul)+y_min).toFixed(y_fx);
var t = evt.target;
var o = svg_prepareHash(t);
var x_mul = parseFloat(t.getAttribute("x_mul"));
var x_off = parseFloat(t.getAttribute("x_off"));
var x_min = parseFloat(t.getAttribute("x_min"));
var d = new Date((((evt.clientX-x_min)/x_mul)+x_off) * 1000);
var y_org = (((o.y_h-evt.clientY)/o.y_mul)+o.y_min).toFixed(o.decimals);
var d = new Date((((evt.clientX-o.x_min)/o.x_mul)+o.x_off) * 1000);
var ts = (d.getHours() < 10 ? '0' : '') + d.getHours() + ":"+
(d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
var tl = evt.target.ownerDocument.getElementById('svg_title');
var tl = t.ownerDocument.getElementById('svg_title');
tl.firstChild.nodeValue = t.getAttribute("title")+": "+y_org+" ("+ts+")";
}
function
sv_menu(evt, embed)
{
var label = evt.target;
var svg = $(label).closest("svg");
var svgNode = $(svg).get(0);
var lid = $(label).attr("line_id");
var data = svg_getcookie();
var sel = $(svg).find("#"+lid);
var selNode = $(sel).get(0);
var tl = $(svg).find("#svg_title");
var par = svgNode.par;
FW_menu(evt, label,
["Copy", "Paste",
svgNode.isSingle ? "Show all lines":"Hide other lines",
selNode.showVal ? "Stop displaying values" : "Display plot values" ],
[undefined, data.length==0, undefined, selNode.nodeName!="polyline"],
function(arg) {
//////////////////////////////////// copy
if(arg == 0) {
document.cookie="fhemweb="+
$(sel).attr("y_min")+":"+$(sel).attr("y_mul")+":"+
svg_compressPoints($(sel).attr("points"));
}
//////////////////////////////////// paste
if(arg == 1) {
var doc = $(svg).get(0).ownerDocument;
var o=doc.createElementNS(svgNS, "polyline");
o.setAttribute("class", "pasted");
o.setAttribute("points", svg_uncompressPoints(data[2]));
var h = parseFloat($(sel).attr("y_h"));
var ny_mul = parseFloat(data[1]);
var ny_min = parseInt(data[0]);
var y_mul = parseFloat($(sel).attr("y_mul"));
var y_min = parseInt($(sel).attr("y_min"));
var tr =
"translate(0,"+ (h/y_mul+y_min-h/ny_mul-ny_min)*y_mul +") "+
"scale(1, "+ (y_mul/ny_mul) +") ";
o.setAttribute("transform", tr);
doc.documentElement.appendChild(o);
}
//////////////////////////////////// hide/show lines
if(arg == 2) {
if(svgNode.isSingle) {
delete(svgNode.isSingle);
$(sel).attr("stroke-width", 1);
$(tl).html($(tl).attr("hiddentitle"));
showOtherLines(0, 1);
} else {
svgNode.isSingle = 1;
$(sel).attr("stroke-width", 3);
$(tl).attr("hiddentitle", $(tl).html());
if($(sel).attr("points") != null)
$(tl).html($(label).attr("title"));
showOtherLines(1, 0);
}
}
//////////////////////////////////// value display
if(arg == 3) {
var hadShowVal = selNode.showVal;
$(svg).find("[id]").each(function(){delete($(this).get(0).showVal)});
$(svg).off("mousemove");
if(par && par.circle) {
$(par.circle).remove();
$(par.div).remove();
}
if(!hadShowVal) {
selNode.showVal = true;
$(svg).mousemove(mousemove);
svgNode.par = par = svg_prepareHash(selNode);
par.circle =
$(svg).get(0).ownerDocument.createElementNS(svgNS, "circle");
$(par.circle).attr("id", "svgmarker").attr("r", "8");
$(svg).append(par.circle);
par.div = $('<div id="svgmarker">');
par.divoffY = $(embed ? embed : svg).offset().top -
$("#content").offset().top-50;
$("#content").append(par.div);
var pl = selNode.points;
if(pl.length > 2)
mousemove({pageX:pl[pl.length-2].x});
}
}
}, embed);
function
mousemove(e)
{
var xRaw = e.pageX, pl = selNode.points, l = pl.length, i1;
if(!embed)
xRaw -= $(svg).offset().left;
for(i1=0; i1<l; i1++)
if(pl[i1].x > xRaw)
break;
if(i1==l || i1==0)
return;
var pp=pl[i1-1], pn=pl[i1];
var xR = (xRaw-pp.x)/(pn.x-pp.x); // Compute interim values
var yRaw = pp.y+xR*(pn.y-pp.y);
var y = (((par.y_h-yRaw)/par.y_mul)+par.y_min).toFixed(par.decimals);
var d = new Date((((xRaw-par.x_min)/par.x_mul)+par.x_off) * 1000);
var ts = (d.getHours() < 10 ? '0' : '') + d.getHours() + ":"+
(d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
$(par.circle).attr("cx", xRaw).attr("cy", yRaw);
var yd = Math.floor((yRaw+par.divoffY) / 20)*20;
$(par.div).html(ts+" "+y)
.css({ left:xRaw-20, top:yd });
}
function
showOtherLines(currval, maxval)
{
$(svg).find("[id]").each(function(){
var id = $(this).attr("id");
if(id.indexOf("line_") != 0 || id == lid)
return;
var h = parseFloat($(this).attr("y_h"));
$(this).attr("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;
setTimeout(function(){ showOtherLines(currval,maxval) }, 10);
}
}
}
function
svg_init_one(embed, svg)
{
var sid = $(svg).attr("id");
if(svg_initialized[sid])
return;
svg_initialized[sid] = true;
$("text.legend", svg).click(function(e){sv_menu(e, embed)});
}
function
svg_init(par) // also called directly from perl, in race condition
{
$("embed").each(function(){
var e = this;
var src = $(e).attr("src");
var ed = e.getSVGDocument();
if(src.indexOf("SVG_showLog") < 0 || !ed)
return;
var sTag = $("svg", ed)[0];
if((par && $(sTag).attr("id") != par))
return;
svg_init_one(e, sTag);
});
}
$(document).ready(function(){
svg_init(); // <embed><svg>
$("svg[id]").each(function(){ // <svg> (direct)
if($(this).attr("id").indexOf("SVGPLOT") == 0)
svg_init_one(undefined, this);
});
});
// longpollSVG code below
function
FW_svgUpdateDevs(devs)
{
// if matches, refresh the SVG by removing and readding the embed tag
var embArr = document.getElementsByTagName("embed");
for(var i = 0; i < embArr.length; i++) {
var svg = embArr[i].getSVGDocument();
if(!svg || !svg.firstChild || !svg.firstChild.nextSibling)
continue;
var flog = svg.firstChild.nextSibling.getAttribute("flog");
for(var j=0; j < devs.length; j++) {
if(flog !== null && flog.match(" "+devs[j]+" ")) {
var e = embArr[i];
var newE = document.createElement("embed");
for(var k=0; k<e.attributes.length; k++)
newE.setAttribute(e.attributes[k].name, e.attributes[k].value);
e.parentNode.insertBefore(newE, e);
e.parentNode.removeChild(e);
break;
}
}
}
}
FW_widgets.SVG = { updateDevs:FW_svgUpdateDevs };

View File

@ -35,3 +35,5 @@ polyline { stroke:black; fill:none; }
.l1fill_stripe {stroke:green; fill:url(#gr1_stripe);} text.l1fill_stripe {stroke:none; fill:green;}
.l0fill_gyr {stroke:red; fill:url(#gr0_gyr);} text.l0fill_gyr {stroke:none; fill:red;}
circle#svgmarker { fill:#278727; opacity:0.5; }