From 61102b2e8536f1f3c04517368a902c07765a1923 Mon Sep 17 00:00:00 2001 From: Eloy Paris Date: Mon, 3 Jun 2013 12:26:53 -0400 Subject: [PATCH 1/7] Fix issues with INSTEON lights in web interface INSTEON lights are treated differently in the web interface versus X10 lights, for example: they get different icons, and cannot be dimmed or brightened. This patch does the following: - Use the same light icons for INSTEON lights that are used for X10 lights. - Allow dimming/brightening of INSTEON lights by clicking on the left or right side of the icon in the web interface (only dimmable light, of course). - Fix a logic error that prevents disabling the button image cache. Ran into this while creating new icons for my web interface. - As an experimental "feature", and a small delay to try to give MH a chance to receive the new state of an INSTEON device changed through the web interface. This will allow the correct state to be displayed when the web page renders again. Disabled by default since it's just a hack (though it works good enough for me). Comments welcome. --- web/bin/button.pl | 43 ++++++++++++++++++++---------- web/bin/button_action.pl | 56 +++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/web/bin/button.pl b/web/bin/button.pl index 2d779d090..9654b07b7 100644 --- a/web/bin/button.pl +++ b/web/bin/button.pl @@ -1,10 +1,15 @@ +# +# Create buttons with JPEG images generated on-the-fly using the GD module. +# +# For text buttons: +# Example: +# +# For item buttons: +# Example: +# $^W = 0; # Avoid redefined sub msgs -# Create jpeg buttons on-the-fly with GD module -# For text buttons: -# For item buttons: - # Authority: anyone my ($text, $type, $state, $bg_color, $file_name_only) = @ARGV; @@ -29,10 +34,14 @@ $image_file =~ s/ /_/g; # Blanks in file names are nasty $image_file = "/cache/$image_file.jpg"; - +# Set to 1 if you'd like to disable the image cache. Normally you should +# not need to do this because it affects performance (MisterHouse needs +# to re-generate the button image every time). This is only useful if you +# are tweaking your button images and need new images re-generated every +# time the button generation script (this script) is called. my $nocache = 0; -#$nocache = 1; -if (-e "$config_parms{data_dir}$image_file" or $nocache) { + +if (-e "$config_parms{data_dir}$image_file" && !$nocache) { return $image_file if $file_name_only; # print "Returning data from: $image_file\n"; my $data = file_read "$config_parms{data_dir}$image_file"; @@ -41,21 +50,27 @@ # Look for an icon my ($icon, $light); + if ($type eq 'item') { my $object = &get_object_by_name($text); ($icon) = &http_get_local_file(&html_find_icon_image($object, 'voice')); -# $light = 1 if $text =~ /light/i or $text =~ /lite/i; - $light = 1 if $object->isa('X10_Item') and !$object->isa('X10_Appliance'); - $light = 1 if $object->isa('EIB2_Item'); -} -else { -# Uncomment this to put in images into group, category icons. Seem too small to be useful. -# ($icon) = &http_get_local_file(&html_find_icon_image($text, 'text')); + + if ( ($object->isa('X10_Item') and !$object->isa('X10_Appliance') ) + || $object->isa('Insteon::BaseLight') + || $object->isa('EIB2_Item') + || $text =~ /light|lite/i) { + $light = 1; + } +} else { + # Uncomment this to put in images into group, category icons. Seem too small to be useful. + #($icon) = &http_get_local_file(&html_find_icon_image($text, 'text')); } + undef $icon if $icon and $icon !~ /.jpg$/i; # GD does not do gifs :( my $image_icon = GD::Image->newFromJpeg($icon) if $icon; my $image; + if ($image_icon or $type eq 'item') { # Template = blank_on/off/unk or blank_light_on/off/dim diff --git a/web/bin/button_action.pl b/web/bin/button_action.pl index 4c6fddf78..3d635755c 100644 --- a/web/bin/button_action.pl +++ b/web/bin/button_action.pl @@ -10,30 +10,62 @@ my ($state, $x, $y) = $state_xy =~ /(\S+)\?(\d+),(\d+)/; #print "db ln=$list_name, i=$item, s=$state_xy xy=$x,$y\n"; - # Do not dim the dishwasher :) -unless (eval qq|$item->isa('X10_Appliance') or $item->isa('Fan_Motor') or $item->isa('Insteon_Device')|) { - $state = 'dim' if $x < 40; # Left side of image - $state = 'brighten' if $x > 110; # Right side of image -} +my $object = &get_object_by_name($item); + +if ($object->isa('X10_Item') && !$object->isa('X10_Appliance') ) { + # Do not dim the dishwasher :) -if (eval qq|$item->isa('EIB7_Item')|) { # Motor/drive states are stop/up/down + # Dim if clicked on left side of image, brighten if clicked on right + # side of image, or use state passed through the button URL if clicked + # in the center of the image. + if ($x < 40) { + $state = 'dim'; + } elsif ($x > 110) { + $state = 'brighten'; + } +} elsif ($object->isa('EIB7_Item') ) { # Motor/drive states are stop/up/down $state = 'stop'; $state = 'down' if $x < 40; # Left side of image $state = 'up' if $x > 110; # Right side of image -} +} elsif ($object->isa('Insteon::DimmableLight') ) { + my @states = $object->get_states(); + my $curr_state = $object->state(); -#if (eval qq|$item->isa('Insteon_Device'|) { -# $state = "toggle"; -#} + # Find the index into @states for the element that corresponds to the + # current state. + my ($index) = grep { $states[$_] eq $curr_state } 0..$#states; -eval qq|$item->set("$state", 'web')|; -print "button_action.pl eval error: $@\n" if $@; + # Dim if clicked on left side of image, brighten if clicked on right + # side of image, or use state passed through the button URL if clicked + # in the center of the image. + if ($x < 40) { + $index-- if ($index); # Can't dim if light is off + $state = $states[$index]; + } elsif ($x > 110) { + $index++ if ($index != $#states); # Can't brighten if light is fully on + $state = $states[$index]; + } +} + +$object->set("$state", 'web'); # print "dbx4a i=$item s=$state\n"; # my $object = &get_object_by_name($item); # $state = $$object{state}; # print "dbx4b i=$item s=$state\n"; +# Internal state of INSTEON devices does not change immediately after +# clicking on the button. That is because, unlike X10 devices (for example), +# an acknowledgement from the INSTEON device needs to be received so MH +# can change the internal state. If we finish the HTTP transaction before +# the acknowledge comes back then the resulting HTML page will display the +# object that was just clicked on in the old state. This delay here prevents +# this problem at the expense of, well, an extra delay. As this is +# experimental, and this delay causes MH to pause for the duration of the +# delay, this is currently disabled by default. But feel free to enable +# to see if things improve. +#sleep(1); my $h = &referer("/bin/list_buttons.pl?$list_name"); + return &http_redirect($h); From 235bc5c01e8835e8c5fef9979472e91e1c19668a Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Sun, 30 Jun 2013 11:44:28 -0500 Subject: [PATCH 2/7] Updated Insteon state definition for new Insteon object model Moved responsibility for Insteon states out of http_server.pl and into the Insteon objects themselves. (i.e. Insteon::DimmableLight) The existing code was not working with the new object model. This is an additional fix for Issue #212. Open Issue: http_server.pl->button_action() probably needs to be modified so Insteon lights dim/brighten correctly. It doesn't look like is_dimable() was ever implemented for any MH object. --- lib/Insteon.pm | 3 --- lib/Insteon/Lighting.pm | 5 +++++ lib/http_server.pl | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index eb3b59c47..799f26c3f 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -549,7 +549,6 @@ so that each class can have its own unique set of voice commands. sub generate_voice_commands { - my $insteon_menu_states = $main::config_parms{insteon_menu_states} if $main::config_parms{insteon_menu_states}; &main::print_log("Generating Voice commands for all Insteon objects"); my $object_string; for my $object (&main::list_all_objects) { @@ -594,8 +593,6 @@ sub generate_voice_commands $object_string .= &main::store_object_data($object_name_v, 'Voice_Cmd', 'Insteon', 'Insteon_link_commands'); push @_insteon_link, $object_name; } elsif ($object->isa('Insteon::BaseDevice')) { - $states = $insteon_menu_states if $insteon_menu_states - && ($object->can('is_dimmable') && $object->is_dimmable); my $cmd_states = "$states,status,get engine version,scan link table,log links,update onlevel/ramprate"; #,on level,ramp rate"; $cmd_states .= ",link to interface,unlink with interface" if $object->isa("Insteon::BaseController") || $object->is_controller; $object_string .= "$object_name_v = new Voice_Cmd '$command [$cmd_states]';\n"; diff --git a/lib/Insteon/Lighting.pm b/lib/Insteon/Lighting.pm index c7660e965..be9146285 100644 --- a/lib/Insteon/Lighting.pm +++ b/lib/Insteon/Lighting.pm @@ -216,6 +216,11 @@ sub new my $self = new Insteon::BaseLight($p_deviceid,$p_interface); bless $self,$class; + + if( $main::config_parms{insteon_menu_states}) { + push(@{$$self{states}}, split( ',', $main::config_parms{insteon_menu_states}) ); + } + return $self; } diff --git a/lib/http_server.pl b/lib/http_server.pl index 9f44eb88a..4e3ff7e69 100644 --- a/lib/http_server.pl +++ b/lib/http_server.pl @@ -2367,9 +2367,7 @@ sub html_item_state { my $object_name = $object->{object_name}; my $object_name2 = &pretty_object_name($object_name); my $isa_X10 = UNIVERSAL::isa($object, 'X10_Item'); -# my $isa_X10 = $object->isa('X10_Item'); # This will abend if object is not an object my $isa_EIB2 = UNIVERSAL::isa($object, 'EIB2_Item'); - my $isa_insteon = UNIVERSAL::isa($object, 'Insteon_Device'); # If not a state item, just list it unless ($isa_X10 or UNIVERSAL::isa($object, 'Group') or exists $object->{state} or $object->{states}) { @@ -2384,9 +2382,7 @@ sub html_item_state { # If >2 possible states, add a Select pull down form my @states; @states = @{$object->{states}} if $object->{states}; -# print "db on=$object_name ix10=$isa_X10 s=@states\n"; @states = split ',', $config_parms{x10_menu_states} if $isa_X10; - @states = split ',', $config_parms{insteon_menu_states} if $isa_insteon; @states = qw(on off) if UNIVERSAL::isa($object, 'X10_Appliance'); From 283432d0320c63f2ea7dc04f9215a8c624352e9c Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Sun, 30 Jun 2013 17:26:50 -0500 Subject: [PATCH 3/7] Corrections for setting Insteon device states for web Flip order of on/off states to match new web object model Used the set_states() function rather than directly modifying data from the Generic_Item class. Eliminated the save/restore of the "states" array. I'm not sure what the original pupose was but saving/restoring prevents users from defining additional states in their ini file. Note that the current device "state" is still stored. --- lib/Insteon/BaseInsteon.pm | 20 +++++--------------- lib/Insteon/Lighting.pm | 6 +++--- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 3ea998f5d..a976953d7 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -1756,33 +1756,23 @@ sub restore_string { $restore_string .= $self->_aldb->restore_string(); } - if ($$self{states}) - { - my $states = ''; - foreach my $state (@{$$self{states}}) - { - $states .= '|' if $states; - $states .= $state; - } - $restore_string .= $self->{object_name} . "->restore_states(q~$states~);\n"; - } return $restore_string; } =item C -Used to reload the persistent states of variables on restart. +Obsolete / do not use. + +Function should remain so that upgrading users will not have issues starting +MH from previous versions that referenced this function in the +mh_temp.saved_states file. =cut sub restore_states { my ($self, $states) = @_; - if ($states) - { - @{$$self{states}} = split(/\|/,$states); - } } =item C diff --git a/lib/Insteon/Lighting.pm b/lib/Insteon/Lighting.pm index be9146285..5d53fea1e 100644 --- a/lib/Insteon/Lighting.pm +++ b/lib/Insteon/Lighting.pm @@ -33,8 +33,8 @@ sub new my $self = new Insteon::BaseDevice($p_deviceid,$p_interface); bless $self,$class; - # include very basic states - @{$$self{states}} = ('on','off'); + # include very basic states; off first so web interface up/down works + $self->set_states('off','on'); return $self; } @@ -218,7 +218,7 @@ sub new bless $self,$class; if( $main::config_parms{insteon_menu_states}) { - push(@{$$self{states}}, split( ',', $main::config_parms{insteon_menu_states}) ); + $self->set_states(split( ',', $main::config_parms{insteon_menu_states})); } return $self; From eadf6e0d3f69ef2c7d297d7ffd252b3b89c9af35 Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Mon, 1 Jul 2013 07:57:43 -0500 Subject: [PATCH 4/7] Make sure web icons show 'on' if light state is 100% --- web/bin/list_buttons.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/web/bin/list_buttons.pl b/web/bin/list_buttons.pl index 7bbc1c533..4f8fe62c9 100644 --- a/web/bin/list_buttons.pl +++ b/web/bin/list_buttons.pl @@ -65,6 +65,7 @@ my $icon; if ($Info{module_GD}) { # Use custom icons if they exist + $state = 'on' if $state eq '100%'; $icon = $state; $icon = 'dim' if $state =~ /d+/; my $image = "/graphics/light-" . lc $item . "_" . $icon . ".gif"; From e1db05a0a830b6855dc8528d761312b507e759e9 Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Mon, 1 Jul 2013 08:40:34 -0500 Subject: [PATCH 5/7] Better set of states for insteon_menu_states given new behavior --- bin/mh.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/mh.ini b/bin/mh.ini index 69d51a153..aa65a3df8 100644 --- a/bin/mh.ini +++ b/bin/mh.ini @@ -2058,7 +2058,7 @@ eib_errata=2 @ These are the states displayed on the tk and web menus @ French: insteon_menu_states=on,off,normal,eco,plus,moins,plus2,moins2,plus3,moins3,+40,-40,5%,30%,60%,100% -insteon_menu_states=on,off,+40,-40,5%,30%,60%,100% +insteon_menu_states=off,20%,40%,50%,60%,80%,on ****************************************************************************** # Category = Misc @@ -2451,4 +2451,4 @@ owfs_uom_temp = F # - add net parms. add cm11_serial parm. # -# \ No newline at end of file +# From 871d5e7e332eb27567a5d6a15127ebf3780619f2 Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Thu, 4 Jul 2013 22:45:35 -0500 Subject: [PATCH 6/7] Fix issue where setting level 100% sets to 0xfe Changed each instance of this calcualtion to use 5/4 rounding prior to calling sprintf(). There is some issue between perl data types and the C sprintf() implementation. In this case perl will print 255 but sprintf will return fe. Some instances of this were previously fixed by adding check logic to force fe or 254 to 100%. I modified all the instances to be consistent. --- lib/Insteon/BaseInsteon.pm | 4 ++-- lib/Insteon/Lighting.pm | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index a976953d7..4aaad4330 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -528,7 +528,7 @@ sub derive_message } else { if ($command eq 'on') { - $message->extra(sprintf("%02X",$level)); + $message->extra(sprintf("%02X",int($level+.5))); } else { $message->extra('00'); } @@ -594,7 +594,7 @@ sub _is_info_request my $is_info_request = 0; if ($cmd eq 'status_request') { $is_info_request++; - my $ack_on_level = (hex($msg{extra}) >= 254) ? 100 : sprintf("%d", hex($msg{extra}) * 100 / 255); + my $ack_on_level = sprintf("%d", int((hex($msg{extra}) * 100 / 255)+.5)); &::print_log("[Insteon::BaseObject] received status for " . $self->{object_name} . " with on-level: $ack_on_level%, " . "hops left: $msg{hopsleft}") if $main::Debug{insteon}; diff --git a/lib/Insteon/Lighting.pm b/lib/Insteon/Lighting.pm index 5d53fea1e..92d69cf18 100644 --- a/lib/Insteon/Lighting.pm +++ b/lib/Insteon/Lighting.pm @@ -193,13 +193,7 @@ sub convert_level my $level = 'ff'; if (defined ($on_level)) { $on_level =~ s/(\d+)%?/$1/; - if ($on_level eq '100') { - $level = 'ff'; - } elsif ($on_level eq '0') { - $level = '00'; - } else { - $level = sprintf('%02X',$on_level * 2.55); - } + $level = sprintf('%02X',int(($on_level * 2.55) + .5)); } return $level; } From 313d46edec465d47d692af219d6250bb2a16a080 Mon Sep 17 00:00:00 2001 From: Michael Stovenour Date: Fri, 5 Jul 2013 08:12:59 -0500 Subject: [PATCH 7/7] Make sure web icons show 'off' if light state is 0% --- web/bin/list_buttons.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/web/bin/list_buttons.pl b/web/bin/list_buttons.pl index 4f8fe62c9..3dedf71f5 100644 --- a/web/bin/list_buttons.pl +++ b/web/bin/list_buttons.pl @@ -66,6 +66,7 @@ if ($Info{module_GD}) { # Use custom icons if they exist $state = 'on' if $state eq '100%'; + $state = 'off' if $state eq '0%'; $icon = $state; $icon = 'dim' if $state =~ /d+/; my $image = "/graphics/light-" . lc $item . "_" . $icon . ".gif";