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 000000000..c4a2fc94f Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/FS20.off.gif differ diff --git a/fhem/webfrontend/pgm5/icons/FS20.on.gif b/fhem/webfrontend/pgm5/icons/FS20.on.gif new file mode 100644 index 000000000..b385876e6 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/FS20.on.gif differ diff --git a/fhem/webfrontend/pgm5/icons/backButton.png b/fhem/webfrontend/pgm5/icons/backButton.png new file mode 100644 index 000000000..e27ea8cdf Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/backButton.png differ diff --git a/fhem/webfrontend/pgm5/icons/blueButton.png b/fhem/webfrontend/pgm5/icons/blueButton.png new file mode 100644 index 000000000..0f92dfd94 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/blueButton.png differ diff --git a/fhem/webfrontend/pgm5/icons/cancel.png b/fhem/webfrontend/pgm5/icons/cancel.png new file mode 100644 index 000000000..5f6dcc87d Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/cancel.png differ diff --git a/fhem/webfrontend/pgm5/icons/grayButton.png b/fhem/webfrontend/pgm5/icons/grayButton.png new file mode 100644 index 000000000..0ce6a30d4 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/grayButton.png differ diff --git a/fhem/webfrontend/pgm5/icons/iui.css b/fhem/webfrontend/pgm5/icons/iui.css new file mode 100644 index 000000000..2a59b6c62 --- /dev/null +++ b/fhem/webfrontend/pgm5/icons/iui.css @@ -0,0 +1,387 @@ +/* iui.css (c) 2007 by iUI Project Members, see LICENSE.txt for license */ +body { + margin: 0; + font-family: Helvetica; + background: #FFFFFF; + color: #000000; + overflow-x: hidden; + -webkit-user-select: none; + -webkit-text-size-adjust: none; +} + +body > *: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 000000000..6421a1676 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/listArrow.png differ diff --git a/fhem/webfrontend/pgm5/icons/listArrowSel.png b/fhem/webfrontend/pgm5/icons/listArrowSel.png new file mode 100644 index 000000000..86832ebc7 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/listArrowSel.png differ diff --git a/fhem/webfrontend/pgm5/icons/listGroup.png b/fhem/webfrontend/pgm5/icons/listGroup.png new file mode 100644 index 000000000..221553ae9 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/listGroup.png differ diff --git a/fhem/webfrontend/pgm5/icons/loading.gif b/fhem/webfrontend/pgm5/icons/loading.gif new file mode 100644 index 000000000..8522ddf1a Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/loading.gif differ diff --git a/fhem/webfrontend/pgm5/icons/pinstripes.png b/fhem/webfrontend/pgm5/icons/pinstripes.png new file mode 100644 index 000000000..c99777512 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/pinstripes.png differ diff --git a/fhem/webfrontend/pgm5/icons/selection.png b/fhem/webfrontend/pgm5/icons/selection.png new file mode 100644 index 000000000..537e3f0b1 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/selection.png differ diff --git a/fhem/webfrontend/pgm5/icons/thumb.png b/fhem/webfrontend/pgm5/icons/thumb.png new file mode 100644 index 000000000..cefa8fc5e Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/thumb.png differ diff --git a/fhem/webfrontend/pgm5/icons/toggle.png b/fhem/webfrontend/pgm5/icons/toggle.png new file mode 100644 index 000000000..3b62ebf26 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/toggle.png differ diff --git a/fhem/webfrontend/pgm5/icons/toggleOn.png b/fhem/webfrontend/pgm5/icons/toggleOn.png new file mode 100644 index 000000000..b016814de Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/toggleOn.png differ diff --git a/fhem/webfrontend/pgm5/icons/toolButton.png b/fhem/webfrontend/pgm5/icons/toolButton.png new file mode 100644 index 000000000..afe4d7a3e Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/toolButton.png differ diff --git a/fhem/webfrontend/pgm5/icons/toolbar.png b/fhem/webfrontend/pgm5/icons/toolbar.png new file mode 100644 index 000000000..3dde94c07 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/toolbar.png differ diff --git a/fhem/webfrontend/pgm5/icons/whiteButton.png b/fhem/webfrontend/pgm5/icons/whiteButton.png new file mode 100644 index 000000000..5514b2700 Binary files /dev/null and b/fhem/webfrontend/pgm5/icons/whiteButton.png differ