From 63e5b10cf163e42a9a67718b0469f330305f7d08 Mon Sep 17 00:00:00 2001 From: johannnes <> Date: Fri, 1 Mar 2013 16:52:00 +0000 Subject: [PATCH] extended charts to support more axes better errorhandling - messages using sourceforge as thirdparty repository for update git-svn-id: https://svn.fhem.de/fhem/trunk@2836 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/www/frontend/FHEM/93_DbLog.pm | 1323 +++++++++++++++++ fhem/www/frontend/controls_frontend.txt | 435 ++++++ .../app/controller/ChartController.js | 716 ++++++++- .../frontend/app/controller/MainController.js | 5 +- .../www/frontend/app/model/ChartModel.js | 19 +- .../frontend/app/model/SavedChartsModel.js | 8 +- .../www/frontend/app/store/TableDataStore.js | 4 +- .../www/frontend/app/view/LineChartPanel.js | 492 +++++- .../www/frontend/app/view/LineChartView.js | 71 +- .../www/frontend/app/view/Viewport.js | 2 +- 10 files changed, 2879 insertions(+), 196 deletions(-) create mode 100644 fhem/www/frontend/FHEM/93_DbLog.pm diff --git a/fhem/www/frontend/FHEM/93_DbLog.pm b/fhem/www/frontend/FHEM/93_DbLog.pm new file mode 100644 index 000000000..8ccddc631 --- /dev/null +++ b/fhem/www/frontend/FHEM/93_DbLog.pm @@ -0,0 +1,1323 @@ + +############################################## +# $Id$ +# +# 93_DbLog.pm +# written by Dr. Boris Neubert 2007-12-30 +# e-mail: omega at online dot de +# +# modified by Tobias Faust 2012-06-26 +# e-mail: tobias dot faust at online dot de +# +############################################## + +package main; +use strict; +use warnings; +use DBI; +use Data::Dumper; + +sub DbLog($$$); + +################################################################ +sub +DbLog_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "DbLog_Define"; + $hash->{UndefFn} = "DbLog_Undef"; + $hash->{NotifyFn} = "DbLog_Log"; + $hash->{GetFn} = "DbLog_Get"; + $hash->{AttrFn} = "DbLog_Attr"; + $hash->{AttrList} = "disable:0,1 loglevel:0,5"; + +} + +############################################################### +sub +DbLog_Define($@) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define DbLog configuration regexp" + if(int(@a) != 4); + + my $regexp = $a[3]; + + eval { "Hallo" =~ m/^$regexp$/ }; + return "Bad regexp: $@" if($@); + $hash->{REGEXP} = $regexp; + + $hash->{CONFIGURATION}= $a[2]; + + return "Can't connect to database." if(!DbLog_Connect($hash)); + + $hash->{STATE} = "active"; + + return undef; +} + +##################################### +sub +DbLog_Undef($$) +{ + my ($hash, $name) = @_; + my $dbh= $hash->{DBH}; + $dbh->disconnect() if(defined($dbh)); + return undef; +} + +################################################################ +# +# Wird bei jeder Aenderung eines Attributes dieser +# DbLog-Instanz aufgerufen +# +################################################################ +sub +DbLog_Attr(@) +{ + my @a = @_; + my $do = 0; + + if($a[0] eq "set" && $a[2] eq "disable") { + $do = (!defined($a[3]) || $a[3]) ? 1 : 2; + } + $do = 2 if($a[0] eq "del" && (!$a[2] || $a[2] eq "disable")); + return if(!$do); + + $defs{$a[1]}{STATE} = ($do == 1 ? "disabled" : "active"); + + return undef; +} + +################################################################ +# +# Parsefunktion, abhaengig vom Devicetyp +# +################################################################ +sub +DbLog_ParseEvent($$) +{ + my ($type, $event)= @_; + my @result; + + # split the event into reading and argument + # "day-temp: 22.0 (Celsius)" -> "day-temp", "22.0 (Celsius)" + my @parts = split(/: /,$event); + my $reading = shift @parts; + my $value = join(": ", @parts); + my $unit = ""; + + #default + if(!defined($reading)) { $reading = ""; } + if(!defined($value)) { $value = ""; } + + # the interpretation of the argument depends on the device type + # EMEM, M232Counter, M232Voltage return plain numbers + if(($type eq "M232Voltage") || + ($type eq "M232Counter") || + ($type eq "EMEM")) { + } + + # Onewire + elsif(($type eq "OWAD") || + ($type eq "OWSWITCH") || + ($type eq "OWMULTI")) { + $reading = "data"; + $value = $event; + } + # FS20 + elsif(($type eq "FS20") || + ($type eq "X10")) { + #@parts = split(/ /,$event); + #$reading = shift @parts; + #$value = join(" ", shift @parts); + + if($reading =~ m/^dim(\d+).*/o) { + $value = $1; + $reading= "dim"; + $unit= "%"; + } + if(!defined($value) || $value eq "") {$value=$reading; $reading="data";} + } + # FHT + elsif($type eq "FHT") { + if($reading =~ m(-from[12]\ ) || $reading =~ m(-to[12]\ )) { + @parts= split(/ /,$event); + $reading= $parts[0]; + $value= $parts[1]; + $unit= ""; + } + if($reading =~ m(-temp)) { $value=~ s/ \(Celsius\)//; $unit= "°C"; } + if($reading =~ m(temp-offset)) { $value=~ s/ \(Celsius\)//; $unit= "°C"; } + if($reading =~ m(^actuator[0-9]*)) { + if($value eq "lime-protection") { + $reading= "actuator-lime-protection"; + undef $value; + } + elsif($value =~ m(^offset:)) { + $reading= "actuator-offset"; + @parts= split(/: /,$value); + $value= $parts[1]; + if(defined $value) { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } + } + elsif($value =~ m(^unknown_)) { + @parts= split(/: /,$value); + $reading= "actuator-" . $parts[0]; + $value= $parts[1]; + if(defined $value) { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } + } + elsif($value eq "synctime") { + $reading= "actuator-synctime"; + undef $value; + } + elsif($value eq "test") { + $reading= "actuator-test"; + undef $value; + } + elsif($value eq "pair") { + $reading= "actuator-pair"; + undef $value; + } + else { + $value=~ s/%//; $value= $value*1.; $unit= "%"; + } + } + } + # KS300 + elsif($type eq "KS300") { + if($event =~ m(T:.*)) { $reading= "data"; $value= $event; } + if($event =~ m(avg_day)) { $reading= "data"; $value= $event; } + if($event =~ m(avg_month)) { $reading= "data"; $value= $event; } + if($reading eq "temperature") { $value=~ s/ \(Celsius\)//; $unit= "°C"; } + if($reading eq "wind") { $value=~ s/ \(km\/h\)//; $unit= "km/h"; } + if($reading eq "rain") { $value=~ s/ \(l\/m2\)//; $unit= "l/m2"; } + if($reading eq "rain_raw") { $value=~ s/ \(counter\)//; $unit= ""; } + if($reading eq "humidity") { $value=~ s/ \(\%\)//; $unit= "%"; } + if($reading eq "israining") { + $value=~ s/ \(yes\/no\)//; + $value=~ s/no/0/; + $value=~ s/yes/1/; + } + } + # HMS + elsif($type eq "HMS" || + $type eq "CUL_WS" || + $type eq "OWTHERM") { + if($event =~ m(T:.*)) { $reading= "data"; $value= $event; } + if($reading eq "temperature") { $value=~ s/ \(Celsius\)//; $unit= "°C"; } + if($reading eq "temperature") { $value=~ s/([-\.\d]+).*/$1/; $unit= "°C"; } #OWTHERM + if($reading eq "humidity") { $value=~ s/ \(\%\)//; $unit= "%"; } + if($reading eq "battery") { + $value=~ s/ok/1/; + $value=~ s/replaced/1/; + $value=~ s/empty/0/; + } + } + + # BS + elsif($type eq "BS") { + if($event =~ m(brightness:.*)) { + @parts= split(/ /,$event); + $reading= "lux"; + $value= $parts[4]*1.; + $unit= "lux"; + } + } + + # Weather + elsif($type eq "WEATHER") { + if($event =~ m(^wind_condition)) { + @parts= split(/ /,$event); # extract wind direction from event + if(defined $parts[0]) { + $reading = "wind_direction"; + $value= $parts[2]; +# $unit= ""; + } + } + if($reading =~ m(^wind)) { $unit= "km/h"; } # wind, wind_speed + if($reading eq "wind_chill") { $unit= "°C"; } + if($reading eq "wind_direction") { $unit= ""; } + if($reading =~ m(^temperature)) { $unit= "°C"; } # wenn reading mit temperature beginnt + if($reading =~ m(^humidity)) { $unit= "%"; } + if($reading =~ m(^pressure)) { $unit= "hPa"; } + if($reading =~ m(^pressure_trend)) { $unit= ""; } + } + + @result= ($reading,$value,$unit); + return @result; +} + + +################################################################ +# +# Hauptroutine zum Loggen. Wird bei jedem Eventchange +# aufgerufen +# +################################################################ +sub +DbLog_Log($$) +{ + # Log is my entry, Dev is the entry of the changed device + my ($log, $dev) = @_; + + return undef if($log->{STATE} eq "disabled"); + + # name and type required for parsing + my $n= $dev->{NAME}; + my $t= uc($dev->{TYPE}); + + # timestamp in SQL format YYYY-MM-DD hh:mm:ss + #my ($sec,$min,$hr,$day,$mon,$yr,$wday,$yday,$isdst)= localtime(time); + #my $ts= sprintf("%04d-%02d-%02d %02d:%02d:%02d", $yr+1900,$mon+1,$day,$hr,$min,$sec); + + my $re = $log->{REGEXP}; + my $max = int(@{$dev->{CHANGED}}); + for (my $i = 0; $i < $max; $i++) { + my $s = $dev->{CHANGED}[$i]; + $s = "" if(!defined($s)); + if($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/) { + my $ts = TimeNow(); + $ts = $dev->{CHANGETIME}[$i] if(defined($dev->{CHANGETIME}[$i])); + # $ts is in SQL format YYYY-MM-DD hh:mm:ss + + my @r= DbLog_ParseEvent($t, $s); + my $reading= $r[0]; + my $value= $r[1]; + my $unit= $r[2]; + if(!defined $reading) { $reading= ""; } + if(!defined $value) { $value= ""; } + if(!defined $unit || $unit eq "") { + $unit = AttrVal("$n", "unit", ""); + } + + my $is= "(TIMESTAMP, DEVICE, TYPE, EVENT, READING, VALUE, UNIT) VALUES " . + "('$ts', '$n', '$t', '$s', '$reading', '$value', '$unit')"; + DbLog_ExecSQL($log, "INSERT INTO history" . $is); + DbLog_ExecSQL($log, "DELETE FROM current WHERE (DEVICE='$n') AND (READING='$reading')"); + DbLog_ExecSQL($log, "INSERT INTO current" . $is); + + + + } + } + + return ""; +} + +################################################################ +# +# zerlegt uebergebenes FHEM-Datum in die einzelnen Bestandteile +# und fuegt noch Defaultwerte ein +# uebergebenes SQL-Format: YYYY-MM-DD HH24:MI:SS +# +################################################################ +sub +DbLog_explode_datetime($%) { + my ($t, %def) = @_; + my %retv; + + my (@datetime, @date, @time); + @datetime = split(" ", $t); #Datum und Zeit auftrennen + @date = split("-", $datetime[0]); + @time = split(":", $datetime[1]) if ($datetime[1]); + if ($date[0]) {$retv{year} = $date[0];} else {$retv{year} = $def{year};} + if ($date[1]) {$retv{month} = $date[1];} else {$retv{month} = $def{month};} + if ($date[2]) {$retv{day} = $date[2];} else {$retv{day} = $def{day};} + if ($time[0]) {$retv{hour} = $time[0];} else {$retv{hour} = $def{hour};} + if ($time[1]) {$retv{minute}= $time[1];} else {$retv{minute}= $def{minute};} + if ($time[2]) {$retv{second}= $time[2];} else {$retv{second}= $def{second};} + + $retv{datetime}=DbLog_implode_datetime($retv{year}, $retv{month}, $retv{day}, $retv{hour}, $retv{minute}, $retv{second}); + + #Log 1, Dumper(%retv); + return %retv +} + +sub +DbLog_implode_datetime($$$$$$) { + my ($year, $month, $day, $hour, $minute, $second) = @_; + my $retv = $year."-".$month."-".$day." ".$hour.":".$minute.":".$second; + + return $retv; +} +################################################################ +# +# Verbindung zur DB aufbauen +# +################################################################ +sub +DbLog_Connect($) +{ + my ($hash)= @_; + + my $configfilename= $hash->{CONFIGURATION}; + if(!open(CONFIG, $configfilename)) { + Log 1, "Cannot open database configuration file $configfilename."; + return 0; } + my @config=; + close(CONFIG); + + my %dbconfig; + eval join("", @config); + + my $dbconn= $dbconfig{connection}; + my $dbuser= $dbconfig{user}; + my $dbpassword= $dbconfig{password}; + + #check the database model + if($dbconn =~ m/pg:/i) { + $hash->{DBMODEL}="POSTGRESQL"; + } elsif ($dbconn =~ m/mysql:/i) { + $hash->{DBMODEL}="MYSQL"; + } elsif ($dbconn =~ m/oracle:/i) { + $hash->{DBMODEL}="ORACLE"; + } elsif ($dbconn =~ m/sqlite:/i) { + $hash->{DBMODEL}="SQLITE"; + } else { + $hash->{DBMODEL}="unknown"; + Log 3, "Unknown dbmodel type in configuration file $configfilename."; + Log 3, "Only Mysql, Postgresql, Oracle, SQLite are fully supported."; + Log 3, "It may cause SQL-Erros during generating plots."; + } + + Log 3, "Connecting to database $dbconn with user $dbuser"; + my $dbh = DBI->connect_cached("dbi:$dbconn", $dbuser, $dbpassword); + if(!$dbh) { + Log 2, "Can't connect to $dbconn: $DBI::errstr"; + return 0; + } + Log 3, "Connection to db $dbconn established"; + $hash->{DBH}= $dbh; + + # creating an own connection for the webfrontend, saved as DBHF in Hash + # this makes sure that the connection doesnt get lost due to other modules + my $dbhf = DBI->connect_cached("dbi:$dbconn", $dbuser, $dbpassword); + if(!$dbhf) { + Log 2, "Can't connect to $dbconn: $DBI::errstr"; + return 0; + } + Log 3, "Connection to db $dbconn established"; + $hash->{DBHF}= $dbhf; + + return 1; +} + +################################################################ +# +# Prozeduren zum Ausfuehren des SQLs +# +################################################################ +sub +DbLog_ExecSQL1($$) +{ + my ($dbh,$sql)= @_; + + my $sth = $dbh->do($sql); + if(!$sth) { + Log 2, "DBLog error: " . $DBI::errstr; + return 0; + } + return $sth; +} + +sub +DbLog_ExecSQL($$) +{ + my ($hash,$sql)= @_; + Log GetLogLevel($hash->{NAME},5), "Executing $sql"; + my $dbh= $hash->{DBH}; + my $sth = DbLog_ExecSQL1($dbh,$sql); + if(!$sth) { + #retry + $dbh->disconnect(); + if(!DbLog_Connect($hash)) { + Log 2, "DBLog reconnect failed."; + return 0; + } + $dbh= $hash->{DBH}; + $sth = DbLog_ExecSQL1($dbh,$sql); + if(!$sth) { + Log 2, "DBLog retry failed."; + return 0; + } + Log 2, "DBLog retry ok."; + } + return $sth; +} + +################################################################ +# +# GET Funktion +# wird zb. zur Generierung der Plots implizit aufgerufen +# +################################################################ +sub +DbLog_Get($@) +{ + my ($hash, @a) = @_; + + return "Usage: get $a[0] ...\n". + " where column_spec is :::\n" . + " see the #DbLog entries in the .gplot files\n" . + " is not used, only for compatibility for FileLog, please use - \n" . + " is a prefix, - means stdout\n" + if(int(@a) < 5); + shift @a; + my $inf = shift @a; + my $outf = shift @a; + my $from = shift @a; + my $to = shift @a; # Now @a contains the list of column_specs + my ($internal, @fld); + + if(uc($outf) eq "INT") { + $outf = "-"; + $internal = 1; + } elsif (uc($outf) eq "WEBCHART") { + # redirect the get request to the chartQuery function + return chartQuery($hash, @_); + } + + my @readings = (); + my (%sqlspec, %from_datetime, %to_datetime); + + #uebergebenen Timestamp anpassen + #moegliche Formate: YYYY | YYYY-MM | YYYY-MM-DD | YYYY-MM-DD_HH24 + $from =~ s/_/\ /g; + $to =~ s/_/\ /g; + %from_datetime = DbLog_explode_datetime($from, DbLog_explode_datetime("2000-01-01 00:00:00", ())); + %to_datetime = DbLog_explode_datetime($to, DbLog_explode_datetime("2099-01-01 00:00:00", ())); + $from = $from_datetime{datetime}; + $to = $to_datetime{datetime}; + + + my ($retval,$sql_timestamp,$sql_dev,$sql_reading,$sql_value, $type, $event, $unit) = ""; + my $writeout = 0; + my (@min, @max, @sum, @cnt, @lastv, @lastd); + my (%tstamp, %lasttstamp, $out_tstamp, $out_value, $minval, $maxval); #fuer delta-h/d Berechnung + + #extract the Device:Reading arguments into @readings array + for(my $i = 0; $i < int(@a); $i++) { + @fld = split(":", $a[$i], 5); + $readings[$i][0] = $fld[0]; # Device + $readings[$i][1] = $fld[1]; # Reading + $readings[$i][2] = $fld[2]; # Default + $readings[$i][3] = $fld[3]; # function + $readings[$i][4] = $fld[4]; # regexp + } + + my $dbh= $hash->{DBH}; + + #vorbereiten der DB-Abfrage, DB-Modell-abhaengig + if ($hash->{DBMODEL} eq "POSTGRESQL") { + $sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{to_timestamp} = "TO_TIMESTAMP('$to', 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{reading_clause} = "(DEVICE || '|' || READING)"; + } elsif ($hash->{DBMODEL} eq "ORACLE") { + $sqlspec{get_timestamp} = "TO_CHAR(TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{from_timestamp} = "TO_TIMESTAMP('$from', 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{to_timestamp} = "TO_TIMESTAMP('$to', 'YYYY-MM-DD HH24:MI:SS')"; + $sqlspec{reading_clause} = "(DEVICE || '|' || READING)"; + } elsif ($hash->{DBMODEL} eq "MYSQL") { + $sqlspec{get_timestamp} = "DATE_FORMAT(TIMESTAMP, '%Y-%m-%d %H:%i:%s')"; + $sqlspec{from_timestamp} = "STR_TO_DATE('$from', '%Y-%m-%d %H:%i:%s')"; + $sqlspec{to_timestamp} = "STR_TO_DATE('$to', '%Y-%m-%d %H:%i:%s')"; + $sqlspec{reading_clause} = "CONCAT(DEVICE,'|',READING)" + } elsif ($hash->{DBMODEL} eq "SQLITE") { + $sqlspec{get_timestamp} = "TIMESTAMP"; + $sqlspec{from_timestamp} = "'$from'"; + $sqlspec{to_timestamp} = "'$to'"; + $sqlspec{reading_clause} = "(DEVICE || '|' || READING)"; + } else { + $sqlspec{get_timestamp} = "TIMESTAMP"; + $sqlspec{from_timestamp} = "'$from'"; + $sqlspec{to_timestamp} = "'$to'"; + $sqlspec{reading_clause} = "(DEVICE || '|' || READING)"; + } + + if(uc($outf) eq "ALL") { + $sqlspec{all} = ",TYPE,EVENT,UNIT"; + } else { + $sqlspec{all} = ""; + } + + for(my $i=0; $i[0]."|".$readings[$i]->[1]."') + AND DEVICE = '".$readings[$i]->[0]."' + AND READING = '".$readings[$i]->[1]."' + AND TIMESTAMP > $sqlspec{from_timestamp} + AND TIMESTAMP < $sqlspec{to_timestamp} + ORDER BY TIMESTAMP"; + + Log GetLogLevel($hash->{NAME},5), "Executing $stm"; + + my $sth= $dbh->prepare($stm) || + return "Cannot prepare statement $stm: $DBI::errstr"; + my $rc= $sth->execute() || + return "Cannot execute statement $stm: $DBI::errstr"; + + if(uc($outf) eq "ALL") { + $retval .= "Timestamp: Device, Type, Event, Reading, Value, Unit\n"; + $retval .= "=====================================================\n"; + } + while( ($sql_timestamp,$sql_dev,$sql_reading,$sql_value, $type, $event, $unit)= $sth->fetchrow_array) { + $writeout = 0; + $out_value = ""; + $out_tstamp = ""; + + ############ Auswerten des 5. Parameters: Regexp ################### + if($readings[$i]->[4] && $readings[$i]->[4]) { + #evaluate + my $val = $sql_value; + eval("$readings[$i]->[4]"); + $sql_value = $val; + if($@) {Log 3, "DbLog: Error in inline function: <".$readings[$i]->[4].">, Fehler: $@";} + $out_tstamp = $sql_timestamp; + $writeout=1; + } + + ############ Auswerten des 4. Parameters: function ################### + if($readings[$i]->[3] && $readings[$i]->[3] eq "int") { + #nur den integerwert uebernehmen falls zb value=15°C + $out_value = $1 if($sql_value =~ m/^(\d+).*/o); + $out_tstamp = $sql_timestamp; + $writeout=1; + + } elsif ($readings[$i]->[3] && $readings[$i]->[3] =~ m/^int(\d+).*/o) { + #Übernehme den Dezimalwert mit den angegebenen Stellen an Nachkommastellen + $out_value = $1 if($sql_value =~ m/^([-\.\d]+).*/o); + $out_tstamp = $sql_timestamp; + $writeout=1; + + } elsif ($readings[$i]->[3] && $readings[$i]->[3] eq "delta-h") { + #Berechnung eines Stundenwertes + %tstamp = DbLog_explode_datetime($sql_timestamp, ()); + if($lastd[$i] eq "undef") { + %lasttstamp = DbLog_explode_datetime($sql_timestamp, ()); + } else { + %lasttstamp = DbLog_explode_datetime($lastd[$i], ()); + } + if("$tstamp{hour}" ne "$lasttstamp{hour}") { + # Aenderung der stunde, Berechne Delta + $out_value = sprintf("%0.1f", $maxval - $minval); + $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00"); + $minval = 999999; + $maxval = -999999; + $writeout=1; + } + } elsif ($readings[$i]->[3] && $readings[$i]->[3] eq "delta-d") { + #Berechnung eines Tageswertes + %tstamp = DbLog_explode_datetime($sql_timestamp, ()); + if($lastd[$i] eq "undef") { + %lasttstamp = DbLog_explode_datetime($sql_timestamp, ()); + } else { + %lasttstamp = DbLog_explode_datetime($lastd[$i], ()); + } + if("$tstamp{day}" ne "$lasttstamp{day}") { + # Aenderung des Tages, Berechne Delta + $out_value = sprintf("%0.1f", $maxval - $minval); + $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, "00", "00", "00"); + $minval = 999999; + $maxval = -999999; + $writeout=1; + } + } else { + $out_value = $sql_value; + $out_tstamp = $sql_timestamp; + $writeout=1; + } + + ###################### Ausgabe ########################### + if($writeout) { + if(uc($outf) eq "ALL") { + $retval .= sprintf("%s: %s, %s, %s, %s, %s, %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); + } else { + $out_tstamp =~ s/\ /_/g; #needed by generating plots + $retval .= "$out_tstamp $out_value\n"; + } + } + + if(defined($sql_value) || $sql_value =~ m/^[-\.\d]+$/o){ + #nur setzen wenn nummerisch + $min[$i] = $sql_value if($sql_value < $min[$i]); + $max[$i] = $sql_value if($sql_value > $max[$i]);; + $sum[$i] += $sql_value; + $minval = $sql_value if($sql_value < $minval); + $maxval = $sql_value if($sql_value > $maxval); + } else { + $min[$i] = 0; + $max[$i] = 0; + $sum[$i] = 0; + $minval = 0; + $maxval = 0; + } + $cnt[$i]++; + $lastv[$i] = $sql_value; + $lastd[$i] = $sql_timestamp; + + } #while fetchrow + + ######## den letzten Abschlusssatz rausschreiben ########## + if($readings[$i]->[3] && ($readings[$i]->[3] eq "delta-h" || $readings[$i]->[3] eq "delta-d")) { + $out_value = sprintf("%0.1f", $maxval - $minval); + $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, $lasttstamp{hour}, "30", "00") if($readings[$i]->[3] eq "delta-h"); + $out_tstamp = DbLog_implode_datetime($lasttstamp{year}, $lasttstamp{month}, $lasttstamp{day}, "00", "00", "00") if($readings[$i]->[3] eq "delta-d"); + if(uc($outf) eq "ALL") { + $retval .= sprintf("%s: %s %s %s %s %s %s\n", $out_tstamp, $sql_dev, $type, $event, $sql_reading, $out_value, $unit); + } else { + $out_tstamp =~ s/\ /_/g; #needed by generating plots + $retval .= "$out_tstamp $out_value\n"; + } + } + # DatenTrenner setzen + $retval .= "#$readings[$i]->[0]"; + $retval .= ":"; + $retval .= "$readings[$i]->[1]" if($readings[$i]->[1]); + $retval .= ":"; + $retval .= "$readings[$i]->[2]" if($readings[$i]->[2]); + $retval .= ":"; + $retval .= "$readings[$i]->[3]" if($readings[$i]->[3]); + $retval .= ":"; + $retval .= "$readings[$i]->[4]" if($readings[$i]->[4]); + $retval .= "\n"; + } #for @readings + + #Ueberfuehren der gesammelten Werte in die globale Variable %data + for(my $j=0; $j{DBHF}; + + my $totalcount; + + if (defined $countsql && $countsql ne "") { + my $query_handle = $dbhf->prepare($countsql) + or return jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$countsql); + + $query_handle->execute() + or return jsonError("Could not execute statement: " . $query_handle->errstr); + + my @data = $query_handle->fetchrow_array(); + $totalcount = join(", ", @data); + + } + + # prepare the query + my $query_handle = $dbhf->prepare($sql) + or return jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$sql); + + # execute the query + $query_handle->execute() + or return jsonError("Could not execute statement: " . $query_handle->errstr); + + my $columns = $query_handle->{'NAME'}; + my $columncnt; + + # When columns are empty but execution was successful, we have done a successful INSERT, UPDATE or DELETE + if($columns) { + $columncnt = scalar @$columns; + } else { + return '{"success": "true", "msg":"All ok"}'; + } + + my $i = 0; + my $jsonstring = '{"data":['; + + while ( my @data = $query_handle->fetchrow_array()) { + + if($i == 0) { + $jsonstring .= '{'; + } else { + $jsonstring .= ',{'; + } + + for ($i = 0; $i < $columncnt; $i++) { + $jsonstring .= '"'; + $jsonstring .= uc($query_handle->{NAME}->[$i]); + $jsonstring .= '":'; + + if (defined $data[$i]) { + my $fragment = substr($data[$i],0,2); + if ($fragment eq "[{") { + $jsonstring .= $data[$i]; + } else { + $jsonstring .= '"'.$data[$i].'"'; + } + } else { + $jsonstring .= '""' + } + + if($i != ($columncnt -1)) { + $jsonstring .= ','; + } + } + $jsonstring .= '}'; + } + $jsonstring .= ']'; + if (defined $totalcount && $totalcount ne "") { + $jsonstring .= ',"totalCount": '.$totalcount.'}'; + } else { + $jsonstring .= '}'; + } + return $jsonstring; +} +################################################################ + + +# reload 93_DbLog.pm +# get DbLog_Bewaesserung - - 2012-06-22 2012-06-23 KS300:temperature:: KS300:humidity:: +# get DbLog - - 2012-11-10_10 2012-11-10_20 KS300:rain:0:delta-h +# http://tulpemd.dyndns.org/fhem?cmd=showlog weblink_Bodenfeuchte_1 DbLog_Bodenfeuchte myDbLogtest null +# +# FileLog +# get FileLog_KS300 KS300-2012-11.log - 2012-11-10 2012-11-22 10:IR\x3a:0:delta-d + +1; + +=pod +=begin html + + +

DbLog

+
    +
    + + + Define +
      + define <name> DbLog <configfilename> <regexp> +

      + + Log events to a database. The database connection is defined in + <configfilename> (see sample configuration file + contrib/dblog/db.conf). The configuration is stored in a separate file + to avoid storing the password in the main configuration file and to have it + visible in the output of the list command. +

      + + The modules DBI and DBD::<dbtype> + need to be installed (use cpan -i <module> + if your distribution does not have it). +

      + + <regexp> is the same as in FileLog. +

      + Sample code to create a MySQL/PostgreSQL database is in + <DBType>_create.sql. + The database contains two tables: current and + history. The latter contains all events whereas the former only + contains the last event for any given reading and device. + The columns have the following meaning: +
        + +
      1. TIMESTAMP: timestamp of event, e.g. 2007-12-30 21:45:22
      2. +
      3. DEVICE: device name, e.g. Wetterstation
      4. +
      5. TYPE: device type, e.g. KS300
      6. +
      7. EVENT: event specification as full string, + e.g. humidity: 71 (%)
      8. +
      9. READING: name of reading extracted from event, + e.g. humidity
      10. + +
      11. VALUE: actual reading extracted from event, + e.g. 71
      12. +
      13. UNIT: unit extracted from event, e.g. %
      14. +
      + The content of VALUE is optimized for automated post-processing, e.g. + yes is translated to 1 +

      + The current values can be retrieved by the following code like FileLog:
      +
        + get myDbLog - - 2012-11-10 2012-11-10 KS300:temperature:: +
      +

      + Examples: +
        + # log everything to database
        + + define myDbLog DbLog /etc/fhem/db.conf .*:.* +
      +
    + + + + Set
      N/A

    + + + Get +
      + get <name> <infile> <outfile> <from> + <to> <column_spec> +

      + Read data from the Database, used by frontends to plot data without direct + access to the Database.
      + +
        +
      • <in>
        + A dummy parameter for FileLog compatibility. Always set to -
      • +
      • <out>
        + A dummy parameter for FileLog compatibility. Set it to - + to check the output for plot-computing.
        Set it to the special keyword + all to get all columns from Database.
      • +
      • <from> / <to>
        + Used to select the data. Please use the following timeformat or + an initial substring of it:
        +
          YYYY-MM-DD_HH24:MI:SS
      • +
      • <column_spec>
        + For each column_spec return a set of data separated by + a comment line on the current connection.
        + Syntax: <device>:<reading>:<default>:<fn>:<regexp>
        +
          +
        • <device>
          + The name of the device. Case sensitive
        • +
        • <reading>
          + The reading of the given device to select. Case sensitive. +
        • +
        • <default>
          + no implemented yet +
        • +
        • <fn> + One of the following: +
            +
          • int
            + Extract the integer at the beginning of the string. Used e.g. + for constructs like 10%
          • +
          • int<digit>
            + Extract the decimal digits including negative character and + decimal point at the beginning og the string. Used e.g. + for constructs like 15.7°C
          • +
          • delta-h / delta-d
            + Return the delta of the values for a given hour or a given day. + Used if the column contains a counter, as is the case for the + KS300 rain column.
          • +
        • +
        • <regexp>
          + The string is evaluated as a perl expression. $val is the + current value returned from the Database. The regexp is executed + before <fn> parameter.
          + Note: The string/perl expression cannot contain spaces, + as the part after the space will be considered as the + next column_spec. +
        • +
      • +
      +

      + Examples: +
        +
      • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature
      • +
      • get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1
        + like from 10am until 08pm at 10.11.2012
      • +
      • get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg
        + return 1 for all occurance of on* (on|on-for-timer etc) and 0 for all off*
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
        + Example of OWAD: value like this: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
        + and output for port B is like this: 2012-11-20_10:23:54 66.647
      • +
      +

      +
    + + Get when used for webcharts +
      + get <name> <infile> <outfile> <from> + <to> <device> <querytype> <xaxis> <yaxis> <savename> +

      + Query the Database to retrieve JSON-Formatted Data, which is used by the charting frontend. +
      + +
        +
      • <name>
        + The name of the defined DbLog, like it is given in fhem.cfg. For functionality this has to be set to logdb.
      • +
      • <in>
        + A dummy parameter for FileLog compatibility. Always set to -
      • +
      • <out>
        + A dummy parameter for FileLog compatibility. Set it to webchart + to use the charting related get function. +
      • +
      • <from> / <to>
        + Used to select the data. Please use the following timeformat:
        +
          YYYY-MM-DD_HH24:MI:SS
      • +
      • <device>
        + A string which represents the device to query.
      • +
      • <querytype>
        + A string which represents the method the query should use. Actually supported values are:
        + getreadings to retrieve the possible readings for a given device
        + getdevices to retrieve all available devices
        + timerange to retrieve charting data, which requires a given xaxis, yaxis, device, to and from
        + savechart to save a chart configuration in the database. Requires a given xaxis, yaxis, device, to and from, and a 'savename' used to save the chart
        + deletechart to delete a saved chart. Requires a given 'savename' which was used to save the chart
        +
      • +
      • <xaxis>
        + A string which represents the xaxis
      • +
      • <yaxis>
        + A string which represents the yaxis
      • +
      • <savename>
        + A string which represents the name a chart will be saved with
      • + +
      +

      + Examples: +
        +
      • get logdb - webchart "" "" "" getcharts
        + Retrieves all saved charts from the Database
      • +
      • get logdb - webchart "" "" "" getdevices
        + Retrieves all available devices from the Database
      • +
      • get logdb - webchart "" "" ESA2000_LED_011e getreadings
        + Retrieves all available Readings for a given device from the Database
      • +
      • get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e timerange TIMESTAMP day_kwh
        + Retrieves charting data, which requires a given xaxis, yaxis, device, to and from
        + Will ouput a JSON like this: [{'TIMESTAMP':'2013-02-11 00:10:10','VALUE':'0.22431388090756'},{'TIMESTAMP'.....}]
      • +
      • get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e savechart TIMESTAMP day_kwh tageskwh
        + Will save a chart in the database with the given name and the chart configuration parameters
      • +
      • get logdb - webchart "" "" "" deletechart "" "" tageskwh
        + Will delete a chart from the database with the given name
      • +
      +

      +
    + + Attributes
      N/A

    +
+ +=end html +=begin html_DE + + +

DbLog

+
    +
    + + + Define +
      + define <name> DbLog <configfilename> <regexp> +

      + + Speichert Events in eine Datenbank. Die Datenbankverbindungsparameter werden + definiert in <configfilename>. (Vergleiche + Beipspielkonfigurationsdatei in contrib/dblog/db.conf).
      + Die Konfiguration ist in einer sparaten Datei abgelegt um das Datenbankpasswort + nicht in Klartext in der FHEM-Haupt-Konfigurationsdatei speichern zu müssen. + Ansonsten wäre es mittels des list + Befehls einfach auslesbar. +

      + + Die Perl-Module DBI and DBD::<dbtype> + müssen installiert werden (use cpan -i <module> + falls die eigene Distribution diese nicht schon mitbringt). +

      + + <regexp> ist identisch wie FileLog. +

      + Ein Beispielcode zum Erstellen einer MySQL/PostGreSQL Datenbak ist in + contrib/dblog/<DBType>_create.sql zu finden. + Die Datenbank beinhaltet 2 Tabellen: current und + history. Die Tabelle current enthält den letzten Stand + pro Device und Reading. In der Tabelle history sind alle + Events historisch gespeichert. + + Die Tabellenspalten haben folgende Bedeutung: +
        +
      1. TIMESTAMP: Zeitpunkt des Events, z.B. 2007-12-30 21:45:22
      2. +
      3. DEVICE: name des Devices, z.B. Wetterstation
      4. +
      5. TYPE: Type des Devices, z.B. KS300
      6. +
      7. EVENT: das auftretende Event als volle Zeichenkette + z.B. humidity: 71 (%)
      8. +
      9. READING: Name des Readings, ermittelt aus dem Event, + z.B. humidity
      10. + +
      11. VALUE: aktueller Wert des Readings, ermittelt aus dem Event, + z.B. 71
      12. +
      13. UNIT: Einheit, ermittelt aus dem Event, z.B. %
      14. +
      + Der Wert des Rreadings ist optimiert für eine automatisierte Nachverarbeitung + z.B. yes ist transformiert nach 1 +

      + Die gespeicherten Werte können mittels GET Funktion angezeigt werden: +
        + get myDbLog - - 2012-11-10 2012-11-10 KS300:temperature +
      +

      + Beispiel: +
        + Speichert alles in der Datenbank
        + + define myDbLog DbLog /etc/fhem/db.conf .*:.* +
      +
    + + + + Set
      N/A

    + + + Get +
      + get <name> <infile> <outfile> <from> + <to> <column_spec> +

      + Ließt Daten aus der Datenbank. Wird durch die Frontends benutzt um Plots + zu generieren ohne selbst auf die Datenank zugreifen zu müssen. +
      + +
        +
      • <in>
        + Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. + Dieser Parameter ist immer auf - zu setzen. +
      • +
      • <out>
        + Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. + Dieser Parameter ist immer auf - zu setzen um die + Ermittlung der Daten aus der Datenbank für die Plotgenerierung zu prüfen.
        + Durchd ie Angabe des Schlüsselworts all werden alle + Spalten der Datenbank ausgegeben. +
      • +
      • <from> / <to>
        + Wird benutzt um den Zeitraum der Daten einzugrenzen. Es ist das folgende + Zeitformat oder ein Teilstring davon zu benutzen:
        +
          YYYY-MM-DD_HH24:MI:SS
      • +
      • <column_spec>
        + Für jede column_spec Gruppe wird ein Datenset zurückgegeben welches + durch einen Kommentar getrennt wird. Dieser Kommentar repräsentiert + die column_spec.
        + Syntax: <device>:<reading>:<default>:<fn>:<regexp>
        +
          +
        • <device>
          + Der Name des Devices. Achtung: Groß/Kleinschreibung beachten!
        • +
        • <reading>
          + Das REading des angegebenen Devices zur Datenselektion. + Achtung: Groß/Kleinschreibung beachten! +
        • +
        • <default>
          + Zur Zeit noch nicht implementiert. +
        • +
        • <fn> + Angabe einer speziellen Funktion: +
            +
          • int
            + Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der + Spalte "VALUE". Benutzt z.B. für Ausprägungen wie 10%. +
          • +
          • int<digit>
            + Ermittelt den Zahlenwert ab dem Anfang der Zeichenkette aus der + Spalte "VALUE", inclusive negativen Vorzeichen und Dezimaltrenner. + Benutzt z.B. für Ausprägungen wie -5.7°C. +
          • +
          • delta-h / delta-d
            + Ermittelt die relative Veränderung eines Zahlenwertes pro Stunde + oder pro Tag. Wird benutzt z.B. für Spalten die einen + hochlaufenden Zähler enthalten wie im Falle für ein KS300 Regenzähler + oder dem 1-wire Modul OWCOUNT. +
          • +
        • +
        • <regexp>
          + Diese Zeichenkette wird als Perl Befehl ausgewertet. $val ist der + aktuelle Wert die die Datenbank für ein Device/Reading ausgibt. + Die regexp wird vor dem angegebenen <fn> Parameter ausgeführt. +
          + Bitte zur Beachtung: Diese Zeichenkette darf keine Leerzeichen + enthalten da diese sonst als <column_spec> Trennung + interpretiert werden und alles nach dem Leerzeichen als neue + <column_spec> gesehen wird. +
        • +
      • +
      +

      + Beispiele: +
        +
      • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature
      • +
      • get myDbLog - - 2012-11-10_10 2012-11-10_20 KS300:temperature::int1
        + gibt Daten aus von 10Uhr bis 20Uhr am 10.11.2012
      • +
      • get myDbLog - all 2012-11-10 2012-11-20 KS300:temperature
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 KS300:temperature KS300:rain::delta-h KS300:rain::delta-d
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 MyFS20:data:::$val=~s/(on|off).*/$1eq"on"?1:0/eg
        + gibt 1 zurück für alle Ausprägungen von on* (on|on-for-timer etc) und 0 für alle off*
      • +
      • get myDbLog - - 2012-11-10 2012-11-20 Bodenfeuchte:data:::$val=~s/.*B:\s([-\.\d]+).*/$1/eg
        + Beispiel von OWAD: Ein Wert wie z.B.: "A: 49.527 % B: 66.647 % C: 9.797 % D: 0.097 V"
        + und die Ausgabe ist für das Reading B folgende: 2012-11-20_10:23:54 66.647
      • +
      +

      +
    + + Get für die Nutzung von webcharts +
      + get <name> <infile> <outfile> <from> + <to> <device> <querytype> <xaxis> <yaxis> <savename> +

      + Liest Daten aus der Datenbank aus und gibt diese in JSON formatiert aus. Wird für das Charting Frontend genutzt +
      + +
        +
      • <name>
        + Der Name des definierten DbLogs, so wie er in der fhem.cfg angegeben wurde. Muss für Funktionalität auf logdb gesetzt werden.
      • +
      • <in>
        + Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. + Dieser Parameter ist immer auf - zu setzen.
      • +
      • <out>
        + Ein Dummy Parameter um eine Kompatibilität zum Filelog herzustellen. + Dieser Parameter ist auf webchart zu setzen um die Charting Get Funktion zu nutzen. +
      • +
      • <from> / <to>
        + Wird benutzt um den Zeitraum der Daten einzugrenzen. Es ist das folgende + Zeitformat zu benutzen:
        +
          YYYY-MM-DD_HH24:MI:SS
      • +
      • <device>
        + Ein String, der das abzufragende Device darstellt.
      • +
      • <querytype>
        + Ein String, der die zu verwendende Abfragemethode darstellt. Zur Zeit unterstützte Werte sind:
        + getreadings um für ein bestimmtes device alle Readings zu erhalten
        + getdevices um alle verfügbaren devices zu erhalten
        + timerange um Chart-Daten abzufragen. Es werden die Parameter 'xaxis', 'yaxis', 'device', 'to' und 'from' benötigt
        + savechart um einen Chart unter Angabe eines 'savename' und seiner zugehörigen Konfiguration abzuspeichern
        + deletechart um einen zuvor gespeicherten Chart unter Angabe eines 'savename' zu löschen
        +
      • +
      • <xaxis>
        + Ein String, der die X-Achse repräsentiert
      • +
      • <yaxis>
        + Ein String, der die Y-Achse repräsentiert
      • +
      • <savename>
        + Ein String, unter dem ein Chart in der Datenbank gespeichert werden soll
      • +
      +

      + Beispiele: +
        +
      • get logdb - webchart "" "" "" getcharts
        + Liefert alle gespeicherten Charts aus der Datenbank
      • +
      • get logdb - webchart "" "" "" getdevices
        + Liefert alle verfügbaren Devices aus der Datenbank
      • +
      • get logdb - webchart "" "" ESA2000_LED_011e getreadings
        + Liefert alle verfügbaren Readings aus der Datenbank unter Angabe eines Gerätes
      • +
      • get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e timerange TIMESTAMP day_kwh
        + Liefert Chart-Daten, die auf folgenden Parametern basieren: 'xaxis', 'yaxis', 'device', 'to' und 'from'
        + Die Ausgabe erfolgt als JSON, z.B.: [{'TIMESTAMP':'2013-02-11 00:10:10','VALUE':'0.22431388090756'},{'TIMESTAMP'.....}]
      • +
      • get logdb - webchart 2013-02-11_00:00:00 2013-02-12_00:00:00 ESA2000_LED_011e savechart TIMESTAMP day_kwh tageskwh
        + Speichert einen Chart unter Angabe eines 'savename' und seiner zugehörigen Konfiguration
      • +
      • get logdb - webchart "" "" "" deletechart "" "" tageskwh
        + Löscht einen zuvor gespeicherten Chart unter Angabe eines 'savename'
      • +
      +

      +
    + + + Attributes
      N/A

    +
+ +=end html_DE +=cut + diff --git a/fhem/www/frontend/controls_frontend.txt b/fhem/www/frontend/controls_frontend.txt index 6e0686e2b..5e15f030d 100644 --- a/fhem/www/frontend/controls_frontend.txt +++ b/fhem/www/frontend/controls_frontend.txt @@ -1,2 +1,437 @@ DIR www/frontend +DIR www/frontend/app +DIR www/frontend/app/model +DIR www/frontend/app/store +DIR www/frontend/app/view +DIR www/frontend/app/controller +DIR www/frontend/lib +DIR www/frontend/lib/ext-4.1.1a +DIR www/frontend/lib/ext-4.1.1a/images +DIR www/frontend/lib/ext-4.1.1a/images/gray +DIR www/frontend/lib/ext-4.1.1a/images/gray/tools +DIR www/frontend/lib/ext-4.1.1a/images/gray/tree +DIR www/frontend/lib/ext-4.1.1a/images/gray/window-header +DIR www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip +DIR www/frontend/lib/ext-4.1.1a/images/gray/datepicker +DIR www/frontend/lib/ext-4.1.1a/images/gray/sizer +DIR www/frontend/lib/ext-4.1.1a/images/gray/tab +DIR www/frontend/lib/ext-4.1.1a/images/gray/toolbar +DIR www/frontend/lib/ext-4.1.1a/images/gray/shared +DIR www/frontend/lib/ext-4.1.1a/images/gray/btn +DIR www/frontend/lib/ext-4.1.1a/images/gray/btn-group +DIR www/frontend/lib/ext-4.1.1a/images/gray/tab-bar +DIR www/frontend/lib/ext-4.1.1a/images/gray/progress +DIR www/frontend/lib/ext-4.1.1a/images/gray/boundlist +DIR www/frontend/lib/ext-4.1.1a/images/gray/dd +DIR www/frontend/lib/ext-4.1.1a/images/gray/box +DIR www/frontend/lib/ext-4.1.1a/images/gray/form +DIR www/frontend/lib/ext-4.1.1a/images/gray/menu +DIR www/frontend/lib/ext-4.1.1a/images/gray/slider +DIR www/frontend/lib/ext-4.1.1a/images/gray/button +DIR www/frontend/lib/ext-4.1.1a/images/gray/layout +DIR www/frontend/lib/ext-4.1.1a/images/gray/panel +DIR www/frontend/lib/ext-4.1.1a/images/gray/window +DIR www/frontend/lib/ext-4.1.1a/images/gray/grid +DIR www/frontend/lib/ext-4.1.1a/images/gray/util +DIR www/frontend/lib/ext-4.1.1a/images/gray/panel-header +DIR www/frontend/lib/ext-4.1.1a/images/gray/tip +UPD 2013-03-01_05:44:48 49099 FHEM/93_DbLog.pm UPD 2013-02-25_08:13:15 499 www/frontend/index.html +UPD 2013-02-27_07:20:39 236 www/frontend/README.txt +UPD 2013-03-01_05:42:05 1655 www/frontend/app/app.js +UPD 2013-03-01_05:43:56 20350 www/frontend/app/view/LineChartPanel.js +UPD 2013-03-01_05:43:56 6770 www/frontend/app/view/Viewport.js +UPD 2013-03-01_05:43:56 2503 www/frontend/app/view/TableDataGridPanel.js +UPD 2013-03-01_05:43:55 1310 www/frontend/app/view/LineChartView.js +UPD 2013-03-01_05:42:32 41591 www/frontend/app/controller/ChartController.js +UPD 2013-03-01_05:42:32 2115 www/frontend/app/controller/MainController.js +UPD 2013-03-01_05:43:32 202 www/frontend/app/model/ReadingsModel.js +UPD 2013-03-01_05:43:32 338 www/frontend/app/model/SavedChartsModel.js +UPD 2013-03-01_05:43:31 674 www/frontend/app/model/ChartModel.js +UPD 2013-03-01_05:43:32 198 www/frontend/app/model/DeviceModel.js +UPD 2013-03-01_05:43:31 685 www/frontend/app/model/TableDataModel.js +UPD 2013-03-01_05:43:45 432 www/frontend/app/store/ChartStore.js +UPD 2013-03-01_05:43:45 505 www/frontend/app/store/SavedChartsStore.js +UPD 2013-03-01_05:43:45 426 www/frontend/app/store/ReadingsStore.js +UPD 2013-03-01_05:43:45 1048 www/frontend/app/store/TableDataStore.js +UPD 2013-03-01_05:43:44 447 www/frontend/app/store/DeviceStore.js +UPD 2013-02-25_08:13:15 1291919 www/frontend/lib/ext-4.1.1a/ext-all.js +UPD 2013-02-25_08:13:15 381835 www/frontend/lib/ext-4.1.1a/ext-all-gray-debug.css +UPD 2013-02-25_08:13:15 1981 www/frontend/lib/ext-4.1.1a/images/gray/tools/tools-sprites-trans.gif +UPD 2013-02-25_08:13:15 5835 www/frontend/lib/ext-4.1.1a/images/gray/tools/tool-sprites.gif +UPD 2013-02-25_08:13:15 971 www/frontend/lib/ext-4.1.1a/images/gray/tools/tool-sprite-tpl.gif +UPD 2013-02-25_08:13:15 911 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-over.gif +UPD 2013-02-25_08:13:15 1001 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-add.gif +UPD 2013-02-25_08:13:15 159 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-end-plus.gif +UPD 2013-02-25_08:13:15 771 www/frontend/lib/ext-4.1.1a/images/gray/tree/loading.gif +UPD 2013-02-25_08:13:15 356 www/frontend/lib/ext-4.1.1a/images/gray/tree/folder-open.gif +UPD 2013-02-25_08:13:15 70 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-end.gif +UPD 2013-02-25_08:13:15 72 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-line.gif +UPD 2013-02-25_08:13:15 945 www/frontend/lib/ext-4.1.1a/images/gray/tree/leaf.gif +UPD 2013-02-25_08:13:15 152 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-plus-nl.gif +UPD 2013-02-25_08:13:15 160 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-plus.gif +UPD 2013-02-25_08:13:15 911 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-below.gif +UPD 2013-02-25_08:13:15 911 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-above.gif +UPD 2013-02-25_08:13:15 151 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-minus-nl.gif +UPD 2013-02-25_08:13:15 73 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow.gif +UPD 2013-02-25_08:13:15 1016 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-yes.gif +UPD 2013-02-25_08:13:15 1001 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-append.gif +UPD 2013-02-25_08:13:15 351 www/frontend/lib/ext-4.1.1a/images/gray/tree/folder.gif +UPD 2013-02-25_08:13:15 617 www/frontend/lib/ext-4.1.1a/images/gray/tree/arrows.gif +UPD 2013-02-25_08:13:15 907 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-between.gif +UPD 2013-02-25_08:13:15 43 www/frontend/lib/ext-4.1.1a/images/gray/tree/s.gif +UPD 2013-02-25_08:13:15 151 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-end-minus-nl.gif +UPD 2013-02-25_08:13:15 157 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-end-minus.gif +UPD 2013-02-25_08:13:15 159 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-minus.gif +UPD 2013-02-25_08:13:15 911 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-under.gif +UPD 2013-02-25_08:13:15 152 www/frontend/lib/ext-4.1.1a/images/gray/tree/elbow-end-plus-nl.gif +UPD 2013-02-25_08:13:15 949 www/frontend/lib/ext-4.1.1a/images/gray/tree/drop-no.gif +UPD 2013-02-25_08:13:15 1690 www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip/form-invalid-tip-corners.gif +UPD 2013-02-25_08:13:15 1690 www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip/form-invalid-tip-default-corners.gif +UPD 2013-02-25_08:13:15 1647 www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip/form-invalid-tip-default-sides.gif +UPD 2013-02-25_08:13:15 1647 www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip/form-invalid-tip-sides.gif +UPD 2013-02-25_08:13:15 2072 www/frontend/lib/ext-4.1.1a/images/gray/editor/tb-sprite.gif +UPD 2013-02-25_08:13:15 204 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-right-corners.gif +UPD 2013-02-25_08:13:15 1647 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-left-corners.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-left-sides.gif +UPD 2013-02-25_08:13:15 202 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-top-corners.gif +UPD 2013-02-25_08:13:15 1649 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-right-corners.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-left-sides.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-right-sides.gif +UPD 2013-02-25_08:13:15 191 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-top-corners.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-bottom-sides.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-top-sides.gif +UPD 2013-02-25_08:13:15 1652 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-bottom-corners.gif +UPD 2013-02-25_08:13:15 203 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-bottom-corners.gif +UPD 2013-02-25_08:13:15 151 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-left-corners.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-top-sides.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-collapsed-right-sides.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/window-header/window-header-default-bottom-sides.gif +UPD 2013-02-25_08:13:15 328 www/frontend/lib/ext-4.1.1a/images/gray/datepicker/datepicker-footer-bg.png +UPD 2013-02-25_08:13:15 1800 www/frontend/lib/ext-4.1.1a/images/gray/datepicker/datepicker-footer-bg.gif +UPD 2013-02-25_08:13:15 1836 www/frontend/lib/ext-4.1.1a/images/gray/datepicker/datepicker-header-bg.gif +UPD 2013-02-25_08:13:15 309 www/frontend/lib/ext-4.1.1a/images/gray/datepicker/datepicker-header-bg.png +UPD 2013-02-25_08:13:15 1060 www/frontend/lib/ext-4.1.1a/images/gray/sizer/s-handle-dark.gif +UPD 2013-02-25_08:13:15 114 www/frontend/lib/ext-4.1.1a/images/gray/sizer/nw-handle.gif +UPD 2013-02-25_08:13:15 494 www/frontend/lib/ext-4.1.1a/images/gray/sizer/s-handle.gif +UPD 2013-02-25_08:13:15 114 www/frontend/lib/ext-4.1.1a/images/gray/sizer/se-handle.gif +UPD 2013-02-25_08:13:15 128 www/frontend/lib/ext-4.1.1a/images/gray/sizer/ne-handle.gif +UPD 2013-02-25_08:13:15 123 www/frontend/lib/ext-4.1.1a/images/gray/sizer/square.gif +UPD 2013-02-25_08:13:15 838 www/frontend/lib/ext-4.1.1a/images/gray/sizer/se-handle-dark.gif +UPD 2013-02-25_08:13:15 839 www/frontend/lib/ext-4.1.1a/images/gray/sizer/nw-handle-dark.gif +UPD 2013-02-25_08:13:15 839 www/frontend/lib/ext-4.1.1a/images/gray/sizer/ne-handle-dark.gif +UPD 2013-02-25_08:13:15 116 www/frontend/lib/ext-4.1.1a/images/gray/sizer/sw-handle.gif +UPD 2013-02-25_08:13:15 839 www/frontend/lib/ext-4.1.1a/images/gray/sizer/sw-handle-dark.gif +UPD 2013-02-25_08:13:15 753 www/frontend/lib/ext-4.1.1a/images/gray/sizer/e-handle.gif +UPD 2013-02-25_08:13:15 1062 www/frontend/lib/ext-4.1.1a/images/gray/sizer/e-handle-dark.gif +UPD 2013-02-25_08:13:15 1811 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-active-bg.gif +UPD 2013-02-25_08:13:15 1789 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-disabled-bg.gif +UPD 2013-02-25_08:13:15 1648 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-over-corners.gif +UPD 2013-02-25_08:13:15 1783 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-over-bg.gif +UPD 2013-02-25_08:13:15 1997 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-active-sides.gif +UPD 2013-02-25_08:13:15 1647 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-corners.gif +UPD 2013-02-25_08:13:15 1796 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-bg.gif +UPD 2013-02-25_08:13:15 1985 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-sides.gif +UPD 2013-02-25_08:13:15 1979 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-over-sides.gif +UPD 2013-02-25_08:13:15 1645 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-active-corners.gif +UPD 2013-02-25_08:13:15 896 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-close.gif +UPD 2013-02-25_08:13:15 1960 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-over-sides.gif +UPD 2013-02-25_08:13:15 1773 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-over-bg.gif +UPD 2013-02-25_08:13:15 1983 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-sides.gif +UPD 2013-02-25_08:13:15 1642 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-active-corners.gif +UPD 2013-02-25_08:13:15 1642 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-over-corners.gif +UPD 2013-02-25_08:13:15 1977 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-disabled-sides.gif +UPD 2013-02-25_08:13:15 1646 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-corners.gif +UPD 2013-02-25_08:13:15 1796 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-disabled-bg.gif +UPD 2013-02-25_08:13:15 1637 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-disabled-corners.gif +UPD 2013-02-25_08:13:15 1998 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-active-sides.gif +UPD 2013-02-25_08:13:15 1632 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-disabled-corners.gif +UPD 2013-02-25_08:13:15 1989 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-disabled-sides.gif +UPD 2013-02-25_08:13:15 1813 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-bottom-active-bg.gif +UPD 2013-02-25_08:13:15 1795 www/frontend/lib/ext-4.1.1a/images/gray/tab/tab-default-top-bg.gif +UPD 2013-02-25_08:13:15 1865 www/frontend/lib/ext-4.1.1a/images/gray/toolbar/scroll-right.gif +UPD 2013-02-25_08:13:15 1861 www/frontend/lib/ext-4.1.1a/images/gray/toolbar/scroll-left.gif +UPD 2013-02-25_08:13:15 845 www/frontend/lib/ext-4.1.1a/images/gray/toolbar/more.gif +UPD 2013-02-25_08:13:15 1801 www/frontend/lib/ext-4.1.1a/images/gray/toolbar/toolbar-default-bg.gif +UPD 2013-02-25_08:13:15 2118 www/frontend/lib/ext-4.1.1a/images/gray/shared/loading-balls.gif +UPD 2013-02-25_08:13:15 1669 www/frontend/lib/ext-4.1.1a/images/gray/shared/icon-error.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/shared/icon-question.gif +UPD 2013-02-25_08:13:15 979 www/frontend/lib/ext-4.1.1a/images/gray/shared/calendar.gif +UPD 2013-02-25_08:13:15 873 www/frontend/lib/ext-4.1.1a/images/gray/shared/glass-bg.gif +UPD 2013-02-25_08:13:15 1099 www/frontend/lib/ext-4.1.1a/images/gray/shared/hd-sprite.gif +UPD 2013-02-25_08:13:15 1483 www/frontend/lib/ext-4.1.1a/images/gray/shared/icon-warning.gif +UPD 2013-02-25_08:13:15 106 www/frontend/lib/ext-4.1.1a/images/gray/shared/left-btn.gif +UPD 2013-02-25_08:13:15 1586 www/frontend/lib/ext-4.1.1a/images/gray/shared/icon-info.gif +UPD 2013-02-25_08:13:15 3236 www/frontend/lib/ext-4.1.1a/images/gray/shared/blue-loading.gif +UPD 2013-02-25_08:13:15 960 www/frontend/lib/ext-4.1.1a/images/gray/shared/warning.gif +UPD 2013-02-25_08:13:15 3236 www/frontend/lib/ext-4.1.1a/images/gray/shared/large-loading.gif +UPD 2013-02-25_08:13:15 107 www/frontend/lib/ext-4.1.1a/images/gray/shared/right-btn.gif +UPD 2013-02-25_08:13:15 311 www/frontend/lib/ext-4.1.1a/images/gray/shared/shadow.png +UPD 2013-02-25_08:13:15 118 www/frontend/lib/ext-4.1.1a/images/gray/shared/shadow-c.png +UPD 2013-02-25_08:13:15 135 www/frontend/lib/ext-4.1.1a/images/gray/shared/shadow-lr.png +UPD 2013-02-25_08:13:15 1803 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-disabled-bg.gif +UPD 2013-02-25_08:13:15 1594 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-sides.gif +UPD 2013-02-25_08:13:15 1594 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-disabled-sides.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-over-corners.gif +UPD 2013-02-25_08:13:15 1892 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-focus-sides.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-over-corners.gif +UPD 2013-02-25_08:13:15 1622 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-over-corners.gif +UPD 2013-02-25_08:13:15 1839 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-bg.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-corners.gif +UPD 2013-02-25_08:13:15 1618 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-disabled-corners.gif +UPD 2013-02-25_08:13:15 1823 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-focus-bg.gif +UPD 2013-02-25_08:13:15 1934 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-over-sides.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-pressed-corners.gif +UPD 2013-02-25_08:13:15 1826 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-bg.gif +UPD 2013-02-25_08:13:15 1810 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-focus-bg.gif +UPD 2013-02-25_08:13:15 1923 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-disabled-sides.gif +UPD 2013-02-25_08:13:15 1615 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-disabled-corners.gif +UPD 2013-02-25_08:13:15 1943 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-disabled-sides.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-disabled-corners.gif +UPD 2013-02-25_08:13:15 1810 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-over-bg.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-corners.gif +UPD 2013-02-25_08:13:15 1889 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-sides.gif +UPD 2013-02-25_08:13:15 1596 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-disabled-sides.gif +UPD 2013-02-25_08:13:15 1589 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-disabled-sides.gif +UPD 2013-02-25_08:13:15 1894 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-pressed-sides.gif +UPD 2013-02-25_08:13:15 1892 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-over-sides.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-over-corners.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-focus-corners.gif +UPD 2013-02-25_08:13:15 1858 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-pressed-bg.gif +UPD 2013-02-25_08:13:15 1892 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-focus-sides.gif +UPD 2013-02-25_08:13:15 1915 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-over-sides.gif +UPD 2013-02-25_08:13:15 1858 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-pressed-bg.gif +UPD 2013-02-25_08:13:15 1892 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-over-sides.gif +UPD 2013-02-25_08:13:15 1917 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-pressed-sides.gif +UPD 2013-02-25_08:13:15 1894 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-pressed-sides.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-corners.gif +UPD 2013-02-25_08:13:15 1837 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-focus-bg.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-corners.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-pressed-corners.gif +UPD 2013-02-25_08:13:15 1622 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-focus-corners.gif +UPD 2013-02-25_08:13:15 1589 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-sides.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-focus-corners.gif +UPD 2013-02-25_08:13:15 1832 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-disabled-bg.gif +UPD 2013-02-25_08:13:15 1935 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-over-sides.gif +UPD 2013-02-25_08:13:15 1915 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-over-sides.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-corners.gif +UPD 2013-02-25_08:13:15 1837 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-focus-bg.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-pressed-corners.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-focus-corners.gif +UPD 2013-02-25_08:13:15 1823 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-focus-bg.gif +UPD 2013-02-25_08:13:15 1810 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-over-bg.gif +UPD 2013-02-25_08:13:15 1823 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-over-bg.gif +UPD 2013-02-25_08:13:15 1837 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-over-bg.gif +UPD 2013-02-25_08:13:15 1935 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-focus-sides.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-pressed-corners.gif +UPD 2013-02-25_08:13:15 1618 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-focus-corners.gif +UPD 2013-02-25_08:13:15 1839 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-pressed-bg.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-corners.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-disabled-corners.gif +UPD 2013-02-25_08:13:15 1585 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-disabled-corners.gif +UPD 2013-02-25_08:13:15 1810 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-focus-bg.gif +UPD 2013-02-25_08:13:15 1934 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-focus-sides.gif +UPD 2013-02-25_08:13:15 1823 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-over-bg.gif +UPD 2013-02-25_08:13:15 1596 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-sides.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-pressed-corners.gif +UPD 2013-02-25_08:13:15 1816 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-pressed-bg.gif +UPD 2013-02-25_08:13:15 1847 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-disabled-bg.gif +UPD 2013-02-25_08:13:15 1915 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-medium-focus-sides.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-over-corners.gif +UPD 2013-02-25_08:13:15 1619 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-pressed-corners.gif +UPD 2013-02-25_08:13:15 1801 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-bg.gif +UPD 2013-02-25_08:13:15 1888 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-disabled-sides.gif +UPD 2013-02-25_08:13:15 1917 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-pressed-sides.gif +UPD 2013-02-25_08:13:15 1947 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-pressed-sides.gif +UPD 2013-02-25_08:13:15 1919 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-sides.gif +UPD 2013-02-25_08:13:15 1945 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-pressed-sides.gif +UPD 2013-02-25_08:13:15 1816 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-pressed-bg.gif +UPD 2013-02-25_08:13:15 1837 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-over-bg.gif +UPD 2013-02-25_08:13:15 1915 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-focus-sides.gif +UPD 2013-02-25_08:13:15 1618 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-small-disabled-corners.gif +UPD 2013-02-25_08:13:15 1938 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-large-sides.gif +UPD 2013-02-25_08:13:15 1839 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-medium-pressed-bg.gif +UPD 2013-02-25_08:13:15 1621 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-small-focus-corners.gif +UPD 2013-02-25_08:13:15 1618 www/frontend/lib/ext-4.1.1a/images/gray/btn/btn-default-toolbar-large-over-corners.gif +UPD 2013-02-25_08:13:15 1598 www/frontend/lib/ext-4.1.1a/images/gray/btn-group/btn-group-default-framed-notitle-corners.gif +UPD 2013-02-25_08:13:15 1623 www/frontend/lib/ext-4.1.1a/images/gray/btn-group/btn-group-default-framed-notitle-sides.gif +UPD 2013-02-25_08:13:15 1598 www/frontend/lib/ext-4.1.1a/images/gray/btn-group/btn-group-default-framed-corners.gif +UPD 2013-02-25_08:13:15 1630 www/frontend/lib/ext-4.1.1a/images/gray/btn-group/btn-group-default-framed-sides.gif +UPD 2013-02-25_08:13:15 1269 www/frontend/lib/ext-4.1.1a/images/gray/tab-bar/scroll-right.gif +UPD 2013-02-25_08:13:15 1260 www/frontend/lib/ext-4.1.1a/images/gray/tab-bar/scroll-left.gif +UPD 2013-02-25_08:13:15 314 www/frontend/lib/ext-4.1.1a/images/gray/tab-bar/tab-bar-default-bg.gif +UPD 2013-02-25_08:13:15 1832 www/frontend/lib/ext-4.1.1a/images/gray/progress/progress-default-bg.gif +UPD 2013-02-25_08:13:15 2869 www/frontend/lib/ext-4.1.1a/images/gray/boundlist/trigger-arrow.png +UPD 2013-02-25_08:13:15 1001 www/frontend/lib/ext-4.1.1a/images/gray/dd/drop-add.gif +UPD 2013-02-25_08:13:15 1016 www/frontend/lib/ext-4.1.1a/images/gray/dd/drop-yes.gif +UPD 2013-02-25_08:13:15 949 www/frontend/lib/ext-4.1.1a/images/gray/dd/drop-no.gif +UPD 2013-02-25_08:13:15 810 www/frontend/lib/ext-4.1.1a/images/gray/box/r-blue.gif +UPD 2013-02-25_08:13:15 839 www/frontend/lib/ext-4.1.1a/images/gray/box/tb.gif +UPD 2013-02-25_08:13:15 810 www/frontend/lib/ext-4.1.1a/images/gray/box/l-blue.gif +UPD 2013-02-25_08:13:15 810 www/frontend/lib/ext-4.1.1a/images/gray/box/r.gif +UPD 2013-02-25_08:13:15 810 www/frontend/lib/ext-4.1.1a/images/gray/box/l.gif +UPD 2013-02-25_08:13:15 851 www/frontend/lib/ext-4.1.1a/images/gray/box/tb-blue.gif +UPD 2013-02-25_08:13:15 1010 www/frontend/lib/ext-4.1.1a/images/gray/box/corners-blue.gif +UPD 2013-02-25_08:13:15 1005 www/frontend/lib/ext-4.1.1a/images/gray/box/corners.gif +UPD 2013-02-25_08:13:15 1080 www/frontend/lib/ext-4.1.1a/images/gray/form/trigger.gif +UPD 2013-02-25_08:13:15 2061 www/frontend/lib/ext-4.1.1a/images/gray/form/checkbox.gif +UPD 2013-02-25_08:13:15 1071 www/frontend/lib/ext-4.1.1a/images/gray/form/trigger-square.gif +UPD 2013-02-25_08:13:15 1425 www/frontend/lib/ext-4.1.1a/images/gray/form/clear-trigger.gif +UPD 2013-02-25_08:13:15 929 www/frontend/lib/ext-4.1.1a/images/gray/form/date-trigger.gif +UPD 2013-02-25_08:13:15 996 www/frontend/lib/ext-4.1.1a/images/gray/form/exclamation.gif +UPD 2013-02-25_08:13:15 819 www/frontend/lib/ext-4.1.1a/images/gray/form/text-bg.gif +UPD 2013-02-25_08:13:15 1975 www/frontend/lib/ext-4.1.1a/images/gray/form/spinner.gif +UPD 2013-02-25_08:13:15 2220 www/frontend/lib/ext-4.1.1a/images/gray/form/search-trigger.gif +UPD 2013-02-25_08:13:15 4183 www/frontend/lib/ext-4.1.1a/images/gray/form/error-tip-corners.gif +UPD 2013-02-25_08:13:15 1487 www/frontend/lib/ext-4.1.1a/images/gray/form/trigger-tpl.gif +UPD 2013-02-25_08:13:15 1746 www/frontend/lib/ext-4.1.1a/images/gray/form/radio.gif +UPD 2013-02-25_08:13:15 743 www/frontend/lib/ext-4.1.1a/images/gray/form/spinner-small.gif +UPD 2013-02-25_08:13:15 1820 www/frontend/lib/ext-4.1.1a/images/gray/menu/menu-item-active-bg.gif +UPD 2013-02-25_08:13:15 941 www/frontend/lib/ext-4.1.1a/images/gray/menu/unchecked.gif +UPD 2013-02-25_08:13:15 834 www/frontend/lib/ext-4.1.1a/images/gray/menu/menu.gif +UPD 2013-02-25_08:13:15 850 www/frontend/lib/ext-4.1.1a/images/gray/menu/item-over.gif +UPD 2013-02-25_08:13:15 959 www/frontend/lib/ext-4.1.1a/images/gray/menu/checked.gif +UPD 2013-02-25_08:13:15 1902 www/frontend/lib/ext-4.1.1a/images/gray/menu/menu-item-active-sides.gif +UPD 2013-02-25_08:13:15 165 www/frontend/lib/ext-4.1.1a/images/gray/menu/menu-parent.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/menu/menu-item-active-corners.gif +UPD 2013-02-25_08:13:15 295 www/frontend/lib/ext-4.1.1a/images/gray/menu/group-checked.gif +UPD 2013-02-25_08:13:15 49 www/frontend/lib/ext-4.1.1a/images/gray/menu/item-over-disabled.gif +UPD 2013-02-25_08:13:15 533 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-v-thumb.gif +UPD 2013-02-25_08:13:15 632 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-v-thumb.png +UPD 2013-02-25_08:13:15 150 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-v-bg.gif +UPD 2013-02-25_08:13:15 675 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-thumb.png +UPD 2013-02-25_08:13:15 542 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-thumb.gif +UPD 2013-02-25_08:13:15 145 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-bg.gif +UPD 2013-02-25_08:13:15 1494 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-bg.png +UPD 2013-02-25_08:13:15 288 www/frontend/lib/ext-4.1.1a/images/gray/slider/slider-v-bg.png +UPD 2013-02-25_08:13:15 3319 www/frontend/lib/ext-4.1.1a/images/gray/button/btn.gif +UPD 2013-02-25_08:13:15 861 www/frontend/lib/ext-4.1.1a/images/gray/button/group-lr.gif +UPD 2013-02-25_08:13:15 846 www/frontend/lib/ext-4.1.1a/images/gray/button/group-tb.gif +UPD 2013-02-25_08:13:15 139 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-o.gif +UPD 2013-02-25_08:13:15 1222 www/frontend/lib/ext-4.1.1a/images/gray/button/btn-sprite.gif +UPD 2013-02-25_08:13:15 863 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-noline.gif +UPD 2013-02-25_08:13:15 898 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-b-noline.gif +UPD 2013-02-25_08:13:15 2459 www/frontend/lib/ext-4.1.1a/images/gray/button/group-cs.gif +UPD 2013-02-25_08:13:15 937 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow.gif +UPD 2013-02-25_08:13:15 937 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-b.gif +UPD 2013-02-25_08:13:15 870 www/frontend/lib/ext-4.1.1a/images/gray/button/btn-arrow.gif +UPD 2013-02-25_08:13:15 937 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-bo.gif +UPD 2013-02-25_08:13:15 828 www/frontend/lib/ext-4.1.1a/images/gray/button/arrow.gif +UPD 2013-02-25_08:13:15 116 www/frontend/lib/ext-4.1.1a/images/gray/button/s-arrow-light.gif +UPD 2013-02-25_08:13:15 871 www/frontend/lib/ext-4.1.1a/images/gray/layout/mini-left.gif +UPD 2013-02-25_08:13:15 872 www/frontend/lib/ext-4.1.1a/images/gray/layout/mini-right.gif +UPD 2013-02-25_08:13:15 856 www/frontend/lib/ext-4.1.1a/images/gray/layout/mini-bottom.gif +UPD 2013-02-25_08:13:15 856 www/frontend/lib/ext-4.1.1a/images/gray/layout/mini-top.gif +UPD 2013-02-25_08:13:15 1727 www/frontend/lib/ext-4.1.1a/images/gray/panel/panel-default-framed-sides.gif +UPD 2013-02-25_08:13:15 1631 www/frontend/lib/ext-4.1.1a/images/gray/panel/panel-default-framed-corners.gif +UPD 2013-02-25_08:13:15 1669 www/frontend/lib/ext-4.1.1a/images/gray/window/icon-error.gif +UPD 2013-02-25_08:13:15 1607 www/frontend/lib/ext-4.1.1a/images/gray/window/icon-question.gif +UPD 2013-02-25_08:13:15 1483 www/frontend/lib/ext-4.1.1a/images/gray/window/icon-warning.gif +UPD 2013-02-25_08:13:15 1586 www/frontend/lib/ext-4.1.1a/images/gray/window/icon-info.gif +UPD 2013-02-25_08:13:15 1672 www/frontend/lib/ext-4.1.1a/images/gray/window/window-default-corners.gif +UPD 2013-02-25_08:13:15 1776 www/frontend/lib/ext-4.1.1a/images/gray/window/window-default-sides.gif +UPD 2013-02-25_08:13:15 136 www/frontend/lib/ext-4.1.1a/images/gray/grid/group-collapse.gif +UPD 2013-02-25_08:13:15 923 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-last-disabled.gif +UPD 2013-02-25_08:13:15 1767 www/frontend/lib/ext-4.1.1a/images/gray/grid/column-header-over-bg.gif +UPD 2013-02-25_08:13:15 325 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-last.gif +UPD 2013-02-25_08:13:15 177 www/frontend/lib/ext-4.1.1a/images/gray/grid/col-move-bottom.gif +UPD 2013-02-25_08:13:15 879 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-prev-disabled.gif +UPD 2013-02-25_08:13:15 59 www/frontend/lib/ext-4.1.1a/images/gray/grid/sort_desc.gif +UPD 2013-02-25_08:13:15 971 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-unlock.gif +UPD 2013-02-25_08:13:15 771 www/frontend/lib/ext-4.1.1a/images/gray/grid/loading.gif +UPD 2013-02-25_08:13:15 1100 www/frontend/lib/ext-4.1.1a/images/gray/grid/wait.gif +UPD 2013-02-25_08:13:15 178 www/frontend/lib/ext-4.1.1a/images/gray/grid/col-move-top.gif +UPD 2013-02-25_08:13:15 829 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-vista-hd.gif +UPD 2013-02-25_08:13:15 875 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-next-disabled.gif +UPD 2013-02-25_08:13:15 697 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-unlock.png +UPD 2013-02-25_08:13:15 825 www/frontend/lib/ext-4.1.1a/images/gray/grid/arrow-left-white.gif +UPD 2013-02-25_08:13:15 823 www/frontend/lib/ext-4.1.1a/images/gray/grid/row-sel.gif +UPD 2013-02-25_08:13:15 186 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-prev.gif +UPD 2013-02-25_08:13:15 1036 www/frontend/lib/ext-4.1.1a/images/gray/grid/pick-button.gif +UPD 2013-02-25_08:13:15 855 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-hrow.gif +UPD 2013-02-25_08:13:15 836 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid3-hrow.gif +UPD 2013-02-25_08:13:15 1640 www/frontend/lib/ext-4.1.1a/images/gray/grid/cell-special-selected-bg.gif +UPD 2013-02-25_08:13:15 823 www/frontend/lib/ext-4.1.1a/images/gray/grid/row-over.gif +UPD 2013-02-25_08:13:15 121 www/frontend/lib/ext-4.1.1a/images/gray/grid/cell-special-bg.png +UPD 2013-02-25_08:13:15 482 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid3-hd-btn.gif +UPD 2013-02-25_08:13:15 832 www/frontend/lib/ext-4.1.1a/images/gray/grid/dirty.gif +UPD 2013-02-25_08:13:15 825 www/frontend/lib/ext-4.1.1a/images/gray/grid/arrow-right-white.gif +UPD 2013-02-25_08:13:15 941 www/frontend/lib/ext-4.1.1a/images/gray/grid/unchecked.gif +UPD 2013-02-25_08:13:15 577 www/frontend/lib/ext-4.1.1a/images/gray/grid/refresh-disabled.gif +UPD 2013-02-25_08:13:15 133 www/frontend/lib/ext-4.1.1a/images/gray/grid/done.gif +UPD 2013-02-25_08:13:15 570 www/frontend/lib/ext-4.1.1a/images/gray/grid/refresh.gif +UPD 2013-02-25_08:13:15 349 www/frontend/lib/ext-4.1.1a/images/gray/grid/dd-insert-arrow-right.png +UPD 2013-02-25_08:13:15 930 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-desc.gif +UPD 2013-02-25_08:13:15 884 www/frontend/lib/ext-4.1.1a/images/gray/grid/nowait.gif +UPD 2013-02-25_08:13:15 925 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-first-disabled.gif +UPD 2013-02-25_08:13:15 1858 www/frontend/lib/ext-4.1.1a/images/gray/grid/column-header-bg.gif +UPD 2013-02-25_08:13:15 843 www/frontend/lib/ext-4.1.1a/images/gray/grid/property-cell-selected-bg.gif +UPD 2013-02-25_08:13:15 59 www/frontend/lib/ext-4.1.1a/images/gray/grid/sort_asc.gif +UPD 2013-02-25_08:13:15 955 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-lock.gif +UPD 2013-02-25_08:13:15 701 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-loading.gif +UPD 2013-02-25_08:13:15 196 www/frontend/lib/ext-4.1.1a/images/gray/grid/group-expand-sprite.gif +UPD 2013-02-25_08:13:15 1083 www/frontend/lib/ext-4.1.1a/images/gray/grid/row-check-sprite.gif +UPD 2013-02-25_08:13:15 345 www/frontend/lib/ext-4.1.1a/images/gray/grid/dd-insert-arrow-left.png +UPD 2013-02-25_08:13:15 815 www/frontend/lib/ext-4.1.1a/images/gray/grid/invalid_line.gif +UPD 2013-02-25_08:13:15 43 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid3-rowheader.gif +UPD 2013-02-25_08:13:15 301 www/frontend/lib/ext-4.1.1a/images/gray/grid/dd-insert-arrow-right.gif +UPD 2013-02-25_08:13:15 283 www/frontend/lib/ext-4.1.1a/images/gray/grid/column-header-over-bg.png +UPD 2013-02-25_08:13:15 959 www/frontend/lib/ext-4.1.1a/images/gray/grid/checked.gif +UPD 2013-02-25_08:13:15 823 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid3-hrow-over.gif +UPD 2013-02-25_08:13:15 834 www/frontend/lib/ext-4.1.1a/images/gray/grid/footer-bg.gif +UPD 2013-02-25_08:13:15 860 www/frontend/lib/ext-4.1.1a/images/gray/grid/drop-yes.gif +UPD 2013-02-25_08:13:15 875 www/frontend/lib/ext-4.1.1a/images/gray/grid/mso-hd.gif +UPD 2013-02-25_08:13:15 817 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-split.gif +UPD 2013-02-25_08:13:15 931 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-asc.gif +UPD 2013-02-25_08:13:15 839 www/frontend/lib/ext-4.1.1a/images/gray/grid/hd-pop.gif +UPD 2013-02-25_08:13:15 817 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-blue-split.gif +UPD 2013-02-25_08:13:15 829 www/frontend/lib/ext-4.1.1a/images/gray/grid/grid-blue-hd.gif +UPD 2013-02-25_08:13:15 136 www/frontend/lib/ext-4.1.1a/images/gray/grid/cell-special-selected-bg.png +UPD 2013-02-25_08:13:15 917 www/frontend/lib/ext-4.1.1a/images/gray/grid/group-by.gif +UPD 2013-02-25_08:13:15 138 www/frontend/lib/ext-4.1.1a/images/gray/grid/group-expand.gif +UPD 2013-02-25_08:13:15 183 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-next.gif +UPD 2013-02-25_08:13:15 1636 www/frontend/lib/ext-4.1.1a/images/gray/grid/cell-special-bg.gif +UPD 2013-02-25_08:13:15 2731 www/frontend/lib/ext-4.1.1a/images/gray/grid/sort-hd.gif +UPD 2013-02-25_08:13:15 293 www/frontend/lib/ext-4.1.1a/images/gray/grid/column-header-bg.png +UPD 2013-02-25_08:13:15 648 www/frontend/lib/ext-4.1.1a/images/gray/grid/hmenu-lock.png +UPD 2013-02-25_08:13:15 155 www/frontend/lib/ext-4.1.1a/images/gray/grid/property-cell-bg.gif +UPD 2013-02-25_08:13:15 196 www/frontend/lib/ext-4.1.1a/images/gray/grid/row-expand-sprite.gif +UPD 2013-02-25_08:13:15 299 www/frontend/lib/ext-4.1.1a/images/gray/grid/dd-insert-arrow-left.gif +UPD 2013-02-25_08:13:15 962 www/frontend/lib/ext-4.1.1a/images/gray/grid/columns.gif +UPD 2013-02-25_08:13:15 947 www/frontend/lib/ext-4.1.1a/images/gray/grid/drop-no.gif +UPD 2013-02-25_08:13:15 327 www/frontend/lib/ext-4.1.1a/images/gray/grid/page-first.gif +UPD 2013-02-25_08:13:15 871 www/frontend/lib/ext-4.1.1a/images/gray/util/splitter/mini-left.gif +UPD 2013-02-25_08:13:15 872 www/frontend/lib/ext-4.1.1a/images/gray/util/splitter/mini-right.gif +UPD 2013-02-25_08:13:15 856 www/frontend/lib/ext-4.1.1a/images/gray/util/splitter/mini-bottom.gif +UPD 2013-02-25_08:13:15 856 www/frontend/lib/ext-4.1.1a/images/gray/util/splitter/mini-top.gif +UPD 2013-02-25_08:13:15 1677 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-right-corners.gif +UPD 2013-02-25_08:13:15 1819 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-bottom-bg.gif +UPD 2013-02-25_08:13:15 1995 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-bottom-sides.gif +UPD 2013-02-25_08:13:15 1842 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-right-sides.gif +UPD 2013-02-25_08:13:15 2003 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-top-sides.gif +UPD 2013-02-25_08:13:15 1819 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-top-bg.gif +UPD 2013-02-25_08:13:15 1676 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-left-corners.gif +UPD 2013-02-25_08:13:15 1633 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-left-bg.gif +UPD 2013-02-25_08:13:15 1851 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-top-bg.gif +UPD 2013-02-25_08:13:15 1841 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-left-sides.gif +UPD 2013-02-25_08:13:15 1641 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-bottom-corners.gif +UPD 2013-02-25_08:13:15 2018 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-top-sides.gif +UPD 2013-02-25_08:13:15 1656 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-top-corners.gif +UPD 2013-02-25_08:13:15 1829 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-bottom-bg.gif +UPD 2013-02-25_08:13:15 1638 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-right-bg.gif +UPD 2013-02-25_08:13:15 1838 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-top-bg.gif +UPD 2013-02-25_08:13:15 1832 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-right-sides.gif +UPD 2013-02-25_08:13:15 2009 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-bottom-sides.gif +UPD 2013-02-25_08:13:15 1842 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-bottom-bg.gif +UPD 2013-02-25_08:13:15 1652 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-bottom-corners.gif +UPD 2013-02-25_08:13:15 1659 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-right-corners.gif +UPD 2013-02-25_08:13:15 1641 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-right-bg.gif +UPD 2013-02-25_08:13:15 1641 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-top-corners.gif +UPD 2013-02-25_08:13:15 1660 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-left-corners.gif +UPD 2013-02-25_08:13:15 1640 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-left-bg.gif +UPD 2013-02-25_08:13:15 1637 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-left-bg.gif +UPD 2013-02-25_08:13:15 1634 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-right-bg.gif +UPD 2013-02-25_08:13:15 1828 www/frontend/lib/ext-4.1.1a/images/gray/panel-header/panel-header-default-framed-collapsed-left-sides.gif +UPD 2013-02-25_08:13:15 1620 www/frontend/lib/ext-4.1.1a/images/gray/tip/tip-corners.gif +UPD 2013-02-25_08:13:15 1623 www/frontend/lib/ext-4.1.1a/images/gray/tip/tip-sides.gif +UPD 2013-02-25_08:13:15 1482 www/frontend/lib/ext-4.1.1a/license.txt diff --git a/fhem/www/frontend/www/frontend/app/controller/ChartController.js b/fhem/www/frontend/www/frontend/app/controller/ChartController.js index e98a8bd9e..3c2c00bb5 100644 --- a/fhem/www/frontend/www/frontend/app/controller/ChartController.js +++ b/fhem/www/frontend/www/frontend/app/controller/ChartController.js @@ -5,6 +5,10 @@ Ext.define('FHEM.controller.ChartController', { extend: 'Ext.app.Controller', refs: [ + { + selector: 'panel[name=chartformpanel]', + ref: 'chartformpanel' //this.getChartformpanel() + }, { selector: 'datefield[name=starttimepicker]', ref: 'starttimepicker' //this.getStarttimepicker() @@ -21,6 +25,14 @@ Ext.define('FHEM.controller.ChartController', { selector: 'button[name=savechartdata]', ref: 'savechartdatabtn' //this.getSavechartdatabtn() }, + { + selector: 'button[name=addyaxisbtn]', + ref: 'addyaxisbtn' //this.getAddyaxisbtn() + }, + { + selector: 'button[name=addbaselinebtn]', + ref: 'addbaselinebtn' //this.getAddbaselinebtn() + }, { selector: 'combobox[name=devicecombo]', ref: 'devicecombo' //this.getDevicecombo() @@ -29,10 +41,50 @@ Ext.define('FHEM.controller.ChartController', { selector: 'combobox[name=xaxiscombo]', ref: 'xaxiscombo' //this.getXaxiscombo() }, + { + selector: 'combobox[name=device2combo]', + ref: 'device2combo' //this.getDevicecombo() + }, + { + selector: 'combobox[name=y2axiscombo]', + ref: 'y2axiscombo' //this.getY2axiscombo() + }, + { + selector: 'combobox[name=device3combo]', + ref: 'device3combo' //this.getDevicecombo() + }, + { + selector: 'combobox[name=y3axiscombo]', + ref: 'y3axiscombo' //this.getY3axiscombo() + }, { selector: 'combobox[name=yaxiscombo]', ref: 'yaxiscombo' //this.getYaxiscombo() }, + { + selector: 'combobox[name=yaxiscolorcombo]', + ref: 'yaxiscolorcombo' //this.getYaxiscombo() + }, + { + selector: 'combobox[name=y2axiscolorcombo]', + ref: 'y2axiscolorcombo' //this.getYaxiscombo() + }, + { + selector: 'combobox[name=y3axiscolorcombo]', + ref: 'y3axiscolorcombo' //this.getYaxiscombo() + }, + { + selector: 'checkboxfield[name=yaxisfillcheck]', + ref: 'yaxisfillcheck' //this.getYaxiscombo() + }, + { + selector: 'checkboxfield[name=y2axisfillcheck]', + ref: 'y2axisfillcheck' //this.getYaxiscombo() + }, + { + selector: 'checkboxfield[name=y3axisfillcheck]', + ref: 'y3axisfillcheck' //this.getYaxiscombo() + }, { selector: 'linechartview', ref: 'linechartview' //this.getLinechartview() @@ -48,9 +100,56 @@ Ext.define('FHEM.controller.ChartController', { { selector: 'grid[name=savedchartsgrid]', ref: 'savedchartsgrid' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base1start]', + ref: 'base1start' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base1end]', + ref: 'base1end' //this.getSavedchartsgrid() + }, + { + selector: 'combobox[name=baseline1colorcombo]', + ref: 'base1color' //this.getSavedchartsgrid() + }, + { + selector: 'checkboxfield[name=baseline1fillcheck]', + ref: 'base1fill' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base2start]', + ref: 'base2start' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base2end]', + ref: 'base2end' //this.getSavedchartsgrid() + }, + { + selector: 'combobox[name=baseline2colorcombo]', + ref: 'base2color' //this.getSavedchartsgrid() + }, + { + selector: 'checkboxfield[name=baseline2fillcheck]', + ref: 'base2fill' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base3start]', + ref: 'base3start' //this.getSavedchartsgrid() + }, + { + selector: 'numberfield[name=base3end]', + ref: 'base3end' //this.getSavedchartsgrid() + }, + { + selector: 'combobox[name=baseline3colorcombo]', + ref: 'base3color' //this.getSavedchartsgrid() + }, + { + selector: 'checkboxfield[name=baseline3fillcheck]', + ref: 'base3fill' //this.getSavedchartsgrid() } - ], /** @@ -61,6 +160,12 @@ Ext.define('FHEM.controller.ChartController', { 'combobox[name=devicecombo]': { select: this.deviceSelected }, + 'combobox[name=device2combo]': { + select: this.deviceSelected + }, + 'combobox[name=device3combo]': { + select: this.deviceSelected + }, 'button[name=requestchartdata]': { click: this.requestChartData }, @@ -73,6 +178,9 @@ Ext.define('FHEM.controller.ChartController', { 'button[name=stepforward]': { click: this.stepchange }, + 'button[name=resetchartform]': { + click: this.resetFormFields + }, 'linechartview': { afterrender: this.enableZoomInChart }, @@ -93,8 +201,19 @@ Ext.define('FHEM.controller.ChartController', { deviceSelected: function(combo){ var device = combo.getValue(), + store, + proxy; + + if (combo.name === "devicecombo") { store = this.getYaxiscombo().getStore(), proxy = store.getProxy(); + } else if (combo.name === "device2combo") { + store = this.getY2axiscombo().getStore(), + proxy = store.getProxy(); + } else if (combo.name === "device3combo") { + store = this.getY3axiscombo().getStore(), + proxy = store.getProxy(); + } if (proxy) { proxy.url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+' + device + '+getreadings&XHR=1'; @@ -113,6 +232,30 @@ Ext.define('FHEM.controller.ChartController', { var device = me.getDevicecombo().getValue(), xaxis = me.getXaxiscombo().getValue(), yaxis = me.getYaxiscombo().getValue(), + yaxiscolorcombo = me.getYaxiscolorcombo().getValue(), + yaxisfillcheck = me.getYaxisfillcheck().checked, + y2device = me.getDevice2combo().getValue(), + y2axis = me.getY2axiscombo().getValue(), + y2axiscolorcombo = me.getY2axiscolorcombo().getValue(), + y2axisfillcheck= me.getY2axisfillcheck().checked, + y3device = me.getDevice3combo().getValue(), + y3axis = me.getY3axiscombo().getValue(), + y3axiscolorcombo = me.getY3axiscolorcombo().getValue(), + y3axisfillcheck = me.getY3axisfillcheck().checked, + + base1start = me.getBase1start().getValue(), + base1end = me.getBase1end().getValue(), + base1color = me.getBase1color().getValue(), + base1fill = me.getBase1fill().checked, + base2start = me.getBase2start().getValue(), + base2end = me.getBase2end().getValue(), + base2color = me.getBase2color().getValue(), + base2fill = me.getBase2fill().checked, + base3start = me.getBase3start().getValue(), + base3end = me.getBase3end().getValue(), + base3color = me.getBase3color().getValue(), + base3fill = me.getBase3fill().checked, + starttime = me.getStarttimepicker().getValue(), dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s'), endtime = me.getEndtimepicker().getValue(), @@ -121,25 +264,20 @@ Ext.define('FHEM.controller.ChartController', { store = me.getLinechartview().getStore(), proxy = store.getProxy(); + //cleanup store + store.removeAll(); + + //cleanup chart + for (var i = view.series.length -1; i >= 0; i--) { + view.series.removeAt(i); + } + //register store listeners store.on("beforeload", function() { me.getLinechartview().setLoading(true); }); - store.on("load", function() { - me.getLinechartview().setLoading(false); - }); - if (proxy) { - var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; - url +=device + '+timerange+' + xaxis + '+' + yaxis + '&XHR=1'; - proxy.url = url; - store.load(); - } - - //remove the old max values of y axis to get a dynamic range - delete view.axes.get(0).maximum; - - view.axes.get(0).setTitle(yaxis); + //setting x-axis title view.axes.get(1).setTitle(xaxis); // set the x axis range dependent on user given timerange @@ -147,7 +285,342 @@ Ext.define('FHEM.controller.ChartController', { view.axes.get(1).toDate = endtime; view.axes.get(1).processView(); - me.getLinechartview().redraw(); + //setup the first y series + var y1series = { + type : 'line', + axis : 'left', + xField : 'TIMESTAMP', + yField : 'VALUE', + title: yaxis, + showInLegend: true, + smooth: 2, + highlight: true, + fill: yaxisfillcheck, + style: { + fill: yaxiscolorcombo, + stroke: yaxiscolorcombo + }, + markerConfig: { + type: 'circle', + size: 3, + radius: 3, + stroke: yaxiscolorcombo + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUE') + + '
Time: ' + storeItem.get('TIMESTAMP')); + } + } + }; + + view.series.add(y1series); + + if (proxy) { + var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; + url +=device + '+timerange+' + xaxis + '+' + yaxis; + url += '&XHR=1'; + proxy.url = url; + store.load(); + store.on("load", function() { + + if (!Ext.isEmpty(y2axis)) { + + //setup the second y series + var y2series = { + type: 'line', + title: y2axis, + style: { + fill: y2axiscolorcombo, + stroke: y2axiscolorcombo + }, + axis: 'left', + fill: y2axisfillcheck, + smooth: 2, + highlight: true, + showInLegend: true, + xField: 'TIMESTAMP2', + yField: 'VALUE2', + markerConfig: { + type: 'circle', + size: 3, + radius: 3, + stroke: y2axiscolorcombo + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUE2') + + '
Time: ' + storeItem.get('TIMESTAMP2')); + } + } + }; + + view.series.add(y2series); + + var url2 = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; + url2 +=y2device + '+timerange+' + xaxis + '+' + y2axis; + url2 += '&XHR=1'; + + Ext.Ajax.request({ + method: 'GET', + disableCaching: false, + url: url2, + success: function(response){ + var json = Ext.decode(response.responseText); + + if (json.success && json.success === "false") { + Ext.Msg.alert("Error", "Error an adding 2nd Y-Axis, error was:
" + json.msg); + } else { + + //rewrite valuedescription to differ from other series / axes + store.each(function(rec, index) { + if (json.data[index]) { + rec.set('VALUE2', json.data[index].VALUE); + rec.set('TIMESTAMP2', json.data[index].TIMESTAMP); + } + + }); + + //add records if y2 contains more than y1 + var storelength = store.getCount(); + if (json.data.length > storelength) { + for (var i = storelength; i < json.data.length; i++) { + store.add( + { + "VALUE2": json.data[i].VALUE, + "TIMESTAMP2": json.data[i].TIMESTAMP + } + ); + } + + } + + if (!Ext.isEmpty(y3axis)) { + + var y3series = { + type: 'line', + title: y3axis, + highlight: true, + style: { + fill: y3axiscolorcombo, + stroke: y3axiscolorcombo + }, + axis: 'left', + fill: y3axisfillcheck, + smooth: 2, + showInLegend: true, + xField: 'TIMESTAMP3', + yField: 'VALUE3', + markerConfig: { + type: 'circle', + size: 3, + radius: 3, + stroke: y3axiscolorcombo + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUE3') + + '
Time: ' + storeItem.get('TIMESTAMP3')); + } + } + }; + + view.series.add(y3series); + + var url3 = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; + url3 +=y3device + '+timerange+' + xaxis + '+' + y3axis; + url3 += '&XHR=1'; + + Ext.Ajax.request({ + method: 'GET', + disableCaching: false, + url: url3, + success: function(response){ + + var json = Ext.decode(response.responseText); + + if (json.success && json.success === "false") { + Ext.Msg.alert("Error", "Error an adding 3rd Y-Axis, error was:
" + json.msg); + } else { + + //rewrite valuedescription to differ from other series / axes + store.each(function(rec, index) { + if (json.data[index]) { + rec.set('VALUE3', json.data[index].VALUE); + rec.set('TIMESTAMP3', json.data[index].TIMESTAMP); + } + + }); + + //add records if y3 contains more than y2 + var storelength = store.getCount(); + if (json.data.length > storelength) { + for (var i = storelength; i < json.data.length; i++) { + store.add( + { + "VALUE3": json.data[i].VALUE, + "TIMESTAMP3": json.data[i].TIMESTAMP + } + ); + } + + } + } + }, + failure: function() { + Ext.Msg.alert("Error", "Error an adding 3rd Y-Axis"); + } + }); + } + } + }, + failure: function() { + Ext.Msg.alert("Error", "Error an adding 2nd Y-Axis"); + } + }); + } + + //adding base lines if neccessary + if (!Ext.isEmpty(base1start) && base1start != "null") { + var bl1 = { + type : 'line', + name: 'baseline1', + axis : 'left', + xField : 'TIMESTAMP', + yField : 'VALUEBASE1', + showInLegend: false, + highlight: true, + fill: base1fill, + style: { + fill : base1color, + 'stroke-width': 3, + stroke: base1color + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUEBASE1') + + '
Time: ' + storeItem.get('TIMESTAMP')); + } + } + }; + view.series.add(bl1); + + store.first().set('VALUEBASE1', base1start); + store.last().set('VALUEBASE1', base1end); + } + + if (!Ext.isEmpty(base2start) && base2start != "null") { + var bl2 = { + type : 'line', + name: 'baseline2', + axis : 'left', + xField : 'TIMESTAMP', + yField : 'VALUEBASE2', + showInLegend: false, + highlight: true, + fill: base2fill, + style: { + fill : base2color, + 'stroke-width': 3, + stroke: base2color + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUEBASE2') + + '
Time: ' + storeItem.get('TIMESTAMP')); + } + } + }; + view.series.add(bl2); + store.first().set('VALUEBASE2', base2start); + store.last().set('VALUEBASE2', base2end); + } + if (!Ext.isEmpty(base3start) && base3start != "null") { + var bl3 = { + type : 'line', + name: 'baseline3', + axis : 'left', + xField : 'TIMESTAMP', + yField : 'VALUEBASE3', + showInLegend: false, + highlight: true, + fill: base3fill, + style: { + fill : base3color, + 'stroke-width': 3, + stroke: base3color + }, + tips : { + trackMouse : true, + width : 140, + height : 100, + renderer : function(storeItem, item) { + this.setTitle(' Value: : ' + storeItem.get('VALUEBASE3') + + '
Time: ' + storeItem.get('TIMESTAMP')); + } + } + }; + view.series.add(bl3); + store.first().set('VALUEBASE3', base3start); + store.last().set('VALUEBASE3', base3end); + + } + + //remove the old max values of y axis to get a dynamic range + delete view.axes.get(0).maximum; + + me.getLinechartview().setLoading(false); + + }, this, {single: true}); + + + } + + + }, + + /** + * reset the form fields e.g. when loading a new chart + */ + resetFormFields: function() { + this.getChartformpanel().getForm().reset(); + this.getDevice2combo().hide(); + this.getY2axiscombo().hide(); + this.getY2axiscolorcombo().hide(); + this.getY2axisfillcheck().hide(); + this.getDevice3combo().hide(); + this.getY3axiscombo().hide(); + this.getY3axiscolorcombo().hide(); + this.getY3axisfillcheck().hide(); + this.getAddyaxisbtn().setDisabled(false); + this.getBase1start().hide(); + this.getBase1end().hide(); + this.getBase1color().hide(); + this.getBase1fill().hide(); + this.getBase2start().hide(); + this.getBase2end().hide(); + this.getBase2color().hide(); + this.getBase2fill().hide(); + this.getBase3start().hide(); + this.getBase3end().hide(); + this.getBase3color().hide(); + this.getBase3fill().hide(); + this.getAddbaselinebtn().setDisabled(false); }, @@ -221,59 +694,85 @@ Ext.define('FHEM.controller.ChartController', { Ext.Msg.prompt("Select a name", "Enter a name to save the Chart", function(action, savename) { if (action === "ok" && !Ext.isEmpty(savename)) { - //getting all devices, check for same name - var store = me.getSavedchartsgrid().getStore(), - storednames = []; + + var device = this.getDevicecombo().getValue(), + xaxis = this.getXaxiscombo().getValue(), + yaxis = this.getYaxiscombo().getValue(), + + yaxiscolorcombo = me.getYaxiscolorcombo().getDisplayValue(), + yaxisfillcheck = me.getYaxisfillcheck().checked, + y2device = me.getDevice2combo().getValue(), + y2axis = me.getY2axiscombo().getValue(), + y2axiscolorcombo = me.getY2axiscolorcombo().getDisplayValue(), + y2axisfillcheck= me.getY2axisfillcheck().checked, + y3device = me.getDevice3combo().getValue(), + y3axis = me.getY3axiscombo().getValue(), + y3axiscolorcombo = me.getY3axiscolorcombo().getDisplayValue(), + y3axisfillcheck = me.getY3axisfillcheck().checked, + base1start = me.getBase1start().getValue(), + base1end = me.getBase1end().getValue(), + base1color = me.getBase1color().getDisplayValue(), + base1fill = me.getBase1fill().checked, + base2start = me.getBase2start().getValue(), + base2end = me.getBase2end().getValue(), + base2color = me.getBase2color().getDisplayValue(), + base2fill = me.getBase2fill().checked, + base3start = me.getBase3start().getValue(), + base3end = me.getBase3end().getValue(), + base3color = me.getBase3color().getDisplayValue(), + base3fill = me.getBase3fill().checked, + + starttime = this.getStarttimepicker().getValue(), + dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s'), + endtime = this.getEndtimepicker().getValue(), + dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s'), + view = this.getLinechartview(); + + var jsonConfig = '{\\"x\\":\\"' + xaxis + '\\",\\"y\\":\\"' + yaxis + '\\",\\"device\\":\\"' + device + '\\",'; + jsonConfig += '\\"yaxiscolorcombo\\":\\"\\' + yaxiscolorcombo + '\\",\\"yaxisfillcheck\\":\\"' + yaxisfillcheck + '\\",'; + jsonConfig += '\\"y2device\\":\\"' + y2device + '\\",'; + jsonConfig += '\\"y2axis\\":\\"' + y2axis + '\\",\\"y2axiscolorcombo\\":\\"' + y2axiscolorcombo + '\\",'; + jsonConfig += '\\"y2axisfillcheck\\":\\"' + y2axisfillcheck + '\\",\\"y3axis\\":\\"' + y3axis + '\\",'; + jsonConfig += '\\"y3device\\":\\"' + y3device + '\\",'; + jsonConfig += '\\"y3axiscolorcombo\\":\\"' + y3axiscolorcombo + '\\",\\"y3axisfillcheck\\":\\"' + y3axisfillcheck + '\\",'; + jsonConfig += '\\"base1start\\":\\"' + base1start + '\\",\\"base1end\\":\\"' + base1end + '\\",'; + jsonConfig += '\\"base1color\\":\\"' + base1color + '\\",\\"base1fill\\":\\"' + base1fill + '\\",'; + jsonConfig += '\\"base2start\\":\\"' + base2start + '\\",\\"base2end\\":\\"' + base2end + '\\",'; + jsonConfig += '\\"base2color\\":\\"' + base2color + '\\",\\"base2fill\\":\\"' + base2fill + '\\",'; + jsonConfig += '\\"base3start\\":\\"' + base3start + '\\",\\"base3end\\":\\"' + base3end + '\\",'; + jsonConfig += '\\"base3color\\":\\"' + base3color + '\\",\\"base3fill\\":\\"' + base3fill + '\\",'; + jsonConfig += '\\"starttime\\":\\"' + dbstarttime + '\\",\\"endtime\\":\\"' + dbendtime + '\\"}'; - store.each(function(record){ - var name = record.get('VALUE'); - storednames.push(name); - }); + var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; + url +=device + '+savechart+""+""+' + savename + '+' + jsonConfig + '&XHR=1'; - if (Ext.Array.contains(storednames, savename)) { - Ext.Msg.alert("Error", "There already is a chart with the name: " + savename); - } else { - - var device = this.getDevicecombo().getValue(), - xaxis = this.getXaxiscombo().getValue(), - yaxis = this.getYaxiscombo().getValue(), - starttime = this.getStarttimepicker().getValue(), - dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s'), - endtime = this.getEndtimepicker().getValue(), - dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s'), - view = this.getLinechartview(); + view.setLoading(true); - var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+'; - url +=device + '+savechart+' + xaxis + '+' + yaxis + '+' + savename + '&XHR=1'; - - view.setLoading(true); - - Ext.Ajax.request({ - method: 'GET', - disableCaching: false, - url: url, - success: function(response){ - view.setLoading(false); - var json = Ext.decode(response.responseText); - if (json.success === true) { - me.getSavedchartsgrid().getStore().load(); - Ext.Msg.alert("Success", "Chart successfully saved!"); - } else if (json.msg) { - Ext.Msg.alert("Error", "The Chart could not be saved, error Message is:

" + json.msg); - } else { - Ext.Msg.alert("Error", "The Chart could not be saved!"); - } - }, - failure: function() { - view.setLoading(false); - if (json && json.msg) { - Ext.Msg.alert("Error", "The Chart could not be saved, error Message is:

" + json.msg); - } else { - Ext.Msg.alert("Error", "The Chart could not be saved!"); - } + Ext.Ajax.request({ + method: 'GET', + disableCaching: false, + url: url, + success: function(response){ + view.setLoading(false); + var json = Ext.decode(response.responseText); + if (json.success === "true") { + me.getSavedchartsgrid().getStore().load(); + Ext.Msg.alert("Success", "Chart successfully saved!"); + } else if (json.msg) { + Ext.Msg.alert("Error", "The Chart could not be saved, error Message is:

" + json.msg); + } else { + Ext.Msg.alert("Error", "The Chart could not be saved!"); } - }); - } + }, + failure: function() { + view.setLoading(false); + if (json && json.msg) { + Ext.Msg.alert("Error", "The Chart could not be saved, error Message is:

" + json.msg); + } else { + Ext.Msg.alert("Error", "The Chart could not be saved!"); + } + } + }); } }, this); @@ -283,25 +782,88 @@ Ext.define('FHEM.controller.ChartController', { * loading saved chart data and trigger the load of the chart */ loadsavedchart: function(grid, td, cellIndex, record) { - + if (cellIndex === 0) { - var name = record.get('VALUE'); - var chartdata = Ext.decode(record.get('EVENT'))[0]; + var name = record.get('NAME'), + rawchartdata = record.get('VALUE'), + chartdata = Ext.decode(rawchartdata); + + //cleanup the form before loading + this.resetFormFields(); if (chartdata && !Ext.isEmpty(chartdata)) { + this.getDevicecombo().setValue(chartdata.device); // load storedata for readings after device has been selected this.deviceSelected(this.getDevicecombo()); this.getXaxiscombo().setValue(chartdata.x); this.getYaxiscombo().setValue(chartdata.y); - this.getStarttimepicker().setValue(chartdata.starttime); - this.getEndtimepicker().setValue(chartdata.endtime); + + if (chartdata.y2device && !Ext.isEmpty(chartdata.y2device) && chartdata.y2device != "null") { + this.getDevice2combo().setValue(chartdata.y2device); + this.getDevice2combo().show(); + this.getY2axiscombo().setValue(chartdata.y2axis); + this.getY2axiscombo().show(); + this.getY2axiscolorcombo().setValue(chartdata.y2axiscolorcombo); + this.getY2axiscolorcombo().show(); + this.getY2axisfillcheck().setValue(chartdata.y2axisfillcheck); + this.getY2axisfillcheck().show(); + } + if (chartdata.y3device && !Ext.isEmpty(chartdata.y3device) && chartdata.y3device != "null") { + this.getDevice3combo().setValue(chartdata.y3device); + this.getDevice3combo().show(); + this.getY3axiscombo().setValue(chartdata.y3axis); + this.getY3axiscombo().show(); + this.getY3axiscolorcombo().setValue(chartdata.y3axiscolorcombo); + this.getY3axiscolorcombo().show(); + this.getY3axisfillcheck().setValue(chartdata.y3axisfillcheck); + this.getY3axisfillcheck().show(); + } + + if (chartdata.base1start && !Ext.isEmpty(chartdata.base1start) && chartdata.base1start != "null") { + this.getBase1start().setValue(chartdata.base1start); + this.getBase1start().show(); + this.getBase1end().setValue(chartdata.base1end); + this.getBase1end().show(); + this.getBase1color().setValue(chartdata.base1color); + this.getBase1color().show(); + this.getBase1fill().setValue(chartdata.base1fill); + this.getBase1fill().show(); + } + + if (chartdata.base2start && !Ext.isEmpty(chartdata.base2start) && chartdata.base2start != "null") { + this.getBase2start().setValue(chartdata.base2start); + this.getBase2start().show(); + this.getBase2end().setValue(chartdata.base2end); + this.getBase2end().show(); + this.getBase2color().setValue(chartdata.base2color); + this.getBase2color().show(); + this.getBase2fill().setValue(chartdata.base2fill); + this.getBase2fill().show(); + } + + if (chartdata.base3start && !Ext.isEmpty(chartdata.base3start) && chartdata.base3start != "null") { + this.getBase3start().setValue(chartdata.base3start); + this.getBase3start().show(); + this.getBase3end().setValue(chartdata.base3end); + this.getBase3end().show(); + this.getBase3color().setValue(chartdata.base3color); + this.getBase3color().show(); + this.getBase3fill().setValue(chartdata.base3fill); + this.getBase3fill().show(); + } + + //convert time + var start = chartdata.starttime.replace("_", " "), + end = chartdata.endtime.replace("_", " "); + this.getStarttimepicker().setValue(start); + this.getEndtimepicker().setValue(end); this.requestChartData(); this.getLinechartpanel().setTitle(name); } else { - Ext.Msg.alert("Error", "The Chart could not be loaded!"); + Ext.Msg.alert("Error", "The Chart could not be loaded! RawChartdata was:
" + rawchartdata); } } @@ -313,10 +875,10 @@ Ext.define('FHEM.controller.ChartController', { deletechart: function(grid, td, cellIndex, par, evt, record) { var me = this, - chartname = record.get('VALUE'), + chartid = record.get('ID'), view = this.getLinechartview(); - if (Ext.isDefined(chartname) && chartname !== "") { + if (Ext.isDefined(chartid) && chartid !== "") { Ext.create('Ext.window.Window', { width: 250, @@ -333,7 +895,7 @@ Ext.define('FHEM.controller.ChartController', { text: "Ok", handler: function(btn){ - var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""+deletechart+""+""+' + chartname + '&XHR=1'; + var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""+deletechart+""+""+' + chartid + '&XHR=1'; view.setLoading(true); @@ -344,7 +906,7 @@ Ext.define('FHEM.controller.ChartController', { success: function(response){ view.setLoading(false); var json = Ext.decode(response.responseText); - if (json && json.success === true) { + if (json && json.success === "true") { me.getSavedchartsgrid().getStore().load(); Ext.Msg.alert("Success", "Chart successfully deleted!"); } else if (json && json.msg) { @@ -373,8 +935,10 @@ Ext.define('FHEM.controller.ChartController', { } }] }).show(); + } else { + Ext.Msg.alert("Error", "The Chart could not be deleted, no record id has been found"); } - } + } }); \ No newline at end of file diff --git a/fhem/www/frontend/www/frontend/app/controller/MainController.js b/fhem/www/frontend/www/frontend/app/controller/MainController.js index 23c0d71a0..924096713 100644 --- a/fhem/www/frontend/www/frontend/app/controller/MainController.js +++ b/fhem/www/frontend/www/frontend/app/controller/MainController.js @@ -72,9 +72,8 @@ Ext.define('FHEM.controller.MainController', { * */ showDatabaseTablePanel: function() { - //TODO: use this when new dblog module is deployed - //Ext.ComponentQuery.query('panel[name=linechartpanel]')[0].hide(); - //Ext.ComponentQuery.query('panel[name=tabledatagridpanel]')[0].show(); + Ext.ComponentQuery.query('panel[name=linechartpanel]')[0].hide(); + Ext.ComponentQuery.query('panel[name=tabledatagridpanel]')[0].show(); } }); \ No newline at end of file diff --git a/fhem/www/frontend/www/frontend/app/model/ChartModel.js b/fhem/www/frontend/www/frontend/app/model/ChartModel.js index d1c618b56..9d955bcbb 100644 --- a/fhem/www/frontend/www/frontend/app/model/ChartModel.js +++ b/fhem/www/frontend/www/frontend/app/model/ChartModel.js @@ -8,9 +8,26 @@ Ext.define('FHEM.model.ChartModel', { name: 'TIMESTAMP', type: 'date', dateFormat: "Y-m-d H:i:s" - },{ + }, + { + name: 'TIMESTAMP2', + type: 'date', + dateFormat: "Y-m-d H:i:s" + }, + { + name: 'TIMESTAMP3', + type: 'date', + dateFormat: "Y-m-d H:i:s" + }, + { name: 'VALUE', type: 'float' + },{ + name: 'VALUE2', + type: 'float' + },{ + name: 'VALUE3', + type: 'float' } ] }); \ No newline at end of file diff --git a/fhem/www/frontend/www/frontend/app/model/SavedChartsModel.js b/fhem/www/frontend/www/frontend/app/model/SavedChartsModel.js index 8d4435802..84f4cff2e 100644 --- a/fhem/www/frontend/www/frontend/app/model/SavedChartsModel.js +++ b/fhem/www/frontend/www/frontend/app/model/SavedChartsModel.js @@ -5,10 +5,14 @@ Ext.define('FHEM.model.SavedChartsModel', { extend: 'Ext.data.Model', fields: [ { - name: 'VALUE', + name: 'ID', + type: 'number' + }, + { + name: 'NAME', type: 'text' },{ - name: 'EVENT', + name: 'VALUE', type: 'text' } ] diff --git a/fhem/www/frontend/www/frontend/app/store/TableDataStore.js b/fhem/www/frontend/www/frontend/app/store/TableDataStore.js index 6d38010ab..9ec0224fb 100644 --- a/fhem/www/frontend/www/frontend/app/store/TableDataStore.js +++ b/fhem/www/frontend/www/frontend/app/store/TableDataStore.js @@ -12,7 +12,7 @@ Ext.define('FHEM.store.TableDataStore', { proxy: { type: 'ajax', method: 'POST', - url: '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""+getTableData+""+""+""+0+100&XHR=1', + url: '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""+getTableData+""+""+""+""+0+100&XHR=1', reader: { type: 'json', root: 'data', @@ -24,7 +24,7 @@ Ext.define('FHEM.store.TableDataStore', { beforeprefetch: function(store, operation) { //override stores url to contain start and limit params in our needed notation store.proxy.url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""'; - store.proxy.url += '+getTableData+""+""+""+' + operation.start +'+' + operation.limit +'&XHR=1'; + store.proxy.url += '+getTableData+""+""+""+""+' + operation.start +'+' + operation.limit +'&XHR=1'; } } }); \ No newline at end of file diff --git a/fhem/www/frontend/www/frontend/app/view/LineChartPanel.js b/fhem/www/frontend/www/frontend/app/view/LineChartPanel.js index eaf32cb27..a640a749b 100644 --- a/fhem/www/frontend/www/frontend/app/view/LineChartPanel.js +++ b/fhem/www/frontend/www/frontend/app/view/LineChartPanel.js @@ -4,7 +4,6 @@ Ext.define('FHEM.view.LineChartPanel', { extend: 'Ext.panel.Panel', alias : 'widget.linechartpanel', - xtype : 'chart', requires: [ 'FHEM.view.LineChartView', 'FHEM.store.ChartStore' @@ -34,7 +33,21 @@ Ext.define('FHEM.view.LineChartPanel', { ] }); + me.comboColorStore = Ext.create('Ext.data.Store', { + fields: ['name', 'value'], + data : [ + {'name':'Blue','value':'#2F40FA'}, + {'name':'Green', 'value':'#46E01B'}, + {'name':'Orange','value':'#F0A800'}, + {'name':'Red','value':'#E0321B'}, + {'name':'Yellow','value':'#F5ED16'} + ] + }); + me.comboDeviceStore = Ext.create('FHEM.store.DeviceStore'); + me.comboDevice2Store = Ext.create('FHEM.store.DeviceStore'); + me.comboDevice3Store = Ext.create('FHEM.store.DeviceStore'); + me.comboDeviceStore.on("load", function(store, e, success) { if(!success) { Ext.Msg.alert("Error", "Connection to database failed! Check your configuration."); @@ -42,86 +55,429 @@ Ext.define('FHEM.view.LineChartPanel', { }); me.comboReadingsStore = Ext.create('FHEM.store.ReadingsStore'); + me.comboReadings2Store = Ext.create('FHEM.store.ReadingsStore'); + me.comboReadings3Store = Ext.create('FHEM.store.ReadingsStore'); - me.dockedItems = [{ - xtype: 'toolbar', - dock: 'top', - layout: 'column', - minheight: 60, - maxHeight: 90, + var chartSettingPanel = Ext.create('Ext.form.Panel', { + title: 'Chart Settings - Click me to edit', + name: 'chartformpanel', + maxHeight: 285, + autoScroll: true, + collapsible: true, + titleCollapse: true, + listeners: { + collapse: me.layoutChart, + expand: me.layoutChart + }, items: [ - { - xtype: 'combobox', - name: 'devicecombo', - fieldLabel: 'Select Device', - store: me.comboDeviceStore, - displayField: 'DEVICE', - valueField: 'DEVICE' - }, - { - xtype: 'combobox', - name: 'xaxiscombo', - fieldLabel: 'Select X Axis', - store: me.comboAxesStore, - displayField: 'name', - valueField: 'name' - }, - { - xtype: 'combobox', - name: 'yaxiscombo', - fieldLabel: 'Select Y Axis', - store: me.comboReadingsStore, - displayField: 'READING', - valueField: 'READING' + { + xtype: 'fieldset', + layout: 'column', + title: 'Select data', + defaults: { + margin: '0 10 10 10' + }, + items: [ + { + xtype: 'combobox', + name: 'devicecombo', + fieldLabel: 'Select Device', + labelWidth: 90, + store: me.comboDeviceStore, + displayField: 'DEVICE', + valueField: 'DEVICE' + }, + { + xtype: 'combobox', + name: 'xaxiscombo', + fieldLabel: 'Select X Axis', + labelWidth: 90, + inputWidth: 100, + store: me.comboAxesStore, + displayField: 'name', + valueField: 'name' + }, + { + xtype: 'combobox', + name: 'yaxiscombo', + fieldLabel: 'Select Y-Axis', + labelWidth: 90, + inputWidth: 110, + store: me.comboReadingsStore, + displayField: 'READING', + valueField: 'READING' + }, + { + xtype: 'combobox', + name: 'yaxiscolorcombo', + fieldLabel: 'Y-Color', + labelWidth: 50, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(0) + }, + { + xtype: 'checkboxfield', + name: 'yaxisfillcheck', + boxLabel: 'Fill' + }, + { + xtype: 'combobox', + name: 'device2combo', + fieldLabel: 'Select 2. Device', + labelWidth: 100, + store: me.comboDevice2Store, + displayField: 'DEVICE', + valueField: 'DEVICE', + hidden: true + }, + { + xtype: 'combobox', + name: 'y2axiscombo', + fieldLabel: 'Y2', + labelWidth: 20, + store: me.comboReadings2Store, + displayField: 'READING', + valueField: 'READING', + hidden: true + }, + { + xtype: 'combobox', + name: 'y2axiscolorcombo', + fieldLabel: 'Y2-Color', + labelWidth: 60, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(1), + hidden: true + }, + { + xtype: 'checkboxfield', + name: 'y2axisfillcheck', + boxLabel: 'Fill', + hidden: true + }, + { + xtype: 'combobox', + name: 'device3combo', + fieldLabel: 'Select 3. Device', + labelWidth: 100, + store: me.comboDevice3Store, + displayField: 'DEVICE', + valueField: 'DEVICE', + hidden: true + }, + { + xtype: 'combobox', + name: 'y3axiscombo', + fieldLabel: 'Y3', + labelWidth: 20, + store: me.comboReadings3Store, + displayField: 'READING', + valueField: 'READING', + hidden: true + }, + { + xtype: 'combobox', + name: 'y3axiscolorcombo', + fieldLabel: 'Y3-Color', + labelWidth: 60, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(2), + hidden: true + }, + { + xtype: 'checkboxfield', + name: 'y3axisfillcheck', + boxLabel: 'Fill', + hidden: true + }, + { + xtype: 'button', + width: 110, + text: 'Add another Y-Axis', + name: 'addyaxisbtn', + handler: function(btn) { + var y2device = btn.up().down('combobox[name=device2combo]'); + var y2 = btn.up().down('combobox[name=y2axiscombo]'); + var y2color = btn.up().down('combobox[name=y2axiscolorcombo]'); + var y2fill = btn.up().down('checkboxfield[name=y2axisfillcheck]'); + + var y3device = btn.up().down('combobox[name=device3combo]'); + var y3 = btn.up().down('combobox[name=y3axiscombo]'); + var y3color = btn.up().down('combobox[name=y3axiscolorcombo]'); + var y3fill = btn.up().down('checkboxfield[name=y3axisfillcheck]'); + + if (y2.hidden) { + y2device.show(); + y2.show(); + y2color.show(); + y2fill.show(); + } else if (y3.hidden) { + y3device.show(); + y3.show(); + y3color.show(); + y3fill.show(); + btn.setDisabled(true); + } + } + }, + { + xtype: 'numberfield', + fieldLabel: 'Startvalue', + name: 'base1start', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'numberfield', + fieldLabel: 'Endvalue', + name: 'base1end', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'combobox', + name: 'baseline1colorcombo', + fieldLabel: 'Baseline 1 Color', + labelWidth: 100, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(0), + hidden: true + }, + { + xtype: 'checkboxfield', + name: 'baseline1fillcheck', + boxLabel: 'Fill', + hidden: true + }, + { + xtype: 'numberfield', + fieldLabel: 'Startvalue', + name: 'base2start', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'numberfield', + fieldLabel: 'Endvalue', + name: 'base2end', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'combobox', + name: 'baseline2colorcombo', + fieldLabel: 'Baseline 2 Color', + labelWidth: 100, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(1), + hidden: true + }, + { + xtype: 'checkboxfield', + name: 'baseline2fillcheck', + boxLabel: 'Fill', + hidden: true + }, + { + xtype: 'numberfield', + fieldLabel: 'Startvalue', + name: 'base3start', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'numberfield', + fieldLabel: 'Endvalue', + name: 'base3end', + allowBlank: false, + labelWidth: 60, + width: 120, + hidden: true + }, + { + xtype: 'combobox', + name: 'baseline3colorcombo', + fieldLabel: 'Baseline 3 Color', + labelWidth: 100, + inputWidth: 70, + store: me.comboColorStore, + displayField: 'name', + valueField: 'value', + value: me.comboColorStore.getAt(2), + hidden: true + }, + { + xtype: 'checkboxfield', + name: 'baseline3fillcheck', + boxLabel: 'Fill', + hidden: true + }, + { + xtype: 'button', + width: 110, + text: 'Add Baseline', + name: 'addbaselinebtn', + handler: function(btn) { + var b1start = btn.up().down('numberfield[name=base1start]'); + var b1end = btn.up().down('numberfield[name=base1end]'); + var b1color = btn.up().down('combobox[name=baseline1colorcombo]'); + var b1fill = btn.up().down('checkboxfield[name=baseline1fillcheck]'); + var b2start = btn.up().down('numberfield[name=base2start]'); + var b2end = btn.up().down('numberfield[name=base2end]'); + var b2color = btn.up().down('combobox[name=baseline2colorcombo]'); + var b2fill = btn.up().down('checkboxfield[name=baseline2fillcheck]'); + var b3start = btn.up().down('numberfield[name=base3start]'); + var b3end = btn.up().down('numberfield[name=base3end]'); + var b3color = btn.up().down('combobox[name=baseline3colorcombo]'); + var b3fill = btn.up().down('checkboxfield[name=baseline3fillcheck]'); + + if (b1start.hidden) { + b1start.show(); + b1end.show(); + b1color.show(); + b1fill.show(); + } else if (b2start.hidden) { + b2start.show(); + b2end.show(); + b2color.show(); + b2fill.show(); + } else if (b3start.hidden) { + b3start.show(); + b3end.show(); + b3color.show(); + b3fill.show(); + btn.setDisabled(true); + } + + } + } + ] }, { - xtype: 'datefield', - name: 'starttimepicker', - format: 'Y-m-d H:i:s', - fieldLabel: 'Select Starttime' - }, + xtype: 'fieldset', + layout: 'column', + title: 'Select Timerange', + defaults: { + margin: '0 0 0 10' + }, + items: [ + { + xtype: 'datefield', + name: 'starttimepicker', + format: 'Y-m-d H:i:s', + fieldLabel: 'Select Starttime', + labelWidth: 90 + }, + { + xtype: 'datefield', + name: 'endtimepicker', + format: 'Y-m-d H:i:s', + fieldLabel: 'Select Endtime', + labelWidth: 90 + } + ] + }, { - xtype: 'datefield', - name: 'endtimepicker', - format: 'Y-m-d H:i:s', - fieldLabel: 'Select Endtime' - }, - { - xtype: 'button', - width: 100, - text: 'Show Chart', - name: 'requestchartdata' - }, - { - xtype: 'button', - width: 100, - text: 'Save Chart', - name: 'savechartdata' - }, - { - xtype: 'button', - width: 100, - text: 'Step back', - name: 'stepback' - }, - { - xtype: 'button', - width: 100, - text: 'Step forward', - name: 'stepforward' + xtype: 'fieldset', + layout: 'column', + defaults: { + margin: '0 0 0 10' + }, + items: [ + { + xtype: 'button', + width: 100, + text: 'Show Chart', + name: 'requestchartdata' + }, + { + xtype: 'button', + width: 100, + text: 'Save Chart', + name: 'savechartdata' + }, + { + xtype: 'button', + width: 100, + text: 'Reset Fields', + name: 'resetchartform' + }, + { + xtype: 'button', + width: 100, + text: 'Step back', + name: 'stepback' + }, + { + xtype: 'button', + width: 100, + text: 'Step forward', + name: 'stepforward' + } + ] } ] - }]; + }); - me.items = [ + var linechartview = Ext.create('Ext.panel.Panel', { + title: 'Chart', + autoScroll: true, + collapsible: true, + titleCollapse: true, + items: [ { - xtype: 'linechartview', - width: '100%' - } + xtype: 'linechartview' + } + ] + }); + + me.items = [ + chartSettingPanel, + linechartview ]; me.callParent(arguments); + me.on("resize", me.layoutChart); + + }, + + /** + * helper function to relayout the chartview dependent on free space + */ + layoutChart: function() { + var lcp = Ext.ComponentQuery.query('linechartpanel')[0]; + var lcv = Ext.ComponentQuery.query('linechartview')[0]; + var cfp = Ext.ComponentQuery.query('form[name=chartformpanel]')[0]; + var chartheight = lcp.getHeight() - cfp.getHeight() - 85; + var chartwidth = lcp.getWidth() - 25; + lcv.setHeight(chartheight); + lcv.setWidth(chartwidth); } }); diff --git a/fhem/www/frontend/www/frontend/app/view/LineChartView.js b/fhem/www/frontend/www/frontend/app/view/LineChartView.js index 6999dda71..06d2ca2b1 100644 --- a/fhem/www/frontend/www/frontend/app/view/LineChartView.js +++ b/fhem/www/frontend/www/frontend/app/view/LineChartView.js @@ -6,57 +6,42 @@ Ext.define('FHEM.view.LineChartView', { alias : 'widget.linechartview', xtype : 'chart', requires : [ 'FHEM.store.ChartStore' ], - style : 'background:#fff', animate : true, - shadow : true, - theme : 'Category1', + legend: { + position: 'right' + }, initComponent : function() { var me = this; me.store = Ext.create('FHEM.store.ChartStore'); - me.axes = [ { - type : 'Numeric', - name : 'yaxe', - position : 'left', - fields : [ 'VALUE' ], - title : 'kW / h', - grid : { - odd : { - opacity : 1, - fill : '#ddd', - stroke : '#bbb', - 'stroke-width' : 0.5 + me.axes = [ + { + type : 'Numeric', + name : 'yaxe', + position : 'left', + fields : [ 'VALUE', 'VALUE2', 'VALUE3', 'VALUEBASE1', 'VALUEBASE2', 'VALUEBASE3' ], + title : 'VALUE', + grid : { + odd : { + opacity : 1, + fill : '#ddd', + stroke : '#bbb', + 'stroke-width' : 0.5 + } } - } - }, { - type : 'Time', - name : 'xaxe', - position : 'bottom', - fields : [ 'TIMESTAMP' ], - dateFormat : "Y-m-d H:i:s", - minorTickSteps : 12, - title : 'Time' - } ]; + }, + { + type : 'Time', + name : 'xaxe', + position : 'bottom', + fields : [ 'TIMESTAMP' ], + dateFormat : "Y-m-d H:i:s", + minorTickSteps : 12, + title : 'Time' + } ]; - me.series = [ { - type : 'line', - axis : 'left', - xField : 'TIMESTAMP', - yField : 'VALUE', - smooth: 2, - fill: true, - highlight: true, - tips : { - trackMouse : true, - width : 140, - height : 100, - renderer : function(storeItem, item) { - this.setTitle(' Value: : ' + storeItem.get('VALUE') + - '
Time: ' + storeItem.get('TIMESTAMP')); - } - } - } ]; + me.series = null; me.callParent(arguments); diff --git a/fhem/www/frontend/www/frontend/app/view/Viewport.js b/fhem/www/frontend/www/frontend/app/view/Viewport.js index c3fc02bce..830199f0c 100644 --- a/fhem/www/frontend/www/frontend/app/view/Viewport.js +++ b/fhem/www/frontend/www/frontend/app/view/Viewport.js @@ -45,7 +45,7 @@ Ext.define('FHEM.view.Viewport', { columns: [ { header: 'Saved Charts', - dataIndex: 'VALUE', + dataIndex: 'NAME', width: '80%' }, {