From 0940c0df0be6f394b9e56a452be62e03d577d969 Mon Sep 17 00:00:00 2001 From: odroegehorn <> Date: Tue, 20 Jan 2009 18:23:07 +0000 Subject: [PATCH] Added iPhone support based on iUI git-svn-id: https://svn.fhem.de/fhem/trunk@337 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/webfrontend/pgm5/README-fhemiphone.txt | 53 + fhem/webfrontend/pgm5/fhemiphone.pl | 1097 ++++++++++++++++++ fhem/webfrontend/pgm5/iUI-LICENSE.txt | 21 + fhem/webfrontend/pgm5/icons/FS20.off.gif | Bin 0 -> 609 bytes fhem/webfrontend/pgm5/icons/FS20.on.gif | Bin 0 -> 377 bytes fhem/webfrontend/pgm5/icons/backButton.png | Bin 0 -> 816 bytes fhem/webfrontend/pgm5/icons/blueButton.png | Bin 0 -> 517 bytes fhem/webfrontend/pgm5/icons/cancel.png | Bin 0 -> 362 bytes fhem/webfrontend/pgm5/icons/grayButton.png | Bin 0 -> 943 bytes fhem/webfrontend/pgm5/icons/iui.css | 387 ++++++ fhem/webfrontend/pgm5/icons/iui.js | 383 ++++++ fhem/webfrontend/pgm5/icons/listArrow.png | Bin 0 -> 259 bytes fhem/webfrontend/pgm5/icons/listArrowSel.png | Bin 0 -> 308 bytes fhem/webfrontend/pgm5/icons/listGroup.png | Bin 0 -> 2867 bytes fhem/webfrontend/pgm5/icons/loading.gif | Bin 0 -> 1435 bytes fhem/webfrontend/pgm5/icons/pinstripes.png | Bin 0 -> 117 bytes fhem/webfrontend/pgm5/icons/selection.png | Bin 0 -> 159 bytes fhem/webfrontend/pgm5/icons/thumb.png | Bin 0 -> 2835 bytes fhem/webfrontend/pgm5/icons/toggle.png | Bin 0 -> 2815 bytes fhem/webfrontend/pgm5/icons/toggleOn.png | Bin 0 -> 163 bytes fhem/webfrontend/pgm5/icons/toolButton.png | Bin 0 -> 531 bytes fhem/webfrontend/pgm5/icons/toolbar.png | Bin 0 -> 171 bytes fhem/webfrontend/pgm5/icons/whiteButton.png | Bin 0 -> 978 bytes 23 files changed, 1941 insertions(+) create mode 100644 fhem/webfrontend/pgm5/README-fhemiphone.txt create mode 100644 fhem/webfrontend/pgm5/fhemiphone.pl create mode 100644 fhem/webfrontend/pgm5/iUI-LICENSE.txt create mode 100644 fhem/webfrontend/pgm5/icons/FS20.off.gif create mode 100644 fhem/webfrontend/pgm5/icons/FS20.on.gif create mode 100644 fhem/webfrontend/pgm5/icons/backButton.png create mode 100644 fhem/webfrontend/pgm5/icons/blueButton.png create mode 100644 fhem/webfrontend/pgm5/icons/cancel.png create mode 100644 fhem/webfrontend/pgm5/icons/grayButton.png create mode 100644 fhem/webfrontend/pgm5/icons/iui.css create mode 100644 fhem/webfrontend/pgm5/icons/iui.js create mode 100644 fhem/webfrontend/pgm5/icons/listArrow.png create mode 100644 fhem/webfrontend/pgm5/icons/listArrowSel.png create mode 100644 fhem/webfrontend/pgm5/icons/listGroup.png create mode 100644 fhem/webfrontend/pgm5/icons/loading.gif create mode 100644 fhem/webfrontend/pgm5/icons/pinstripes.png create mode 100644 fhem/webfrontend/pgm5/icons/selection.png create mode 100644 fhem/webfrontend/pgm5/icons/thumb.png create mode 100644 fhem/webfrontend/pgm5/icons/toggle.png create mode 100644 fhem/webfrontend/pgm5/icons/toggleOn.png create mode 100644 fhem/webfrontend/pgm5/icons/toolButton.png create mode 100644 fhem/webfrontend/pgm5/icons/toolbar.png create mode 100644 fhem/webfrontend/pgm5/icons/whiteButton.png diff --git a/fhem/webfrontend/pgm5/README-fhemiphone.txt b/fhem/webfrontend/pgm5/README-fhemiphone.txt new file mode 100644 index 000000000..1b66ad9ee --- /dev/null +++ b/fhem/webfrontend/pgm5/README-fhemiphone.txt @@ -0,0 +1,53 @@ +Description of the pgm5-iphone webfrontend: + +(c) Olaf Droegehorn + o.droegehorn@dhs-computertechnik.de + www.dhs-computertechnik.de + +General description: + +Web frontend 5 (webfrontend/pgm5) (known upto FHEM 4.2 as pgm2): + +This frontend is CGI/CSS based. It has support for rooms, and FHT/KS300 logs. + +This webfrontend is an updated version for the iPhone(R): +It resides in YOUR HTTP server, and doesn't provide an own, like the FHEMWEB module does. + +How it works: +The iPhone-WebFrontend works the same way as PGM5. + +This is a VERY FIRST BETA !!! +Most of the things are for observing ONLY at the moment! +Changeing states is in progress, but not supported for now. !!! + + + +INSTALLATION: +Copy the file fhemiphone.pl and the whole subdir "icons" to your cgi-bin directory (/home/httpd/cgi-bin), and commandref.html to the html directory (/home/httpd/html) (or also to cgi-bin directory). + +The *.gplot files should be reused from the built-in FHEMWEB and should reside in the installed FHEM directory. Here we don't provide specific *.gplot files as the mechanisms are exactly the same. + +Note: The program looks for icons in the following order: +., , ., + + +NOTE: This is based on IUI (wich is part of the icons-subdir) + + +Copy the file pgm5/02_FHEMRENDERER.pm to the installed FHEM directory. +This gives you a graphic rendering engine (gnuplot & gnuplot-scroll at the moment), which can be configured to renderer images in intervals. + +Call /cgi-bin/fhemiphone.pl + +If you want to show only a part of your devices on a single screen +(i.e divide them into separate rooms), then assign each device the +room attribute in the config file: + + attr ks300 room garden + attr ks300-log room garden + +The attribute title of the global device will be used as title on the first +screen, which shows the list of all rooms. Devices in the room +"hidden" will not be shown. Devices without a room attribute go +to the room "misc". + diff --git a/fhem/webfrontend/pgm5/fhemiphone.pl b/fhem/webfrontend/pgm5/fhemiphone.pl new file mode 100644 index 000000000..ff774a25a --- /dev/null +++ b/fhem/webfrontend/pgm5/fhemiphone.pl @@ -0,0 +1,1097 @@ +#!/usr/bin/perl + +#Note: use warnings/-w is deadly on some linux devices (e.g.WL500GX) +use strict; +use warnings; +use POSIX; + +use Time::HiRes qw(gettimeofday); + + +use CGI; +use IO::Socket; + +################### +# Config +my $addr = "localhost:7072"; # FHZ server +my $absicondir = "/home/httpd/icons"; # Copy your icons here +my $relicondir = "/icons"; +my $gnuplotdir = "/usr/local/FHEM"; # the .gplot filees live here (should be the FHEM dir, as FHEMRENDERER needs them there) +my $fhemwebdir = "/home/httpd/cgi-bin"; # the fhemweb.pl & style.css files live here +my $faq = "/home/httpd/cgi-bin/faq.html"; +my $howto = "/home/httpd/cgi-bin/HOWTO.html"; +my $doc = "/home/httpd/cgi-bin/commandref.html"; +my $tmpfile = "/tmp/pgm6-"; # the Images will be rendered there with beginning of name +my $configfile = "/etc/fhem.conf"; # the fhem.conf file is that +my $plotmode = "gnuplot"; # Current plotmode +my $plotsize = "320,200"; # Size for a plot +my $renderer = "pgm6_renderer"; # Name of suitable renderer +my $rendrefresh= "00:15:00"; # Refresh Interval for the Renderer + + +# Nothing to config below +######################### + +######################### +# Forward declaration +sub checkDirs(); +sub digestCgi(); +sub doDetail($); +sub fhemcmd($); +sub fileList($); +sub makeTable($$$$$$$$); +sub parseXmlList($); +sub showRoom(); +sub showArchive($); +sub showLog($); +sub showLogWrapper($); +sub roomOverview($); +sub style($$); +sub fatal($); +sub zoomLink($$$$); +sub calcWeblink($$); +sub makeEdit($$$$); + + +######################### +# Global variables; +my $me = $ENV{SCRIPT_NAME}; + +my %icons; # List of icons +my $iconsread; # Timestamp of last icondir check +my %rooms; # hash of all rooms +my %devs; # hash of all devices ant their attributes +my %types; # device types, for sorting +my $room; # currently selected room +my $detail; # durrently selected device for detail view +my $title; # Page title +my $cmdret; # Returned data by the fhem call +my $scrolledweblinkcount; # Number of scrolled weblinks +my %pos; # scroll position +my $RET; # Returned data (html) +my $RETTYPE; # image/png or the like +my $SF; # Short for submit form +my $ti; # Tabindex for all input fields +my @zoom; # "qday", "day","week","month","year" +my %zoom; # the same as @zoom +my $wname; # Web instance name +my $data; # Filecontent from browser when editing a file +my $lastxmllist; # last time xmllist was parsed + +my ($lt, $ltstr); + +############### +# Initialize internal structures +my $n = 0; +@zoom = ("qday", "day","week","month","year"); +%zoom = map { $_, $n++ } @zoom; + + +################## +# iPhone Anpassungen: + +$me = "" if(!$me); +my $q = new CGI; +$ti = 1; + +################## +# Lets go: +my ($cmd,$debug) = digestCgi(); + +my $docmd = 0; +$docmd = 1 if($cmd && + $cmd !~ /^showlog/ && + $cmd !~ /^toweblink/ && + $cmd !~ /^showarchive/ && + $cmd !~ /^style / && + $cmd !~ /^edit/); + +$cmdret = fhemcmd($cmd) if($docmd); + +parseXmlList($docmd); + +if($cmd =~ m/^showlog /) { + showLog($cmd); + exit (0); +} + + +if($cmd =~ m/^toweblink (.*)$/) { + my @aa = split(":", $1); + my $max = 0; + for my $d (keys %devs) { + $max = ($1+1) if($d =~ m/^wl_(\d+)$/ && $1 >= $max); + } + $devs{$aa[0]}{INT}{currentlogfile}{VAL} =~ m,([^/]*)$,; + $aa[2] = "CURRENT" if($1 eq $aa[2]); + $cmdret = fhemcmd("define wl_$max weblink fileplot $aa[0]:$aa[1]:$aa[2]"); + if(!$cmdret) { + $detail = "wl_$max"; + parseXmlList($docmd); + } +} + +print $q->header; +print $q->start_html(-name=>$title, -title=>$title, -meta=> {'viewport'=>'width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;'}, -style =>{ -type=>'text/css', -media=>'screen', -src=>'./icons/iui.css'}, -script=>{ -type=>"application/x-javascript", -src=>"./icons/iui.js"}); +print"
"; +print"

"; +print" "; +print"
"; + +#For manually enetered commands +#if($cmdret) { +# $detail = ""; +# $room = ""; +# $cmdret =~ s//>/g; +# print "
\n"; +# print "
$cmdret
\n"; +# print "
\n"; +#} + +if ($cmd =~ m/^style /) { + style($cmd,undef); +} elsif ($detail) { + doDetail($detail); +} elsif ($room && !$detail) { + showRoom(); +} elsif ($cmd =~ /^showlogwrapper/) { + showLogWrapper($cmd); +} elsif ($cmd =~ m/^showarchive/) { + showArchive($cmd); +} else { + roomOverview($cmd); +} + +print $q->end_html; +exit(0); + + +################### +sub +fhemcmd($) +{ + my $p = shift; + + my $server = IO::Socket::INET->new(PeerAddr => $addr); + if(!$server) { + print $q->h3("Can't connect to the server on $addr"); + print $q->end_html; + return 0; + } + syswrite($server, "$p; quit\n"); + my ($lst, $buf) = ("", ""); + while(sysread($server, $buf, 2048) > 0) { + $lst .= $buf; + } + close($server); + return $lst; +} + +########################### +# Digest CGI parameters +sub +digestCgi() +{ + my (%arg, %val, %dev); + my ($cmd, $debug, $c) = ("","",""); + + foreach my $p ($q->param) { + my $v = $q->param($p); + $debug .= "$p : $v
\n"; + + if($p eq "detail") { $detail = $v; } + if($p eq "room") { $room = $v; } + if($p eq "cmd") { $cmd = $v; delete($q->{$p}); } + if($p =~ m/^arg\.(.*)$/) { $arg{$1} = $v; } + if($p =~ m/^val\.(.*)$/) { $val{$1} = $v; } + if($p =~ m/^dev\.(.*)$/) { $dev{$1} = $v; } + if($p =~ m/^cmd\.(.*)$/) { $cmd = $v; $c= $1; delete($q->{$p}); } + if($p eq "pos") { %pos = split(/[=]/, $v); } + if($p eq "data") { $data = $v; } + + + } + $cmd.=" $dev{$c}" if($dev{$c}); + $cmd.=" $arg{$c}" if($arg{$c}); + $cmd.=" $val{$c}" if($val{$c}); + return ($cmd, $debug); +} + +##################### +# Get the data and parse it. We are parsing XML in a non-scientific way :-) +sub +parseXmlList($) +{ + my $docmd = shift; + my $name; + + if(!$docmd && $lastxmllist && (time() - $lastxmllist) < 2) { + $room = $devs{$detail}{ATTR}{room}{VAL} if($detail); + return; + } + + $lastxmllist = time(); + %rooms = (); + %devs = (); + %types = (); + $title = ""; + + foreach my $l (split("\n", fhemcmd("xmllist"))) { + + ####### Device + if($l =~ m/^\t\t<(.*) name="(.*)" state="(.*)" sets="(.*)" attrs="(.*)">/){ + $name = $2; + $devs{$name}{type} = ($1 eq "HMS" ? "KS300" : $1); + $devs{$name}{state} = $3; + $devs{$name}{sets} = $4; + $devs{$name}{attrs} = $5; + next; + } + ####### INT, ATTR & STATE + if($l =~ m,^\t\t\t<(.*) key="(.*)" value="([^"]*)"(.*)/>,) { + my ($t, $n, $v, $m) = ($1, $2, $3, $4); + #### NEW ###### + $v =~ s,<br>,
,g; + $devs{$name}{$t}{$n}{VAL} = $v; + if($m) { + $m =~ m/measured="(.*)"/; + $devs{$name}{$t}{$n}{TIM} = $1; + } + + if($t eq "ATTR" && $n eq "room") { + $rooms{$v}{$name} = 1; + if($name eq "global") { + $rooms{$v}{LogFile} = 1; + $devs{LogFile}{ATTR}{room}{VAL} = $v; + } + } + + if($name eq "global" && $n eq "logfile") { + my $ln = "LogFile"; + $devs{$ln}{type} = "FileLog"; + $devs{$ln}{INT}{logfile}{VAL} = $v; + $devs{$ln}{state} = "active"; + } + } + + } + if(defined($devs{global}{ATTR}{archivedir})) { + $devs{LogFile}{ATTR}{archivedir}{VAL} = + $devs{global}{ATTR}{archivedir}{VAL}; + } + + ################# + #Tag the gadgets without room with "Unsorted" + if(%rooms) { + foreach my $name (keys %devs ) { + if(!$devs{$name}{ATTR}{room}) { + $devs{$name}{ATTR}{room}{VAL} = "Unsorted"; + $rooms{Unsorted}{$name} = 1; + } + } + } + + ############### + # Needed for type sorting + foreach my $d (sort keys %devs ) { + $types{$devs{$d}{type}} = 1; + } + $title = $devs{global}{ATTR}{title}{VAL} ? + $devs{global}{ATTR}{title}{VAL} : "DHS - Office Management"; + $room = $devs{$detail}{ATTR}{room}{VAL} if($detail); +} + +############################## +sub +makeTable($$$$$$$$) +{ + my($d,$t,$header,$hash,$clist,$ccmd,$makelink,$cmd) = (@_); + + return if(!$hash && !$clist); + + $t = "EM" if($t =~ m/^EM.*$/); # EMWZ,EMEM,etc. + print " \n"; + + # Header + print " "; + foreach my $h (split(",", $header)) { + print ""; + } + print "\n"; + if($clist) { + print "\n"; + my @al = map { s/[:;].*//;$_ } split(" ", $clist); + print ""; + print ""; + print ""; + print $q->hidden("dev.$ccmd$d", $d); + print "", $row?"odd":"even"); + $row = ($row+1)%2; + if($makelink && $doc) { + print ""; + } else { + print ""; + } + + if($v eq "DEF") { + makeEdit($d, $t, "modify", $hash->{$v}{VAL}); + } else { + print ""; + } + + print "" if($hash->{$v}{TIM}); + print "" + if($cmd); + + print "\n"; + } + print "
$h
" . $q->popup_menu(-name=>"arg.$ccmd$d", -value=>\@al) . "" . $q->textfield(-name=>"val.$ccmd$d", -size=>6) . "" . $q->submit(-name=>"cmd.$ccmd$d", -value=>$ccmd) . "
\n"; + } + + my $row = 1; + foreach my $v (sort keys %{$hash}) { + printf("
$v$v$hash->{$v}{VAL}$hash->{$v}{TIM}$cmd
\n"; + print "
\n"; + +} + +############################## +sub +showArchive($) +{ + my ($arg) = @_; + my (undef, $d) = split(" ", $arg); + + my $fn = $devs{$d}{INT}{logfile}{VAL}; + if($fn =~ m,^(.+)/([^/]+)$,) { + $fn = $2; + } + $fn = $devs{$d}{ATTR}{archivedir}{VAL} . "/" . $fn; + my $t = $devs{$d}{type}; + + print "
\n"; + print "
\n"; + print "", $row?"odd":"even"); + $row = ($row+1)%2; + if(!defined($l)) { + print(""); + } else { + foreach my $ln (split(",", $l->{VAL})) { + my ($lt, $name) = split(":", $ln); + $name = $lt if(!$name); + print(""); + } + } + print ""; + } + + print "
\n"; + + my $row = 0; + my $l = $devs{$d}{ATTR}{logtype}; + foreach my $f (fileList($fn)) { + printf("
$ftext$name
\n"; + print "
\n"; + print "
\n"; +} + + +############################## +sub +doDetail($) +{ + my ($d) = @_; + + print $q->start_form; + print $q->hidden("detail", $d); + + $room = $devs{$d}{ATTR}{room}{VAL} if($devs{$d}{ATTR}{room}); + + my $t = $devs{$d}{type}; + + print "
\n"; + print "
\n"; + print "Delete $d\n"; + + my $pgm = "Javascript:" . + "s=document.getElementById('edit').style;". + "if(s.display=='none') s.display='block'; else s.display='none';". + "s=document.getElementById('disp').style;". + "if(s.display=='none') s.display='block'; else s.display='none';"; + print "Modify $d"; + + + print "
\n"; + makeTable($d, $t, "State,Value,Measured", + $devs{$d}{STATE}, $devs{$d}{sets}, "set", 0, undef); + makeTable($d, $t, "Internal,Value", + $devs{$d}{INT}, "", undef, 0, undef); + makeTable($d, $t, "Attribute,Value,Action", + $devs{$d}{ATTR}, $devs{$d}{attrs}, "attr", 1, + $d eq "global" ? "" : "delattr"); + print "
\n"; + print "
\n"; + + print $q->end_form; +} + +############## +# Room overview +sub +roomOverview($) +{ + my ($cmd) = @_; + print "
    "; + + ######### + #Alte Kommando-Zeile + # print "Cmd: "; + # print $q->textfield(-name=>"cmd", -size=>30); + + + print "
  • Rooms:
  • "; + + $room = "" if(!$room); + foreach my $r (sort keys %rooms) { + next if($r eq "hidden"); + print "
  • $r
  • "; + } + print "
  • All together
  • "; + + print "
  • Help/Configuration:
  • "; + print "
  • Howto
  • "; + print "
  • FAQ
  • "; + print "
  • Details
  • "; + print "
  • Examples
  • "; + print "
  • Edit files
  • "; + + print "
"; +} + +################# +# Read in the icons +sub +checkDirs() +{ + return if($iconsread && (time() - $iconsread) < 5); + %icons = (); + + if(opendir(DH, $absicondir)) { + while(my $l = readdir(DH)) { + next if($l =~ m/^\./); + my $x = $l; + $x =~ s/\.[^.]+$//; # Cut .gif/.jpg + $icons{$x} = $l; + } + closedir(DH); + } + $iconsread = time(); +} + +######################## +# Generate the html output: i.e present the data +sub +showRoom() +{ + checkDirs(); + my $havelookedforrenderer; + + print $q->start_form( -id => $room, -title => $room, -class => 'panel', -selected => 'true', action => $me.'?room='.$room); + + foreach my $type (sort keys %types) { + + ################# + # Filter the devices in the room + if($room && $room ne "all") { + my $havedev; + foreach my $d (sort keys %devs ) { + next if($devs{$d}{type} ne $type); + next if(!$rooms{$room}{$d}); + $havedev = 1; + last; + } + next if(!$havedev); + } + + my $rf = ($room ? "&room=$room" : ""); + + + ############################ + # Print the table headers + my $t = $type; + $t = "EM" if($t =~ m/^EM.*$/); + + if($type eq "FS20") { + print "

FS20

"; + } + if($type eq "FHT") { + #print " FHT dev.Measured"; + print "

FHT

"; + } + + print "

Logs

" if($type eq "FileLog"); + print "

HMS/KS300 Readings

" if($type eq "KS300"); + print "

Scheduled commands (at)

" if($type eq "at"); + print "

Triggers (notify)

" if($type eq "notify"); + print "

Global variables

" if($type eq "_internal_"); + + print"
"; + foreach my $d (sort keys %devs ) { + + next if($devs{$d}{type} ne $type); + next if($room && $room ne "all" && !$rooms{$room}{$d}); + + ##################### + # Check if the icon exists + + my $v = $devs{$d}{state}; + + if($type eq "FS20") { + + my $v = $devs{$d}{state}; + my $iv = $v; + my $iname = ""; + + $v = "" if(!defined($v)); + print"
"; + print" "; + print"
ONOFF
"; + print"
"; + + } elsif($type eq "FHT") { + + print "
"; + $v = $devs{$d}{STATE}{"measured-temp"}{VAL}; + $v = "" if(!defined($v)); + + $v =~ s/ .*//; + + print " "; + print " "; + + print ""; + + print ""; + + print "
$d"; + print "$v°"; + print ""; + $v = sprintf("%2.1f", int(2*$v)/2) if($v =~ m/[0-9.-]/); + my @tv = map { ($_.".0", $_+0.5) } (16..26); + $v = int($v*20)/$v if($v =~ m/^[0-9].$/); + print $q->hidden("arg.$d", "desired-temp"); + print $q->hidden("dev.$d", $d); + print $q->popup_menu(-name=>"val.$d", -values=>\@tv, -default=>$v, -class=>'input'); + print ""; + + print $q->submit(-name=>"cmd.$d", -value=>"set"); + + print "
"; + print "
"; + + } elsif($type eq "FileLog") { + + print "
"; + print " "; + + print "\n"; + if($devs{$d}{ATTR}{archivedir}) { + print(""); + } + my $l = $devs{$d}{ATTR}{logtype}; + if(!defined($l)) { + my %h = ("VAL" => "text"); + $l = \%h; + } + + + foreach my $f (fileList($devs{$d}{INT}{logfile}{VAL})) { + print "
$d$varchive
"; + print "
"; + print "
"; + print " "; + + print(""); + + foreach my $ln (split(",", $l->{VAL})) { + my ($lt, $name) = split(":", $ln); + $name = $lt if(!$name); + print(""); + } + } + + } elsif($type eq "weblink" && $room ne "all") { + $v = $devs{$d}{INT}{LINK}{VAL}; + $t = $devs{$d}{INT}{WLTYPE}{VAL}; + if($t eq "link") { + print "\n"; + } elsif($t eq "fileplot") { + my @va = split(":", $v, 3); + if(@va != 3 || !$devs{$va[0]}{INT}{currentlogfile}) { + print(""); + } else { + if($va[2] eq "CURRENT") { + $devs{$va[0]}{INT}{currentlogfile}{VAL} =~ m,([^/]*)$,; + $va[2] = $1; + } + + ################### + # Search for fitting renderer + if (!$havelookedforrenderer) { + my $haverend; + foreach my $rend (sort keys %devs ) { + next if($rend ne $renderer); + $haverend = 1; + last; + } + $havelookedforrenderer = 1; + if (!$haverend) { + fhemcmd ("define $renderer FHEMRENDERER"); + fhemcmd ("attr $renderer plotmode $plotmode"); + fhemcmd ("attr $renderer plotsize $plotsize"); + fhemcmd ("attr $renderer refresh $rendrefresh"); + fhemcmd ("attr $renderer tmpfile $tmpfile"); + fhemcmd ("set $renderer on"); + fhemcmd ("get $renderer"); + } + } + print ""; + print ""; + } + } + + } else { + print "
"; + print "
$f$name$dBroken definition: $v"; + + my $wl = "&pos=" . join("=", map {"$_=$pos{$_}"} keys %pos); + + my $arg="$me?cmd=showlog $d $va[0] $va[1] $va[2]$wl"; + if($plotmode eq "SVG") { + my ($w, $h) = split(",", $plotsize); + print "\n"; + } else { + print "\n"; + } + + print ""; + print "$d
"; + print "\n"; + } + print "
$d$v
"; + } + print "
\n"; + } + print $q->end_form; +} + +################# +sub +fileList($) +{ + my ($fname) = @_; + $fname =~ m,^(.*)/([^/]*)$,; # Split into dir and file + my ($dir,$re) = ($1, $2); + return if(!$re); + $re =~ s/%./\.*/g; + my @ret; + return @ret if(!opendir(DH, $dir)); + while(my $f = readdir(DH)) { + next if($f !~ m,^$re$,); + push(@ret, $f); + } + closedir(DH); + return sort @ret; +} + +###################### +sub +showLogWrapper($) +{ + my ($cmd) = @_; + my (undef, $d, $type, $file) = split(" ", $cmd, 4); + my $havelookedforrenderer; + + if($type eq "text") { + $devs{$d}{INT}{logfile}{VAL} =~ m,^(.*)/([^/]*)$,; # Split into dir and file + my $path = "$1/$file"; + $path = $devs{$d}{ATTR}{archivedir}{VAL} . "/$file" if(!-f $path); + + open(FH, $path) || fatal("$path: $!"); + my $cnt = join("", ); + close(FH); + $cnt =~ s//>/g; + + print "
\n"; + print "
$cnt
\n"; + print "
\n"; + + } else { + + ################### + # Search for fitting renderer + if (!$havelookedforrenderer) { + my $havedev; + foreach my $d (sort keys %devs ) { + next if($d ne $renderer); + $havedev = 1; + last; + } + $havelookedforrenderer = 1; + if (!$havedev) { + fhemcmd ("define $renderer FHEMRENDERER"); + fhemcmd ("attr $renderer plotmode $plotmode"); + fhemcmd ("attr $renderer plotsize $plotsize"); + fhemcmd ("attr $renderer refresh $rendrefresh"); + fhemcmd ("attr $renderer tmpfile $tmpfile"); + fhemcmd ("set $renderer on"); + fhemcmd ("get $renderer"); + } + } + print "
\n"; + print "\n"; + print "
\n"; + + print ""; + print "
"; + my $arg = "$me?cmd=showlog undef $d $type $file"; + if($plotmode eq "SVG") { + my ($w, $h) = split(",", $plotsize); + print "\n"; + } else { + print "\n"; + } + + print "
Convert to weblink
\n"; + print "\n"; + print "
\n"; + } +} + +###################### +sub +showLog($) +{ + my ($cmd) = @_; + my (undef, $wl, $d, $type, $file) = split(" ", $cmd, 5); + + my $arguments = "pos=" . join("&", map {"$_=$pos{$_}"} keys %pos); + + if (($wl eq "undef") || ($pos{off}) || ($pos{zoom})) { + if ($wl eq "undef") { + fhemcmd ("get $renderer $d $type $file $arguments"); + } else { + if (!$arguments) { + fhemcmd ("get $renderer $wl $d $type $file"); + } else { + fhemcmd ("get $renderer $wl $d $type $file $arguments"); + } + } + } + + print $q->header(-type=>"image/png"); + + if ($wl eq "undef") { + open (FH, "$tmpfile$file.png"); # read in the result and send it + print join("", ); + close(FH); + unlink ("$tmpfile$file.png"); + } else { + open(FH, "$tmpfile$wl.png"); # read in the result and send it + print join("", ); + close(FH); + } + + exit(0); +} + +################## +sub +fatal($) +{ + my ($msg) = @_; + print $q->header; + print $q->start_html(); + print($msg); + print $q->end_html; + exit(0); +} + +################## +# Multiline (for some types of widgets) editor with submit +sub +makeEdit($$$$) +{ + my ($name, $type, $cmd, $val) = @_; + + print ""; + print "
"; + my $eval = $val; + $eval =~ s,
,\n,g; + + if($type eq "at" || $type eq "notify") { + print ""; + } else { + print ""; + } + $ti++; + print "
" . $q->submit(-name=>"cmd.${cmd}$name", -value=>"$cmd $name"); + + print "
"; + $eval = "
$eval
" if($eval =~ m/\n/); + print "
$eval
"; + print ""; +} + +################## +# Generate the zoom and scroll images with links if appropriate +sub +zoomLink($$$$) +{ + my ($cmd, $img, $alt, $br) = @_; + + my ($d,$off) = split("=", $cmd, 2); + + return if($plotmode eq "gnuplot"); # No scrolling + return if($devs{$d} && $devs{$d}{ATTR}{fixedrange}); + return if($devs{$d} && $devs{$d}{ATTR}{noscroll}); + + my $val = $pos{$d}; + + $cmd = "room=$room&pos="; + if($d eq "zoom") { + + $val = "day" if(!$val); + $val = $zoom{$val}; + return if(!defined($val) || $val+$off < 0 || $val+$off >= int(@zoom) ); + $val = $zoom[$val+$off]; + return if(!$val); + + # Approximation of the next offset. + my $w_off = $pos{off}; + $w_off = 0 if(!$w_off); + if($val eq "qday") { + $w_off = $w_off*4; + } elsif($val eq "day") { + $w_off = ($off < 0) ? $w_off*7 : int($w_off/4); + } elsif($val eq "week") { + $w_off = ($off < 0) ? $w_off*4 : int($w_off/7); + } elsif($val eq "month") { + $w_off = ($off < 0) ? $w_off*12: int($w_off/4); + } elsif($val eq "year") { + $w_off = int($w_off/12); + } + $cmd .= "zoom=$val=off=$w_off"; + + } else { + + return if((!$val && $off > 0) || ($val && $val+$off > 0)); # no future + $off=($val ? $val+$off : $off); + my $zoom=$pos{zoom}; + $zoom = 0 if(!$zoom); + $cmd .= "zoom=$zoom=off=$off"; + + } + + print ""; + print "\"$alt\""; + print "
" if($br); +} + +################## +# Calculate either the number of scrollable weblinks (for $d = undef) or +# for the device the valid from and to dates for the given zoom and offset +sub +calcWeblink($$) +{ + my ($d,$wl) = @_; + + return if($plotmode eq "gnuplot"); + my $now = time(); + + my $zoom = $pos{zoom}; + $zoom = "day" if(!$zoom); + + if(!$d) { + foreach my $d (sort keys %devs ) { + next if($devs{$d}{type} ne "weblink"); + next if(!$room || ($room ne "all" && !$rooms{$room}{$d})); + next if($devs{$d}{ATTR} && $devs{$d}{ATTR}{noscroll}); + next if($devs{$d}{ATTR} && $devs{$d}{ATTR}{fixedrange}); + $scrolledweblinkcount++; + } + return; + } + + return if(!$devs{$wl}); + return if($devs{$wl} && $devs{$wl}{ATTR}{noscroll}); + + if($devs{$wl} && $devs{$wl}{ATTR}{fixedrange}) { + my @range = split(" ", $devs{$wl}{ATTR}{fixedrange}{VAL}); + $devs{$d}{from} = $range[0]; + $devs{$d}{to} = $range[1]; + return; + } + + my $off = $pos{$d}; + $off = 0 if(!$off); + $off += $pos{off} if($pos{off}); + + if($zoom eq "qday") { + + my $t = $now + $off*21600; + my @l = localtime($t); + $l[2] = int($l[2]/6)*6; + $devs{$d}{from} + = sprintf("%04d-%02d-%02d_%02d",$l[5]+1900,$l[4]+1,$l[3],$l[2]); + $devs{$d}{to} + = sprintf("%04d-%02d-%02d_%02d",$l[5]+1900,$l[4]+1,$l[3],$l[2]+6); + + } elsif($zoom eq "day") { + + my $t = $now + $off*86400; + my @l = localtime($t); + $devs{$d}{from} = sprintf("%04d-%02d-%02d",$l[5]+1900,$l[4]+1,$l[3]); + $devs{$d}{to} = sprintf("%04d-%02d-%02d",$l[5]+1900,$l[4]+1,$l[3]+1); + + } elsif($zoom eq "week") { + + my @l = localtime($now); + my $t = $now - ($l[6]*86400) + ($off*86400)*7; + @l = localtime($t); + $devs{$d}{from} = sprintf("%04d-%02d-%02d",$l[5]+1900,$l[4]+1,$l[3]); + + @l = localtime($t+7*86400); + $devs{$d}{to} = sprintf("%04d-%02d-%02d",$l[5]+1900,$l[4]+1,$l[3]); + + + } elsif($zoom eq "month") { + + my @l = localtime($now); + while($off < -12) { + $off += 12; $l[5]--; + } + $l[4] += $off; + $l[4] += 12, $l[5]-- if($l[4] < 0); + $devs{$d}{from} = sprintf("%04d-%02d", $l[5]+1900, $l[4]+1); + + $l[4]++; + $l[4] = 0, $l[5]++ if($l[4] == 12); + $devs{$d}{to} = sprintf("%04d-%02d", $l[5]+1900, $l[4]+1); + + } elsif($zoom eq "year") { + + my @l = localtime($now); + $l[5] += $off; + $devs{$d}{from} = sprintf("%04d", $l[5]+1900); + $devs{$d}{to} = sprintf("%04d", $l[5]+1901); + + } +} + +################## +# List/Edit/Save css and gnuplot files +sub +style($$) +{ + my ($cmd, $msg) = @_; + my @a = split(" ", $cmd); + + if($a[1] eq "list") { + + my @fl; + push(@fl, "fhem.cfg"); + push(@fl, "
"); + push(@fl, fileList("$fhemwebdir/.*.css")); + push(@fl, "
"); + push(@fl, fileList("$gnuplotdir/.*.gplot")); + push(@fl, "
"); + push(@fl, fileList("$fhemwebdir/.*html")); + + print "
\n"; + print "
\n"; + print " $msg

\n" if($msg); + print " \n"; + my $row = 0; + foreach my $file (@fl) { + print ""; + print ""; + $row = ($row+1)%2; + } + print "
$file
\n"; + print "
\n"; + print "
\n"; + + } elsif($a[1] eq "examples") { + + my @fl = fileList("$fhemwebdir/example.*"); + print "
\n"; + print "
\n"; + print " $msg

\n" if($msg); + print " \n"; + my $row = 0; + foreach my $file (@fl) { + print ""; + print ""; + $row = ($row+1)%2; + } + print "
$file
\n"; + print "
\n"; + print "
\n"; + + } elsif($a[1] eq "edit") { + + $a[2] =~ s,/,,g; # little bit of security + my $f = ($a[2] eq "fhem.cfg" ? $configfile : + "$fhemwebdir/$a[2]"); + if(!open(FH, $f)) { + print "$f: $!"; + return; + } + my $data = join("", ); + close(FH); + + print "
\n"; + print "
"; + + print $q->submit(-name=>"save", -value=>"Save $f") . "

"; + + print $q->hidden("cmd", "style save $a[2]"); + print ""; + print "
"; + print "
\n"; + + } elsif($a[1] eq "save") { + + $a[2] =~ s,/,,g; # little bit of security + my $f = ($a[2] eq "fhem.cfg" ? $configfile : + "$fhemwebdir/$a[2]"); + if(!open(FH, ">$f")) { + print "$f: $!"; + return; + } + print FH $data; + close(FH); + style("style list", "Saved file $f"); + $f = ($a[2] eq "fhem.cfg" ? $configfile : $a[2]); + + fhemcmd("rereadcfg") if($a[2] eq "fhem.cfg"); + } + +} + +1; + diff --git a/fhem/webfrontend/pgm5/iUI-LICENSE.txt b/fhem/webfrontend/pgm5/iUI-LICENSE.txt new file mode 100644 index 000000000..c43b26eec --- /dev/null +++ b/fhem/webfrontend/pgm5/iUI-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2007, iUI Project Members + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of the iUI Project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/fhem/webfrontend/pgm5/icons/FS20.off.gif b/fhem/webfrontend/pgm5/icons/FS20.off.gif new file mode 100644 index 0000000000000000000000000000000000000000..c4a2fc94f093f6cae4d7b678f41fc1384404dfa2 GIT binary patch literal 609 zcmZ?wbhEHbhqm5(|<~T#H5M2rL8^nz1{UwCbZ0$&@yFC$CRGQGy8g$ z&Y3=A?u?alCNEtxW$ns2YnRShyLQU@ZF9D6ow51Aip7a*R_1NoP`!3t@9GUp)^1#X z;9%R%eG5*UT6pQ&+)LM1FWq`@`K|+N_io*~bL+-~yZ7$hxnSqHwRWcJJoB^Ox@3+k5T#xqI(#K74lb>G$W)?>&0``u`vX zia%KxxftphbQpjD6ekSq`x@$-n!TIbnyihxj8){?6)ml-t<8JnTI-D`+B@1>oAgRI zsax7Px;opMt0_qf&a`rLcXze5F;$V5?Ao}>RnA7aS8}SAz3U3y?T2StT01&BIXRdr z@2Xcdwz9orBikz_QSaPkE@y6jRZgl|rOU)*hn#G`ii)I)vYbVGz1)AnXJ0>mKgi6& z>%^vZ@&cn%TVwsCm;)CcTsF?Z>Luoa6F*R0IjxS@LOy57|rmaN^l{=mVuo%ef4}$s{qz6--~Xo`Q2ZzAT$GwvlA5AW zo>`Ki5R#Fq;O^-gz@Ye(g^`QFobazu?A8p8oxP$Cb0HJluIVdw!Le zd|ItDL^RFy_J)j~r`vp^)ld2u?cDiz)3(3Qs!p9*E1i41`DLb3cyMB)b3v=GhipiQ zqDN8h1banq@9+uJ$|u`P&6AoVASx-yCNz1`5=Nd0qWnVA+>9Io6BKwvxH&i{%kYQ^ La_(_*WUvMRn_1ST literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/backButton.png b/fhem/webfrontend/pgm5/icons/backButton.png new file mode 100644 index 0000000000000000000000000000000000000000..e27ea8cdf9a4f3b35c78f6bb0a407a4047e72e12 GIT binary patch literal 816 zcmV-01JC@4P)bjHXOOu;C=bm%!b8qQKFBYiNkNO|%7ySIJu2Y@Z0Ra$@|h{{KDF!wUiay4ePIl$1|oy7p)(1ow7 zpJZTzo@Ujo_5>tq2Mlrozr48EHFO7XNgA_29ANiNAo}}5{S52Sbo1IDYVdhwS+_Wy zoKFX!%mpCCX3~4hOG}-xdkE2FW}D0a0cPL4n(>DQ$e1)=fCCL&TtG%ONRX*&K$Hs1 z^0Wo3~(tBk?t!FTGG|Z4oXdx zId|<57s|Dp6h>m#K}vyEsFVO0D?%#V(Rz3m{w?xMP4Uv?Xp}Cb@AZxCtZ0}cc6*JNbM-_bh@vw>o;~9>&@|ZG&9()b uZiGWt*08@evNu^#lbM`SC`$WRfB^uXx!j#d-gDys00006a2G$37eJC1 zL6jIkkr_sy8b+TQNTnJ{ryEPG98R(wN1Gi_wH;8m9Z|X-OQs%7s2)_n9#p{}Rl^@v z#UEG3Aw!5EM2R9qhayFZB464gM~)*%kR)Q>CrXbeWZx)Cjwn*4DpRK}PK_>U;4fOR zFlyj4ZQwa@;X!xfLwMsxdgDlGuSsvbO@HK6gymI&z*vXoS%~IaiRNI9=VXuQXOie{ zi?eTqv2l&Fa)_mKiKTOkymODWb&jxhkFa)+v3i!di=fGsp0SywyPT-NrL)GTwa4zB zu!H~r0J=#;K~xyijmXy$!9W;>;SV7zS{9M$y@u#r5MA#7g3PRw{bw9_&fXWkf9`}J z1UhHm$9o*K`Xixl1;=mfRA;%_3Y(>)9#FT}61$1aVkKFm!Dj@AF(nLS9U==pBsfww zkwRiXBoOZsal{%#3|fuQz{&)Jk|Pu-Ch=p0=?EiFM-h38D55||>5ox0_qa}&ZhXto+2n%b?bJ&~1lBv41sCu<^TcbQd%6WH^KYEFCb>M6lGmB5Yk=QN{x?{B^{XMUP{gvn9ib>3s1 zH?T*~9Jgun6TdTj{!@8wKFcS3#uNS_qcgE!iy1C(p6~dcyy%V`I28q33JVB*RL#r1M2rxNM@=b3by}%6L@3@8Q0(;Sjy)I%7R94p3rUrL+5@vYP9ueYhGNu!f<0K{ z6jZFp!Ms?N02!A_izYnYs}jO#5=IGxf>i&EI&r&A$;Ao;v{Q&GzhTh211rL@0i)f{HoZbRpr95k0S}e8R{OJAhV^m zVT~$T*zmi;G~u6yM`To3i(J}zY2;8J+w=0jrP6Hlo2*N(m38k%-Zx1Wb%chJ@D#tXX2=Gj8NT9dY=dKxAd2~TYrWSK?vHPKQnOR$Xu2W z@F?C`kpoFcLh3r{rh6<8&6e4_FzVbJ4v);cu+VsUvlbeh)3e&tvP2q>->GC8B#}LN z%8uzS4Z0iK+Y>EUv$=F_r`(+jHR@W%5@W` *:not(.toolbar) { + display: none; + position: absolute; + margin: 0; + padding: 0; + left: 0; + top: 45px; + width: 100%; + min-height: 372px; +} + +body[orient="landscape"] > *:not(.toolbar) { + min-height: 268px; +} + +body > *[selected="true"] { + display: block; +} + +a[selected], a:active { + background-color: #194fdb !important; + background-image: url(listArrowSel.png), url(selection.png) !important; + background-repeat: no-repeat, repeat-x; + background-position: right center, left top; + color: #FFFFFF !important; +} + +a[selected="progress"] { + background-image: url(loading.gif), url(selection.png) !important; +} + +/************************************************************************************************/ + +body > .toolbar { + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + border-bottom: 1px solid #2d3642; + border-top: 1px solid #6d84a2; + padding: 10px; + height: 45px; + background: url(toolbar.png) #6d84a2 repeat-x; +} + +.toolbar > h1 { + position: absolute; + overflow: hidden; + left: 50%; + margin: 1px 0 0 -75px; + height: 45px; + font-size: 20px; + width: 150px; + font-weight: bold; + text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + color: #FFFFFF; +} + +body[orient="landscape"] > .toolbar > h1 { + margin-left: -125px; + width: 250px; +} + +.button { + position: absolute; + overflow: hidden; + top: 8px; + right: 6px; + margin: 0; + border-width: 0 5px; + padding: 0 3px; + width: auto; + height: 30px; + line-height: 30px; + font-family: inherit; + font-size: 12px; + font-weight: bold; + color: #FFFFFF; + text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0; + text-overflow: ellipsis; + text-decoration: none; + white-space: nowrap; + background: none; + -webkit-border-image: url(toolButton.png) 0 5 0 5; +} + +.blueButton { + -webkit-border-image: url(blueButton.png) 0 5 0 5; + border-width: 0 5px; +} + +.leftButton { + left: 6px; + right: auto; +} + +#backButton { + display: none; + left: 6px; + right: auto; + padding: 0; + max-width: 55px; + border-width: 0 8px 0 14px; + -webkit-border-image: url(backButton.png) 0 8 0 14; +} + +.whiteButton, +.grayButton { + display: block; + border-width: 0 12px; + padding: 10px; + text-align: center; + font-size: 20px; + font-weight: bold; + text-decoration: inherit; + color: inherit; +} + +.whiteButton { + -webkit-border-image: url(whiteButton.png) 0 12 0 12; + text-shadow: rgba(255, 255, 255, 0.7) 0 1px 0; +} + +.grayButton { + -webkit-border-image: url(grayButton.png) 0 12 0 12; + color: #FFFFFF; +} + +/************************************************************************************************/ + +body > ul > li { + position: relative; + margin: 0; + border-bottom: 1px solid #E0E0E0; + padding: 8px 0 8px 10px; + font-size: 20px; + font-weight: bold; + list-style: none; +} + +body > ul > li.group { + position: relative; + top: -1px; + margin-bottom: -2px; + border-top: 1px solid #7d7d7d; + border-bottom: 1px solid #999999; + padding: 1px 10px; + background: url(listGroup.png) repeat-x; + font-size: 17px; + font-weight: bold; + text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0; + color: #FFFFFF; +} + +body > ul > li.group:first-child { + top: 0; + border-top: none; +} + +body > ul > li > a { + display: block; + margin: -8px 0 -8px -10px; + padding: 8px 32px 8px 10px; + text-decoration: none; + color: inherit; + background: url(listArrow.png) no-repeat right center; +} + +a[target="_replace"] { + box-sizing: border-box; + -webkit-box-sizing: border-box; + padding-top: 25px; + padding-bottom: 25px; + font-size: 18px; + color: cornflowerblue; + background-color: #FFFFFF; + background-image: none; +} + +/************************************************************************************************/ + +body > .dialog { + top: 0; + width: 100%; + min-height: 417px; + z-index: 2; + background: rgba(0, 0, 0, 0.8); + padding: 0; + text-align: right; +} + +.dialog > fieldset { + box-sizing: border-box; + -webkit-box-sizing: border-box; + width: 100%; + margin: 0; + border: none; + border-top: 1px solid #6d84a2; + padding: 10px 6px; + background: url(toolbar.png) #7388a5 repeat-x; +} + +.dialog > fieldset > h1 { + margin: 0 10px 0 10px; + padding: 0; + font-size: 20px; + font-weight: bold; + color: #FFFFFF; + text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; + text-align: center; +} + +.dialog > fieldset > label { + position: absolute; + margin: 16px 0 0 6px; + font-size: 14px; + color: #999999; +} + +input { + box-sizing: border-box; + -webkit-box-sizing: border-box; + width: 100%; + margin: 8px 0 0 0; + padding: 6px 6px 6px 44px; + font-size: 16px; + font-weight: normal; +} + +/************************************************************************************************/ + +body > .panel { + box-sizing: border-box; + -webkit-box-sizing: border-box; + padding: 10px; + background: #c8c8c8 url(pinstripes.png); +} + +.panel > fieldset { + position: relative; + margin: 0 0 20px 0; + padding: 0; + background: #FFFFFF; + -webkit-border-radius: 10px; + border: 1px solid #999999; + text-align: right; + font-size: 16px; +} + +.row { + position: relative; + min-height: 42px; + border-bottom: 1px solid #999999; + -webkit-border-radius: 0; + text-align: right; +} + +fieldset > .row:last-child { + border-bottom: none !important; +} + +.row > input { + box-sizing: border-box; + -webkit-box-sizing: border-box; + margin: 0; + border: none; + padding: 12px 10px 0 110px; + height: 42px; + background: none; +} + +.row > label { + position: absolute; + margin: 0 0 0 14px; + line-height: 42px; + font-weight: bold; +} + +.row > table { + position: absolute; + margin: 0 0 0 14px; + height: 42px; +} + +.row > input { + box-sizing: border-box; + -webkit-box-sizing: border-box; + width: 100%; + margin: 0 0 0 0; + font-size: 10px; + font-weight: normal; +} + +.row > .toggle { + position: absolute; + top: 6px; + right: 6px; + width: 100px; + height: 28px; +} + +.toggle { + border: 1px solid #888888; + -webkit-border-radius: 6px; + background: #FFFFFF url(toggle.png) repeat-x; + font-size: 19px; + font-weight: bold; + line-height: 30px; +} + +.toggle[toggled="true"] { + border: 1px solid #143fae; + background: #194fdb url(toggleOn.png) repeat-x; +} + +.toggleOn { + display: none; + position: absolute; + width: 60px; + text-align: center; + left: 0; + top: 0; + color: #FFFFFF; + text-shadow: rgba(0, 0, 0, 0.4) 0px -1px 0; +} + +.toggleOff { + position: absolute; + width: 60px; + text-align: center; + right: 0; + top: 0; + color: #666666; +} + +.toggle[toggled="true"] > .toggleOn { + display: block; +} + +.toggle[toggled="true"] > .toggleOff { + display: none; +} + +.thumb { + position: absolute; + top: -1px; + left: -1px; + width: 40px; + height: 28px; + border: 1px solid #888888; + -webkit-border-radius: 6px; + background: #ffffff url(thumb.png) repeat-x; +} + +.toggle[toggled="true"] > .thumb { + left: auto; + right: -1px; +} + +.panel > h2 { + margin: 0 0 8px 14px; + font-size: inherit; + font-weight: bold; + color: #4d4d70; + text-shadow: rgba(255, 255, 255, 0.75) 2px 2px 0; +} + +/************************************************************************************************/ + +#preloader { + display: none; + background-image: url(loading.gif), url(selection.png), + url(blueButton.png), url(listArrowSel.png), url(listGroup.png); +} diff --git a/fhem/webfrontend/pgm5/icons/iui.js b/fhem/webfrontend/pgm5/icons/iui.js new file mode 100644 index 000000000..27de42806 --- /dev/null +++ b/fhem/webfrontend/pgm5/icons/iui.js @@ -0,0 +1,383 @@ +/* + Copyright (c) 2007, iUI Project Members + See LICENSE.txt for licensing terms + */ + + +(function() { + +var slideSpeed = 20; +var slideInterval = 0; + +var currentPage = null; +var currentDialog = null; +var currentWidth = 0; +var currentHash = location.hash; +var hashPrefix = "#_"; +var pageHistory = []; +var newPageCount = 0; +var checkTimer; + +// ************************************************************************************************* + +window.iui = +{ + showPage: function(page, backwards) + { + if (page) + { + if (currentDialog) + { + currentDialog.removeAttribute("selected"); + currentDialog = null; + } + + if (hasClass(page, "dialog")) + showDialog(page); + else + { + var fromPage = currentPage; + currentPage = page; + + if (fromPage) + setTimeout(slidePages, 0, fromPage, page, backwards); + else + updatePage(page, fromPage); + } + } + }, + + showPageById: function(pageId) + { + var page = $(pageId); + if (page) + { + var index = pageHistory.indexOf(pageId); + var backwards = index != -1; + if (backwards) + pageHistory.splice(index, pageHistory.length); + + iui.showPage(page, backwards); + } + }, + + showPageByHref: function(href, args, method, replace, cb) + { + var req = new XMLHttpRequest(); + req.onerror = function() + { + if (cb) + cb(false); + }; + + req.onreadystatechange = function() + { + if (req.readyState == 4) + { + if (replace) + replaceElementWithSource(replace, req.responseText); + else + { + var frag = document.createElement("div"); + frag.innerHTML = req.responseText; + iui.insertPages(frag.childNodes); + } + if (cb) + setTimeout(cb, 1000, true); + } + }; + + if (args) + { + req.open(method || "GET", href, true); + req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + req.setRequestHeader("Content-Length", args.length); + req.send(args.join("&")); + } + else + { + req.open(method || "GET", href, true); + req.send(null); + } + }, + + insertPages: function(nodes) + { + var targetPage; + for (var i = 0; i < nodes.length; ++i) + { + var child = nodes[i]; + if (child.nodeType == 1) + { + if (!child.id) + child.id = "__" + (++newPageCount) + "__"; + + var clone = $(child.id); + if (clone) + clone.parentNode.replaceChild(child, clone); + else + document.body.appendChild(child); + + if (child.getAttribute("selected") == "true" || !targetPage) + targetPage = child; + + --i; + } + } + + if (targetPage) + iui.showPage(targetPage); + }, + + getSelectedPage: function() + { + for (var child = document.body.firstChild; child; child = child.nextSibling) + { + if (child.nodeType == 1 && child.getAttribute("selected") == "true") + return child; + } + } +}; + +// ************************************************************************************************* + +addEventListener("load", function(event) +{ + var page = iui.getSelectedPage(); + if (page) + iui.showPage(page); + + setTimeout(preloadImages, 0); + setTimeout(checkOrientAndLocation, 0); + checkTimer = setInterval(checkOrientAndLocation, 300); +}, false); + +addEventListener("click", function(event) +{ + var link = findParent(event.target, "a"); + if (link) + { + function unselect() { link.removeAttribute("selected"); } + + if (link.href && link.hash && link.hash != "#") + { + link.setAttribute("selected", "true"); + iui.showPage($(link.hash.substr(1))); + setTimeout(unselect, 500); + } + else if (link == $("backButton")) + history.back(); + else if (link.getAttribute("type") == "submit") + submitForm(findParent(link, "form")); + else if (link.getAttribute("type") == "cancel") + cancelDialog(findParent(link, "form")); + else if (link.target == "_replace") + { + link.setAttribute("selected", "progress"); + iui.showPageByHref(link.href, null, null, link, unselect); + } + else if (!link.target) + { + link.setAttribute("selected", "progress"); + iui.showPageByHref(link.href, null, null, null, unselect); + } + else + return; + + event.preventDefault(); + } +}, true); + +addEventListener("click", function(event) +{ + var div = findParent(event.target, "div"); + if (div && hasClass(div, "toggle")) + { + div.setAttribute("toggled", div.getAttribute("toggled") != "true"); + event.preventDefault(); + } +}, true); + +function checkOrientAndLocation() +{ + if (window.innerWidth != currentWidth) + { + currentWidth = window.innerWidth; + var orient = currentWidth == 320 ? "profile" : "landscape"; + document.body.setAttribute("orient", orient); + setTimeout(scrollTo, 100, 0, 1); + } + + if (location.hash != currentHash) + { + var pageId = location.hash.substr(hashPrefix.length) + iui.showPageById(pageId); + } +} + +function showDialog(page) +{ + currentDialog = page; + page.setAttribute("selected", "true"); + + if (hasClass(page, "dialog") && !page.target) + showForm(page); +} + +function showForm(form) +{ + form.onsubmit = function(event) + { + event.preventDefault(); + submitForm(form); + }; + + form.onclick = function(event) + { + if (event.target == form && hasClass(form, "dialog")) + cancelDialog(form); + }; +} + +function cancelDialog(form) +{ + form.removeAttribute("selected"); +} + +function updatePage(page, fromPage) +{ + if (!page.id) + page.id = "__" + (++newPageCount) + "__"; + + location.href = currentHash = hashPrefix + page.id; + pageHistory.push(page.id); + + var pageTitle = $("pageTitle"); + if (page.title) + pageTitle.innerHTML = page.title; + + if (page.localName.toLowerCase() == "form" && !page.target) + showForm(page); + + var backButton = $("backButton"); + if (backButton) + { + var prevPage = $(pageHistory[pageHistory.length-2]); + if (prevPage && !page.getAttribute("hideBackButton")) + { + backButton.style.display = "inline"; + backButton.innerHTML = prevPage.title ? prevPage.title : "Back"; + } + else + backButton.style.display = "none"; + } +} + +function slidePages(fromPage, toPage, backwards) +{ + var axis = (backwards ? fromPage : toPage).getAttribute("axis"); + if (axis == "y") + (backwards ? fromPage : toPage).style.top = "100%"; + else + toPage.style.left = "100%"; + + toPage.setAttribute("selected", "true"); + scrollTo(0, 1); + clearInterval(checkTimer); + + var percent = 100; + slide(); + var timer = setInterval(slide, slideInterval); + + function slide() + { + percent -= slideSpeed; + if (percent <= 0) + { + percent = 0; + if (!hasClass(toPage, "dialog")) + fromPage.removeAttribute("selected"); + clearInterval(timer); + checkTimer = setInterval(checkOrientAndLocation, 300); + setTimeout(updatePage, 0, toPage, fromPage); + } + + if (axis == "y") + { + backwards + ? fromPage.style.top = (100-percent) + "%" + : toPage.style.top = percent + "%"; + } + else + { + fromPage.style.left = (backwards ? (100-percent) : (percent-100)) + "%"; + toPage.style.left = (backwards ? -percent : percent) + "%"; + } + } +} + +function preloadImages() +{ + var preloader = document.createElement("div"); + preloader.id = "preloader"; + document.body.appendChild(preloader); +} + +function submitForm(form) +{ + iui.showPageByHref(form.action || "POST", encodeForm(form), form.method); +} + +function encodeForm(form) +{ + function encode(inputs) + { + for (var i = 0; i < inputs.length; ++i) + { + if (inputs[i].name) + args.push(inputs[i].name + "=" + escape(inputs[i].value)); + } + } + + var args = []; + encode(form.getElementsByTagName("input")); + encode(form.getElementsByTagName("select")); + return args; +} + +function findParent(node, localName) +{ + while (node && (node.nodeType != 1 || node.localName.toLowerCase() != localName)) + node = node.parentNode; + return node; +} + +function hasClass(self, name) +{ + var re = new RegExp("(^|\\s)"+name+"($|\\s)"); + return re.exec(self.getAttribute("class")) != null; +} + +function replaceElementWithSource(replace, source) +{ + var page = replace.parentNode; + var parent = replace; + while (page.parentNode != document.body) + { + page = page.parentNode; + parent = parent.parentNode; + } + + var frag = document.createElement(parent.localName); + frag.innerHTML = source; + + page.removeChild(parent); + + while (frag.firstChild) + page.appendChild(frag.firstChild); +} + +function $(id) { return document.getElementById(id); } +function ddd() { console.log.apply(console, arguments); } + +})(); diff --git a/fhem/webfrontend/pgm5/icons/listArrow.png b/fhem/webfrontend/pgm5/icons/listArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..6421a16762c0f843bf342a1d01090ce8b77256fa GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoN!3HEN%BSrG36!`-lmzFem6RtIr7}3CCgZF_RMP;g_&g!eAw8+>M~{e^us?&19oxF zesu1f-zQziO$!7&@4IwANODeU)>*x*Rn^qtPS=tAh0|PL2=Z@Q!F+ZRSJaYqUMAhQ zG`Dhu3(aFX@_xb(O9venU+*Q2KNu!0^l!Yucs8g@r!jItQ`$kM_RelF{r G5}E+j-eN=m literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/listArrowSel.png b/fhem/webfrontend/pgm5/icons/listArrowSel.png new file mode 100644 index 0000000000000000000000000000000000000000..86832ebc7b961840d95938a98a0905cb09bc84a9 GIT binary patch literal 308 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 Is33zi04%B!Q2+n{ literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/pinstripes.png b/fhem/webfrontend/pgm5/icons/pinstripes.png new file mode 100644 index 0000000000000000000000000000000000000000..c99777512fb135af7dcdf4130e5770072cb39241 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)O!2~4dW-8AFQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jio`u#978H@CD~Lx<6>s|lboE)9K^uL#$fKtT%LaBX8=$M NgQu&X%Q~loCIB>$9?k#& literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/selection.png b/fhem/webfrontend/pgm5/icons/selection.png new file mode 100644 index 0000000000000000000000000000000000000000..537e3f0b13ea422a5ac5487317872414d51574c1 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JiX1&%978x{Sr2X$Jj5WtvT?p{@1r}0Gc6VGKL6Qv;l%Dc zTgw%X}gxBOXmxAbz1ds%&OUSwBY#<9&n!x%hW{an^L HB{Ts5+CMp^ literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/thumb.png b/fhem/webfrontend/pgm5/icons/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..cefa8fc5e9db724b96d36a8df814ecdf85d05582 GIT binary patch literal 2835 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 literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/toggle.png b/fhem/webfrontend/pgm5/icons/toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..3b62ebf26eaf8b21e341d413a6bd7cebcf5e7075 GIT binary patch literal 2815 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 literal 0 HcmV?d00001 diff --git a/fhem/webfrontend/pgm5/icons/whiteButton.png b/fhem/webfrontend/pgm5/icons/whiteButton.png new file mode 100644 index 0000000000000000000000000000000000000000..5514b270053a41039e9e96ef8591796521ee0312 GIT binary patch literal 978 zcmV;@11}R*8aG1*L?d1O->_T@+D~u1Y}!yL0VI=|WL) zUB!jV?nDqFVEqcjNU7Q;7-O1A(q?8dlX-96yZ5-5q-Ly5=jDGlkNe*5{m(h~zIU$B ztvpZh0>hld-kwYNkqy3Ko}K-V%D(G)Z`(9CN35XDjL&xuH$Mh_XF(}UB(Tw%lJL(m|<1LC)=R9eoc+{eJ zNQwFm3ZOo%tAYib`a=Ya6#-N=4JiO(J0G^!!{zGLYx*)_QJi=^gsb6jQm_J=B!6wD zD%0-bC{8`;0(Q9FGD%w0qhLC5rQirtj;nGAUq!X!Dqd2rt6&lNPuJ>g7pqvn zrLC}dJQx&>lhnC-)EE`H)reg0T)izKV$|3W#Z{x-uG+(BCOFiFCeuK~FK_qmdcCS1 z>8qPuMY7^&mwW$}US2j%`m-B`MG8c7zyGk_Te#n^&tIcWp2Z*u%VSYOaGhsH3-(@~ zmGI-aXiXaBcJ0@|xe~1jq8+&sKXGKh9t>WX-&ip>B{%Tf)t#qUx1u_4ei+87UrHDfl6dXpw%k7!k~_oku06C3S8F^@(Qf&-L}LBUh<3`S518 zHslKoXby|rLrF-aMbk8j+ef37#cK7AgI*>xK3*KI+wDGQqnw)Edn!H~2Jv7#l+-p> zSL68q&WJ!JQ!W>a*5>p3XLojzq*mM7in|*4KU7;ZTp6~iyZ`_I07*qoM6N<$f;?{4 AHUIzs literal 0 HcmV?d00001