Skip to content

Commit

Permalink
Merge pull request #265 from krkeegan/insteon_i2cs_linking
Browse files Browse the repository at this point in the history
Insteon:  Add Link_to_Interface for I2CS Devices
  • Loading branch information
krkeegan committed Oct 12, 2013
2 parents 9e6b634 + 0a7b4f6 commit ef4be61
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 28 deletions.
130 changes: 105 additions & 25 deletions lib/Insteon/BaseInsteon.pm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ L<Generic_Item|Generic_Item>

package Insteon::BaseObject;

use Switch;
use strict;
use Insteon::AllLinkDatabase;

Expand Down Expand Up @@ -802,6 +803,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') &&
Expand Down Expand Up @@ -833,9 +843,9 @@ 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 ($msg{is_nack})
{
Expand Down Expand Up @@ -970,6 +980,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;
Expand Down Expand Up @@ -1367,25 +1378,91 @@ 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<link_to_interface_i2cs([group,data3])>
Performs the same task as C<link_to_interface> 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<unlink_to_interface([group])>
Expand Down Expand Up @@ -1418,7 +1495,7 @@ sub unlink_to_interface
}
}

=item C<enter_linking_mode(group)>
=item C<enter_linking_mode(group, success_callback, failure_callback)>
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
Expand All @@ -1437,12 +1514,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);
}

Expand Down Expand Up @@ -1774,11 +1853,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);
}

Expand Down Expand Up @@ -3104,7 +3184,7 @@ C<Insteon::BaseDevice::enter_linking_mode()>.

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}}) {
Expand All @@ -3116,7 +3196,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<derive_message([command,extra])>
Expand Down
18 changes: 17 additions & 1 deletion lib/Insteon/BaseInterface.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -710,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();
}
}
Expand Down
16 changes: 16 additions & 0 deletions lib/Insteon/Message.pm
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ sub failure_callback
return $$self{failure_callback};
}

=item C<success_callback(data)>
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<send_attempts(data)>
Stores and retrieves the number of times Misterhouse has tried to send the message.
Expand Down
23 changes: 21 additions & 2 deletions lib/Insteon_PLM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,16 @@ 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
my $cmd = '01'; # controller code
$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);
}

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -538,7 +547,7 @@ sub _parse_data {
{
$callback = $pending_message->callback(); #$$self{_mem_callback};
$$self{_mem_callback} = undef;
}
}
if ($callback){
package main;
eval ($callback);
Expand Down Expand Up @@ -735,6 +744,16 @@ 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;
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;
}
#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))
Expand Down

0 comments on commit ef4be61

Please sign in to comment.