From 6cd77297e749f9f2afe91b28bb408fa52316360f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 3 Oct 2013 19:16:34 -0700 Subject: [PATCH 1/9] Insteon: Add Success_Callback to Message Object Success_callback is eval'd when the ACK for the message is received. - Note: There are so many callbacks all over the place within the code, it might be nice to condense as many as possible into this routine. --- lib/Insteon/BaseInsteon.pm | 9 ++++++++- lib/Insteon/Message.pm | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 7e857a6d3..48aeee65b 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -831,8 +831,15 @@ sub _process_message . $self->get_object_name . " in response to a " . $pending_cmd . " command, but the command code " . $msg{cmd_code} . " is incorrect. Ignorring received message."); - $self->corrupt_count_log(1) if $self->can('corrupt_count_log'); + $self->corrupt_count_log(1) if $self->can('corrupt_count_log'); $p_setby->active_message->no_hop_increase(1); + } elsif ($clear_message && $p_setby->active_message->success_callback){ + main::print_log("[Insteon::BaseObject] DEBUG4: Now calling message success callback: " + . $p_setby->active_message->success_callback) if $main::Debug{insteon} >= 4; + package main; + eval $p_setby->active_message->success_callback; + ::print_log("[Insteon::BaseObject] problem w/ success callback: $@") if $@; + package Insteon::BaseObject; } } elsif ($msg{is_nack}) diff --git a/lib/Insteon/Message.pm b/lib/Insteon/Message.pm index 70ea818f3..fea11d116 100644 --- a/lib/Insteon/Message.pm +++ b/lib/Insteon/Message.pm @@ -104,6 +104,22 @@ sub failure_callback return $$self{failure_callback}; } +=item C + +Data will be evaluated after the receipt of an ACK from the device for this command. + +=cut + +sub success_callback +{ + my ($self, $callback) = @_; + if ($callback) + { + $$self{success_callback} = $callback; + } + return $$self{success_callback}; +} + =item C Stores and retrieves the number of times Misterhouse has tried to send the message. From 25905c98799e146109c9692da7739ef4aa8c4b7c Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 3 Oct 2013 19:27:59 -0700 Subject: [PATCH 2/9] Insteon: Add routine for Linking I2CS devices The routine requires a few steps to complete --- lib/Insteon/BaseInsteon.pm | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 48aeee65b..ee5fd36c9 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -25,6 +25,7 @@ L package Insteon::BaseObject; +use Switch; use strict; use Insteon::AllLinkDatabase; @@ -1393,6 +1394,53 @@ sub link_to_interface } } +=item C + +Performs the same task as C however this routine is designed +to perform the initial link to I2CS devices. These devices cannot be initially +linked to the PLM in the normal way. This process requires more steps than the +normal routine which will take longer to perform and therefore is more prone to +faile. As such, this should likely only be used if necessary. + +=cut + +sub link_to_interface_i2cs +{ + my ($self,$p_group, $p_data3, $step) = @_; + my $success_callback_prefix = $self->get_object_name."->link_to_interface_i2cs('$p_group','$p_data3',"; + my $success_callback = ""; + my $failure_callback = "::print_log('[Insteon::BaseInsteon] Error Link_To_Interface_I2CS ". + "routine failed for device: ".$self->get_object_name."')"; + $step = 0 if ($step eq ''); + switch ($step){ + case (0) { #Put PLM into initiate linking mode + $success_callback = $success_callback_prefix . "'1')"; + $self->interface()->initiate_linking_as_controller('00', $success_callback, $failure_callback); + } + case (1) { #Ask device to respond to link request + $success_callback = $success_callback_prefix . "'2')"; + $self->enter_linking_mode($p_group, $success_callback, $failure_callback); + } + case (2) { #Scan device to get an accurate link table + $success_callback = $success_callback_prefix . "'3')"; + $self->scan_link_table($success_callback, $failure_callback); + } + case (3) { #Add link from device->PLM + $success_callback = $success_callback_prefix . "'4')"; + my $group = $p_group; + $group = '01' unless $group; + my $link_info = "deviceid=" . lc $self->device_id . " group=$group is_controller=0 ". + "callback=$success_callback failure_callback=$failure_callback"; + $self->interface->add_link("$link_info"); + } + case (4) { + ::print_log('[Insteon::BaseInsteon] Link_To_Interface_I2CS successfully completed'. + ' for device ' .$self->get_object_name); + } + } +} + + =item C Will delete the contoller link from the device to the interface if such a link exists. From 20e48aca2aaad26d3f31cf6bd7a96a51e0943e88 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 3 Oct 2013 19:31:29 -0700 Subject: [PATCH 3/9] Insteon: Add Callback to Routines used by Link to I2CS Each routine needs to have success and failure callback parameters --- lib/Insteon/BaseInsteon.pm | 16 +++++++++------- lib/Insteon_PLM.pm | 13 +++++++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index ee5fd36c9..eccd36445 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -1440,7 +1440,6 @@ sub link_to_interface_i2cs } } - =item C Will delete the contoller link from the device to the interface if such a link exists. @@ -1471,7 +1470,7 @@ sub unlink_to_interface } } -=item C +=item C BETA -- Can be used to create the initial link with i2cs devices. i1 devices will not respond to this command. In the future, this will be incorporated into a @@ -1490,12 +1489,14 @@ Returns: nothing sub enter_linking_mode { - my ($self,$p_group) = @_; + my ($self,$p_group, $success_callback, $failure_callback) = @_; my $group = $p_group; $group = '01' unless $group; my $extra = sprintf("%02x", $group); $extra .= '0' x (30 - length $extra); my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'linking_mode', $extra); + $message->success_callback($success_callback); + $message->failure_callback($failure_callback); $self->_send_cmd($message); } @@ -1827,11 +1828,12 @@ Returns: nothing =cut sub get_engine_version { - my ($self) = @_; + my ($self, $success_callback, $failure_callback) = @_; my $message = new Insteon::InsteonMessage('insteon_send', $self, 'get_engine_version'); my $self_object_name = $self->get_object_name; - $message->failure_callback("$self_object_name->_get_engine_version_failure()"); + $message->failure_callback("$self_object_name->_get_engine_version_failure();$failure_callback"); + $message->success_callback($success_callback); $self->_send_cmd($message); } @@ -3157,7 +3159,7 @@ C. sub initiate_linking_as_controller { - my ($self, $p_group) = @_; + my ($self, $p_group, $success_callback, $failure_callback) = @_; # iterate over the members if ($$self{members}) { foreach my $member_ref (keys %{$$self{members}}) { @@ -3169,7 +3171,7 @@ sub initiate_linking_as_controller } } } - $self->interface()->initiate_linking_as_controller($p_group); + $self->interface()->initiate_linking_as_controller($p_group, $success_callback, $failure_callback); } =item C diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index dd6315f74..743144d55 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -308,7 +308,7 @@ controller will be added for this group, otherwise it will be for group 00. sub initiate_linking_as_controller { - my ($self, $group) = @_; + my ($self, $group, $success_callback, $failure_callback) = @_; $group = '00' unless $group; # set up the PLM as the responder @@ -316,6 +316,8 @@ sub initiate_linking_as_controller $cmd .= $group; # WARN - must be 2 digits and in hex!! my $message = new Insteon::InsteonMessage('all_link_start', $self); $message->interface_data($cmd); + $message->success_callback($success_callback); + $message->failure_callback($failure_callback); $self->queue_message($message); } @@ -509,6 +511,13 @@ sub _parse_data { } elsif ($record_type eq $prefix{all_link_start}) { + if ($self->active_message->success_callback){ + package main; + eval ($self->active_message->success_callback); + &::print_log("[Insteon_PLM] WARN1: Error encountered during ack callback: " . $@) + if $@ and $main::Debug{insteon} >= 1; + package Insteon_PLM; + } # clear the active message because we're done $self->clear_active_message(); } @@ -538,7 +547,7 @@ sub _parse_data { { $callback = $pending_message->callback(); #$$self{_mem_callback}; $$self{_mem_callback} = undef; - } + } if ($callback){ package main; eval ($callback); From ea07cb2d611be9179339497c333cb91f6343e9b3 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 3 Oct 2013 19:32:16 -0700 Subject: [PATCH 4/9] Insteon: Convert Link_to_Interface to Handle i2cs or non-i2cs devices The routine first checks to see if the device will respond to a get_engine_version request. If any ACK is received the routine continues as normal. In this case even if the device is an I2CS, the device must already have link entries as it responds to the PLM. If a NAK is received, it is assumes that this device is an i2cs and the link_to_interface_i2cs routine is called. --- lib/Insteon/BaseInsteon.pm | 54 ++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index eccd36445..c3ced8cc3 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -1373,25 +1373,45 @@ the device. sub link_to_interface { - my ($self,$p_group, $p_data3) = @_; + my ($self,$p_group, $p_data3, $step) = @_; my $group = $p_group; $group = '01' unless $group; - # add a link first to this device back to interface - # and, add a reference to creating a link from interface back to device via hook - my $callback_instance = $self->interface->get_object_name; - my $callback_info = "deviceid=" . lc $self->device_id . " group=$group is_controller=0"; - my %link_info = ( object => $self->interface, group => $group, is_controller => 1, -# on_level => '100%', ramp_rate => '0.1s', Controllers don't use on_level or ramp_rate - callback => "$callback_instance->add_link('$callback_info')"); - $link_info{data3} = $p_data3 if $p_data3; - if ($self->_aldb) { - $self->_aldb->add_link(%link_info); - } - else - { - &main::print_log("[BaseInsteon] This item " . $self->get_object_name . - " does not have an ALDB object. Linking is not permitted."); - } + my $success_callback_prefix = $self->get_object_name."->link_to_interface(\"$p_group\",\"$p_data3\","; + my $success_callback = ""; + my $failure_callback = '::print_log("[Insteon::BaseInsteon] Error: The Link_To_Interface '. + 'routine failed for device: '.$self->get_object_name.'")'; + $step = 0 if ($step eq ''); + switch ($step){ + case (0) { #If NAK on get_engine, then this is an I2CS device + $success_callback = $success_callback_prefix . "\"1\")"; + $failure_callback = $self->get_object_name."->link_to_interface_i2cs(\"$p_group\",\"$p_data3\")"; + $self->get_engine_version($success_callback, $failure_callback); + } + case (1) { #Add Link from object->PLM + $success_callback = $success_callback_prefix . "\"2\")"; + my %link_info = ( object => $self->interface, group => $group, is_controller => 1, + callback => "$success_callback", failure_callback=> "$failure_callback"); + $link_info{data3} = $p_data3 if $p_data3; + if ($self->_aldb) { + $self->_aldb->add_link(%link_info); + } + else + { + &main::print_log("[Insteon::BaseInsteon] Error: This item, " . $self->get_object_name . + ", does not have an ALDB object. Linking is not permitted."); + } + } + case (2){ #Add Link from PLM->object + $success_callback = $success_callback_prefix . "\"3\")"; + my $link_info = "deviceid=" . lc $self->device_id . " group=$group is_controller=0 " . + "callback=$success_callback failure_callback=$failure_callback"; + $self->interface->add_link($link_info); + } + case (3){ + ::print_log('[Insteon::BaseInsteon] Link_To_Interface successfully completed'. + ' for device ' .$self->get_object_name); + } + } } =item C From 8319d8da71c71dc9c171523db815129bd9840e4f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 3 Oct 2013 21:07:23 -0700 Subject: [PATCH 5/9] Insteon: Linking Mode Requires an ACK, Don't Clear Messages on All_Link_Complete --- lib/Insteon/BaseInsteon.pm | 1 + lib/Insteon_PLM.pm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index c3ced8cc3..1ea23ff7d 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -976,6 +976,7 @@ sub _process_command_stack or $message->command eq 'get_operating_flags' or $message->command eq 'read_write_aldb' or $message->command eq 'ping' + or $message->command eq 'linking_mode' ) { $$self{awaiting_ack} = 1; diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index 743144d55..b14308a88 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -743,7 +743,7 @@ sub _parse_data { { #ALL-Linking Completed my $link_address = substr($message_data,4,6); &::print_log("[Insteon_PLM] DEBUG2: ALL-Linking Completed with $link_address ($message_data)") if $main::Debug{insteon} >= 2; - $self->clear_active_message(); + #$self->clear_active_message(); } elsif ($parsed_prefix eq $prefix{all_link_clean_failed} and ($message_length == 12)) { #ALL-Link Cleanup Failure Report From 2159a5186f382267737be919a9c8b5037735dc18 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 5 Oct 2013 12:34:00 -0700 Subject: [PATCH 6/9] Insteon_I2CS_Linking: Clear Active Message When PLM Reports All-Linking-Complete Rather than clear the linking mode command when the device ACKs, wait to clear the message until the PLM reports the linking success. --- lib/Insteon/BaseInsteon.pm | 9 +++++++++ lib/Insteon_PLM.pm | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 1ea23ff7d..73a61c7f0 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -801,6 +801,15 @@ sub _process_message $clear_message = 1; } } + elsif ($pending_cmd eq 'linking_mode'){ + $corrupt_cmd = 1 if ($msg{cmd_code} ne $self->message_type_hex($pending_cmd)); + if (!$corrupt_cmd){ + &::print_log("[Insteon::BaseObject] received linking mode ACK from " . $self->{object_name}) + if $main::Debug{insteon}; + $self->interface->_set_timeout('xmit', 2000); + $clear_message = 0; + } + } else { if (($pending_cmd eq 'do_read_ee') && diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index b14308a88..0dc156830 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -743,7 +743,15 @@ sub _parse_data { { #ALL-Linking Completed my $link_address = substr($message_data,4,6); &::print_log("[Insteon_PLM] DEBUG2: ALL-Linking Completed with $link_address ($message_data)") if $main::Debug{insteon} >= 2; - #$self->clear_active_message(); + if ($self->active_message->success_callback){ + main::print_log("[Insteon::Insteon_PLM] DEBUG4: Now calling message success callback: " + . $self->active_message->success_callback) if $main::Debug{insteon} >= 4; + package main; + eval $self->active_message->success_callback; + ::print_log("[Insteon::Insteon_PLM] problem w/ success callback: $@") if $@; + package Insteon::BaseObject; + } + $self->clear_active_message(); } elsif ($parsed_prefix eq $prefix{all_link_clean_failed} and ($message_length == 12)) { #ALL-Link Cleanup Failure Report From c2fee31b6f8442b67bc3786ce16e76c1aa593660 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 8 Oct 2013 19:44:24 -0700 Subject: [PATCH 7/9] Insteon_I2CS_Linking: Move Success Callback Eval to BaseInterface This allows for per device _process_message subs to exist without needing to reproduce all of the eval code. --- lib/Insteon/BaseInsteon.pm | 9 +-------- lib/Insteon/BaseInterface.pm | 10 +++++++++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 73a61c7f0..f465feef7 100644 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -843,14 +843,7 @@ sub _process_message . $msg{cmd_code} . " is incorrect. Ignorring received message."); $self->corrupt_count_log(1) if $self->can('corrupt_count_log'); $p_setby->active_message->no_hop_increase(1); - } elsif ($clear_message && $p_setby->active_message->success_callback){ - main::print_log("[Insteon::BaseObject] DEBUG4: Now calling message success callback: " - . $p_setby->active_message->success_callback) if $main::Debug{insteon} >= 4; - package main; - eval $p_setby->active_message->success_callback; - ::print_log("[Insteon::BaseObject] problem w/ success callback: $@") if $@; - package Insteon::BaseObject; - } + } } elsif ($msg{is_nack}) { diff --git a/lib/Insteon/BaseInterface.pm b/lib/Insteon/BaseInterface.pm index e562aa763..e6c1fd906 100644 --- a/lib/Insteon/BaseInterface.pm +++ b/lib/Insteon/BaseInterface.pm @@ -562,7 +562,15 @@ sub on_standard_insteon_received # ask the object to process the received message and update its state # Object will return true if this is the end of the send transaction if($object->_process_message($self, %msg)) { - $self->clear_active_message(); + if ($self->active_message->success_callback){ + main::print_log("[Insteon::BaseInterface] DEBUG4: Now calling message success callback: " + . $self->active_message->success_callback) if $main::Debug{insteon} >= 4; + package main; + eval $self->active_message->success_callback; + ::print_log("[Insteon::BaseInterface] problem w/ success callback: $@") if $@; + package Insteon::BaseInterface; + } + $self->clear_active_message(); } } else From d63e458a42402092798aba8fd9015621f4eaf811 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 8/9] Insteon_i2CS_Linking: Add Success_Callback to Extended Messages --- lib/Insteon/BaseInterface.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Insteon/BaseInterface.pm b/lib/Insteon/BaseInterface.pm index e6c1fd906..a461b284a 100644 --- a/lib/Insteon/BaseInterface.pm +++ b/lib/Insteon/BaseInterface.pm @@ -718,6 +718,14 @@ sub on_extended_insteon_received } &::print_log("[Insteon::BaseInterface] Processing message for " . $object->get_object_name) if $main::Debug{insteon} >=3; if($object->_process_message($self, %msg)) { + if ($self->active_message->success_callback){ + main::print_log("[Insteon::BaseInterface] DEBUG4: Now calling message success callback: " + . $self->active_message->success_callback) if $main::Debug{insteon} >= 4; + package main; + eval $self->active_message->success_callback; + ::print_log("[Insteon::BaseInterface] problem w/ success callback: $@") if $@; + package Insteon::BaseInterface; + } $self->clear_active_message(); } } From 0a7b4f621b48d65e11cdc1751a976bdea528ae02 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 10 Oct 2013 18:25:37 -0700 Subject: [PATCH 9/9] Insteon_i2CS_Linking: Clear Awaiting Ack Flag on All-Link-Complete If flag not cleared, all messages to that device will stall --- lib/Insteon_PLM.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index 0dc156830..ff2af022e 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -751,6 +751,8 @@ sub _parse_data { ::print_log("[Insteon::Insteon_PLM] problem w/ success callback: $@") if $@; package Insteon::BaseObject; } + #Clear awaiting_ack flag + $self->active_message->setby->_process_command_stack(0); $self->clear_active_message(); } elsif ($parsed_prefix eq $prefix{all_link_clean_failed} and ($message_length == 12))