diff --git a/lib/http_server.pl b/lib/http_server.pl index ce64ee87e..6a6f35875 100644 --- a/lib/http_server.pl +++ b/lib/http_server.pl @@ -684,6 +684,11 @@ sub http_process_request { # print "Error, no SET argument: $header\n" unless $get_arg; + #allow setby to be passed in URL. Objects can then take a setby argument if there is an alternative action. + #used for RGB. Downside is that the true setby (web) would be lost. + my $get_arg_setby = ""; + ($get_arg_setby) = $get_arg =~ /select_setby=(\S+)/; + $get_arg =~ s/select_setby=(\S+)// if ($get_arg_setby); # Change select_item=$item&select_state=abc to $item=abc $get_arg =~ s/select_item=(\S+)\&&select_state=/$1=/; @@ -758,10 +763,10 @@ sub http_process_request { # Can be a scalar or a object $state =~ tr/\"/\'/; # So we can use "" to quote it - + $get_arg_setby = "web [$client_ip_address]" unless $get_arg_setby; # my $eval_cmd = qq[($item and ref($item) and UNIVERSAL::isa($item, 'Generic_Item')) ? my $eval_cmd = qq[($item and ref($item) ne '' and ref($item) ne 'SCALAR' and $item->can('set')) ? - ($item->set("$state", "web [$client_ip_address]")) : ($item = "$state")]; + ($item->set("$state", "$get_arg_setby")) : ($item = "$state")]; print "SET eval: $eval_cmd\n" if $main::Debug{http}; eval $eval_cmd; print "SET eval error. cmd=$eval_cmd error=$@\n" if $@; diff --git a/lib/json_server.pl b/lib/json_server.pl index fa3e1c1e5..79b747630 100755 --- a/lib/json_server.pl +++ b/lib/json_server.pl @@ -883,34 +883,52 @@ sub json_get { } if ( $path[0] eq 'security' ) { - #check if $Authorized if (defined $path[1] and $path[1] eq 'authorize') { - print "IN AUTHORIZE\n"; + # Passwords are stored as MD5 hashes in the user data file + # Take that MD5, then take the current date (in YYYYDDMM format) and then calculate + # an authorization MD5 value. Adding in the current date means that the lifespan of a compromised + # password token is at most 1 day. my $status = ""; if ($args{user} && $args{user}[0] eq "") { - $status = "Empty Username"; + $status = "fail"; + &main::print_log("json_server.pl: ERROR, authorize attempt with no username"); } elsif ($args{password} && $args{password}[0] eq "") { - $status = "Empty Password"; + $status = "fail"; + &main::print_log("json_server.pl: ERROR, authorize attempt with no password"); + } else { my $password = &Groups('getpw','',$args{user}[0]); my $time_seed = &main::time_date_stamp('18',$Time); - my $time_seedY = &main::time_date_stamp('18',$Time - 86400); - my $time_seedT = &main::time_date_stamp('18',$Time + 86400); - + #to account for clock drift, check today and tomorrow values around midnight #if time is between 11:55 and midnight then also check tomorrow #if time is between midnight and 00:05 then also check yesterday - print "PW=$password, time_seed=$time_seed, $time_seedY, $time_seedT\n"; + if (time_greater_than("11:55 PM")) { + my $time_seedT = &main::time_date_stamp('18',$Time + 86400); + my $pwdcheck1 = md5_hex($password . $time_seedT); + $status = "success" if (lc $args{password}[0] eq lc $pwdcheck1); + } + if (time_less_than("00:05 AM")) { + my $time_seedY = &main::time_date_stamp('18',$Time - 86400); + my $pwdcheck2 = md5_hex($password . $time_seedY); + $status = "success" if (lc $args{password}[0] eq lc $pwdcheck2); + } + #print "PW=$password, time_seed=$time_seed"; my $pwdcheck = md5_hex($password . $time_seed); - print "PWC=$pwdcheck\n"; + #print "PWC=$pwdcheck\n"; - if (lc $args{password}[0] eq lc $pwdcheck) { + if ($status eq "" and (lc $args{password}[0] eq lc $pwdcheck)) { $status = "success"; + &main::print_log("json_server.pl: INFO, user $args{user}[0] successfully authenticated"); + } else { $status = "fail"; + &main::print_log("json_server.pl: WARNING, user $args{user}[0] authentication attempt failed"); + } } $json_data{security}->{authorize} = $status; } else { + #check if $Authorized my $ref; my $users; my $found = 0; @@ -1508,7 +1526,7 @@ sub json_object_detail { my %json_complete_object; my @f = qw( category filename measurement rf_id set_by members state states state_log type label sort_order groups hidden parents schedule logger_status - idle_time text html seconds_remaining fp_location fp_icons fp_icon_set img link level); + idle_time text html seconds_remaining fp_location fp_icons fp_icon_set img link level rgb); # Build list of fields based on those requested. foreach my $f ( sort @f ) { @@ -1551,6 +1569,14 @@ sub json_object_detail { $value = $a if ( defined $a and $a ne "" ); #don't return a null value } + elsif ( $f eq 'rgb' ) { + my ($a,$b,$c) = $object->$method; + + $value = "$a,$b,$c" if (( defined $a and $a ne "" ) #don't return a null value + and ( defined $b and $b ne "" ) + and ( defined $c and $c ne "" )); + } + #if ( $f eq 'hidden' ) { # my $a = $object->$method; # if ($a == 1 or $a eq "1") { diff --git a/web/ia7/house/modes.shtml b/web/ia7/house/modes.shtml index 376b763e6..b250f4e31 100644 --- a/web/ia7/house/modes.shtml +++ b/web/ia7/house/modes.shtml @@ -4,7 +4,7 @@
-
@@ -12,14 +12,14 @@
-
-
@@ -29,7 +29,7 @@
-
diff --git a/web/ia7/house/sample.shtml b/web/ia7/house/sample.shtml index 4e4fa57e8..2632e508a 100644 --- a/web/ia7/house/sample.shtml +++ b/web/ia7/house/sample.shtml @@ -15,7 +15,7 @@
-
@@ -23,7 +23,7 @@
-
diff --git a/web/ia7/include/javascript.js b/web/ia7/include/javascript.js index 719fc8088..55035d5e4 100644 --- a/web/ia7/include/javascript.js +++ b/web/ia7/include/javascript.js @@ -1,5 +1,5 @@ -var ia7_ver = "v2.0.690"; +var ia7_ver = "v2.0.751"; var coll_ver = ""; var entity_store = {}; //global storage of entities var json_store = {}; @@ -1058,7 +1058,7 @@ var loadList = function() { var button_text = ''; var button_html = ''; var entity_arr = []; - URLHash.fields = "category,label,sort_order,members,state,states,state_log,hidden,type,text,schedule,logger_status,link"; + URLHash.fields = "category,label,sort_order,members,state,states,state_log,hidden,type,text,schedule,logger_status,link,rgb"; $.ajax({ type: "GET", url: "/json/"+HashtoJSONArgs(URLHash), @@ -1171,6 +1171,13 @@ var loadList = function() { if (json_store.ia7_config.prefs.always_double_buttons == "yes") { if (name.length < 30) dbl_btn = "
"; } + var btn_rgb = ""; + if (json_store.objects[entity].rgb !== undefined) { + //TODO fix + btn_rgb = ''; + btn_rgb += ''; + + } // direct control item, differentiate the button var btn_direct = ""; if (json_store.ia7_config.objects !== undefined && json_store.ia7_config.objects[entity] !== undefined) { @@ -1180,7 +1187,7 @@ var loadList = function() { } button_html = "
"; + button_html += name+btn_rgb+dbl_btn+""+json_store.objects[entity].state+"
"; entity_arr.push(button_html); } }//entity each loop @@ -1418,7 +1425,7 @@ var sortArrayByArray = function (listArray, sortArray){ //Used to dynamically update the state of objects var updateList = function(path) { var URLHash = URLToHash(); - URLHash.fields = "state,state_log,schedule,logger_status,type"; + URLHash.fields = "state,state_log,schedule,logger_status,type,rgb"; URLHash.long_poll = 'true'; URLHash.time = json_store.meta.time; if (updateSocket !== undefined && updateSocket.readyState != 4){ @@ -1447,8 +1454,12 @@ var updateList = function(path) { } else { color = getButtonColor(json.data[entity].state); } - $('button[entity="'+entity+'"]').find('.pull-right').text( - json.data[entity].state); + var btn_rgb = ""; + if (json.data[entity].rgb !== undefined) { + $('button[entity="'+entity+'"]').find('.object-color').css("color",'rgb('+json.data[entity].rgb+')'); + console.log("changing color to "+json.data[entity].rgb); + } + $('button[entity="'+entity+'"]').find('.object-state').text(json.data[entity].state); $('button[entity="'+entity+'"]').removeClass("btn-default"); $('button[entity="'+entity+'"]').removeClass("btn-success"); $('button[entity="'+entity+'"]').removeClass("btn-warning"); @@ -1489,7 +1500,7 @@ var updateItem = function(item,link,time) { time = ""; } var path_str = "/objects" // override, for now, would be good to add voice_cmds - var arg_str = "fields=state,states,label,state_log,schedule,logger_status&long_poll=true&items="+item+"&time="+time; + var arg_str = "fields=state,states,label,state_log,schedule,logger_status,rgb&long_poll=true&items="+item+"&time="+time; $.ajax({ type: "GET", url: "/LONG_POLL?json('GET','"+path_str+"','"+arg_str+"')", @@ -1501,7 +1512,8 @@ var updateItem = function(item,link,time) { JSONStore(json); requestTime = json_store.meta.time; var color = getButtonColor(json.data[item].state); - $('button[entity="'+item+'"]').find('.pull-right').text( +//TODO object-state to all buttons! + $('button[entity="'+item+'"]').find('.object-state').text( json.data[item].state); $('button[entity="'+item+'"]').removeClass("btn-default"); $('button[entity="'+item+'"]').removeClass("btn-success"); @@ -1567,7 +1579,7 @@ var updateStaticPage = function(link,time) { if ($(this).attr('entity') != '' && json.data[$(this).attr('entity')] != undefined ) { //need an entity item for this to work. entity = $(this).attr('entity'); var color = getButtonColor(json.data[entity].state); - $('button[entity="'+entity+'"]').find('.pull-right').text(json.data[entity].state); + $('button[entity="'+entity+'"]').find('.object-state').text(json.data[entity].state); $('button[entity="'+entity+'"]').removeClass("btn-default"); $('button[entity="'+entity+'"]').removeClass("btn-success"); $('button[entity="'+entity+'"]').removeClass("btn-warning"); @@ -1744,7 +1756,7 @@ var loadCollection = function(collection_keys) { if (name.length < 30) dbl_btn = "
"; var button_html = "
"; + button_html += name+dbl_btn+""+json_store.objects[item].state+""; button_html = "
" + button_html + "
"; entity_arr.push(button_html); items += item+","; @@ -2394,9 +2406,10 @@ var graph_rrd = function(start,group,time) { var new_data = 1; var data_timeout = 0; var refresh = 60; //refresh data every 60 seconds by default - - if (!$('#rrd-graph').is(':visible')) { +//TODO Changepage, unless rrd-graph visible then stop refresh counter? + if (!$('#rrd-graph').is(':visible')) { //if (URLHash.path == path){ $('#loader').show(); + console.log("showing loader "+URLHash.path+" : : "+$('#top-graph').length); } if (json_store.ia7_config.prefs.rrd_refresh !== undefined) refresh = json_store.ia7_config.prefs.rrd_refresh; @@ -3569,7 +3582,11 @@ var create_state_modal = function(entity) { var modal_state = json_store.objects[entity].state; - $('#control').find('.object-title').html(name + " - " + json_store.objects[entity].state + ""); + var title = name + " - " + json_store.objects[entity].state + ""; + if (json_store.objects[entity].rgb !== undefined) { + title += ' '; + } + $('#control').find('.object-title').html(title); $('#control').find('.control-dialog').attr("entity", entity); var modal_states = json_store.objects[entity].states; // HP need to have at least 2 states to be a controllable object... @@ -3639,8 +3656,7 @@ var create_state_modal = function(entity) { } var slider_data = sliderDetails(modal_states); $('#control').find('.states').append("
"); - var val = $(".object-state").text().replace(/\%/,''); - + var val = $(".modal-object-state").text().replace(/\%/,''); var position = slider_data.values.indexOf(val); if (val == "on") position = slider_data.max; if (val == "off") position = slider_data.min; @@ -3659,7 +3675,7 @@ var create_state_modal = function(entity) { } else { if (slider_data.pct) sliderstate += "%"; } - $('#control').find('.object-state').text(sliderstate); + $('#control').find('.modal-object-state').text(sliderstate); }); $( "#slider" ).on( "slidechange", function(event, ui) { @@ -3678,7 +3694,49 @@ var create_state_modal = function(entity) { $(".get-status").delay(4000).fadeOut("slow", function () { $(this).remove(); }); }); }); - + if (json_store.objects[entity].rgb !== undefined) { + console.log("Insert RGB Slider Here"); + $('#control').find('.states').append("
"); + $('#control').find('.states').append("
"); + $('#control').find('.states').append("
"); + + $('#sliderR' ).slider({ + min: 0, + max: 255, + value: json_store.objects[entity].rgb.split(',')[0] + }); + $('#sliderG' ).slider({ + min: 0, + max: 255, + value: json_store.objects[entity].rgb.split(',')[1] + }); + $('#sliderB' ).slider({ + min: 0, + max: 255, + value: json_store.objects[entity].rgb.split(',')[2] + }); + $( ".rgb-slider" ).on( "slide", function(event, ui) { + var sliderstate; + if ($(this).hasClass("red-handle")) { + sliderstate = ui.value+","+$('#sliderG').slider("value")+","+$('#sliderB').slider("value"); + } else if ($(this).hasClass("green-handle")) { + sliderstate = $('#sliderR').slider("value")+","+ui.value+","+$('#sliderB').slider("value"); + } else if ($(this).hasClass("blue-handle")) { + sliderstate = $('#sliderR').slider("value")+","+$('#sliderG').slider("value")+","+ui.value; + } + $('.object-color').css("color","rgb("+sliderstate+")"); + }); + $( ".rgb-slider" ).on( "slidechange", function(event, ui) { + var sliderstate = $('#sliderR').slider("value")+","+$('#sliderG').slider("value")+","+$('#sliderB').slider("value"); + var rgb_url= '/SET;none?select_item='+$(this).parents('.control-dialog').attr("entity")+'&select_state='+sliderstate+'&select_setby=rgb'; + console.log("rgb_url="+rgb_url); + $.get(rgb_url).fail(function() { + $(".modal-header").append($("

 Failure: Could not send command to Misterhouse

")); + $(".get-status").delay(4000).fadeOut("slow", function () { $(this).remove(); }); + }); + }); + + } } if (slider_active) { advanced_html = "
"+advanced_html; //this is clunky but showing advanced states is kinda ugly anyways diff --git a/web/ia7/include/jquery.longclick-1.0.min.js b/web/ia7/include/jquery.longclick-1.0.min.js index d12dd595c..c8f53b376 100644 --- a/web/ia7/include/jquery.longclick-1.0.min.js +++ b/web/ia7/include/jquery.longclick-1.0.min.js @@ -1 +1,26 @@ -(function(a){var b={NS:"jquery.longclick-",delay:700};a.fn.mayTriggerLongClicks=function(c){var d=a.extend(b,c);var f;var e;return a(this).on("mousedown touchstart",function(){e=false;f=setTimeout(function(g){e=true;a(g).trigger("longClick")},d.delay,this)}).on("mouseup touchend",function(){clearTimeout(f)}).on("click",function(g){if(e){g.stopImmediatePropagation()}})}})(jQuery); \ No newline at end of file +(function(a) { + var b = { + NS: "jquery.longclick-", + delay: 700 + }; + a.fn.mayTriggerLongClicks = function(c) { + var d = a.extend(b, c); + var f; + var e; + return a(this).on("mousedown touchstart", function() { + e = false; + f = setTimeout(function(g) { + e = true; + a(g).trigger("longClick") + }, d.delay, this) + }).on("mouseup touchend", function() { + clearTimeout(f) + }).on("touchmove", function() { + clearTimeout(f) + }).on("click", function(g) { + if (e) { + g.stopImmediatePropagation() + } + }) + } +})(jQuery); \ No newline at end of file diff --git a/web/ia7/include/tables.css b/web/ia7/include/tables.css index 8b28a5add..eb0a96873 100644 --- a/web/ia7/include/tables.css +++ b/web/ia7/include/tables.css @@ -1,7 +1,5 @@ .table-curved { border-collapse: separate; -} -.table-curved { border: solid #ccc 1px; border-radius: 6px; border-left:0px; @@ -149,4 +147,4 @@ width: 320px; } } - } \ No newline at end of file + } diff --git a/web/ia7/index.shtml b/web/ia7/index.shtml index d06a2ae6f..f22597ec5 100644 --- a/web/ia7/index.shtml +++ b/web/ia7/index.shtml @@ -233,7 +233,6 @@ .sched-dropdown-menu { margin-left: 14px !important; } - .brightness-slider { margin-left: 16px; margin-right: 16px; @@ -249,30 +248,37 @@ width: 32px; margin-left: -16px } + .red-handle .ui-slider-handle { + background: red; + } + .green-handle .ui-slider-handle { + background: green; + } + .blue-handle .ui-slider-handle { + background: blue; + } .popover { min-width: 200px; - } - + } .mh-page-link { padding-left: 30px; } - .mh-wi-text { font-size: 15px; margin-top: 13px; } - .mh-wi-icon { margin-top: 2px; } - #option_collection { margin-top: 5px; } - .whatsnew-panel { margin-right: 15px; } + .fa-rgb-border { + text-shadow: -1px 0 #000, 0 1px #000, 1px 0 #000, 0 -1px #000; + } #loader { position: absolute; @@ -354,7 +360,7 @@