From 6ea66b9cecef18410f740a8c515ed8b0f9e76546 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 29 Jan 2013 23:55:25 -0800 Subject: [PATCH 01/26] Add support for saving ALDB_Delta Add basic code for storing and saving the ALDB_Delta. ALDB_Delta and related functions are most related to the ALDB object not the device object. Moving and rewriting code from initial ALDB_Delta branch. --- lib/Insteon/AllLinkDatabase.pm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 44ca2488e..48668f8c2 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -61,6 +61,13 @@ sub scandatetime return $$self{scandatetime}; } +sub aldb_delta +{ + my ($self, $p_aldb_delta) = @_; + $$self{aldb_delta} = $p_aldb_delta if $p_aldb_delta; + return $$self{aldb_delta}; +} + sub restore_string { my ($self) = @_; @@ -100,6 +107,11 @@ sub restore_string $restore_string .= $$self{device}->get_object_name . "->_aldb->scandatetime(q~" . $self->scandatetime . "~) if " . $$self{device}->get_object_name . "->_aldb;\n"; } + if (defined $self->aldb_delta) + { + $restore_string .= $$self{device}->get_object_name . "->_aldb->aldb_delta(q~" . $self->aldb_delta . "~) if " + . $$self{device}->get_object_name . "->_aldb;\n"; + } $restore_string .= $$self{device}->get_object_name . "->_aldb->health(q~" . $self->health . "~) if " . $$self{device}->get_object_name . "->_aldb;\n"; $restore_string .= $$self{device}->get_object_name . "->_aldb->restore_aldb(q~$aldb~) if " . $$self{device}->get_object_name . "->_aldb;\n"; From 5678c13803256ef2703706fbb7143d86bac3a9ba Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 00:15:25 -0800 Subject: [PATCH 02/26] Enable Framework for Skipping Unchanged Devices in Scan All Linkes Alter Scan All Links so that when the first argument passed is true, devices which are unchanged will be skipped --- lib/Insteon.pm | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index e65106b9a..c941449c3 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -13,7 +13,8 @@ my (@_sync_devices,@_sync_device_failures,$current_sync_device); sub scan_all_linktables { - if ($current_scan_device) + my($skip_unchanged) = @_; + if ($current_scan_device) { &main::print_log("[Scan all linktables] WARN: link already underway. Ignoring request for new scan ..."); return; @@ -58,21 +59,34 @@ sub scan_all_linktables } $_scan_cnt = scalar @_scan_devices; - &_get_next_linkscan(); + &_get_next_linkscan($skip_unchanged); } sub _get_next_linkscan_failure { + my($skip_unchanged) = @_; push @_scan_device_failures, $current_scan_device; &main::print_log("[Scan all link tables] WARN: failure occurred when scanning " . $current_scan_device->get_object_name . ". Moving on..."); - &_get_next_linkscan(); + &_get_next_linkscan($skip_unchanged); } sub _get_next_linkscan { - $current_scan_device = shift @_scan_devices; + my($skip_unchanged, $changed_device) = @_; + if ($skip_unchanged){ + if (!defined($changed_device)){ + $current_scan_device = shift @_scan_devices; + #set the call backs + #run query_aldb_delta check + } else { + #if a device is returned it needs to be scanned + $current_scan_device = $object; + } + } else { + $current_scan_device = shift @_scan_devices; + } if ($current_scan_device) { From 701617832fda7dec4de02cb085afb17beaf4cd57 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 00:25:25 -0800 Subject: [PATCH 03/26] Add Query ALDB Delta to ALDB object also fix a typo --- lib/Insteon.pm | 2 +- lib/Insteon/AllLinkDatabase.pm | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index c941449c3..2f99e7079 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -82,7 +82,7 @@ sub _get_next_linkscan #run query_aldb_delta check } else { #if a device is returned it needs to be scanned - $current_scan_device = $object; + $current_scan_device = $changed_device; } } else { $current_scan_device = shift @_scan_devices; diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 48668f8c2..bb099adc1 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -68,6 +68,15 @@ sub aldb_delta return $$self{aldb_delta}; } +sub query_aldb_delta +{ + my ($self, $action) = @_; + $$self{aldb_delta_action} = $action; + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'status_request'); + if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; + $self->_send_cmd($message); +} + sub restore_string { my ($self) = @_; From ee3572f60aece1ff047ec5486f8809053eaea5c6 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 00:59:25 -0800 Subject: [PATCH 04/26] Intercept status_request responses for Query ALDB Delta Add framework to catch status_request responses that were requested by query aldb delta --- lib/Insteon/BaseInsteon.pm | 54 +++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 54b34e2d7..a344da9bb 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -426,22 +426,45 @@ sub message_type sub _is_info_request { my ($self, $cmd, $ack_setby, %msg) = @_; - my $is_info_request = ($cmd eq 'status_request') ? 1 : 0; -#print "cmd: $cmd; is_info_request: $is_info_request\n"; - if ($is_info_request) { - my $ack_on_level = (hex($msg{extra}) >= 254) ? 100 : sprintf("%d", hex($msg{extra}) * 100 / 255); - &::print_log("[Insteon::BaseObject] received status for " . - $self->{object_name} . " with on-level: $ack_on_level%, " - . "hops left: $msg{hopsleft}") if $main::Debug{insteon}; - $self->level($ack_on_level); # update the level value - if ($ack_on_level == 0) { - $self->SUPER::set('off', $ack_setby); - } elsif ($ack_on_level > 0 and !($self->isa('Insteon::DimmableLight'))) { - $self->SUPER::set('on', $ack_setby); - } else { - $self->SUPER::set($ack_on_level . '%', $ack_setby); + my $is_info_request; + if ($cmd eq 'status_request') { + if (defined($self->_aldb->{aldb_delta_action})){ + #This status request was requested by query_aldb_delta + if ($self->_aldb->{aldb_delta_action} eq 'set'){ + $self->_aldb->aldb_delta($msg{cmd_code}); + &::print_log("[Insteon::BaseObject] The ALDB Delta for " + . $self->{object_name} . " updated to " . $self->_aldb->aldb_delta()); + ## run callback + } else { + # Are the ALDB tables in sync? + if ($self->_aldb->aldb_delta() eq $msg{cmd_code}){ + &::print_log("[Insteon::BaseObject] The ALDB Database for " + . $self->{object_name} . " is in sync."); + #Things in sync run unchanged callback + } else { + &::print_log("[Insteon::BaseObject] WARN The ALDB Database for " + . $self->{object_name} . " is out of sync."); + #things are out of sync run changed callback + } + } + $self->_aldb->{aldb_delta_action} = undef; + } else { + #This is a regular status_request + my $ack_on_level = (hex($msg{extra}) >= 254) ? 100 : sprintf("%d", hex($msg{extra}) * 100 / 255); + &::print_log("[Insteon::BaseObject] received status for " . + $self->{object_name} . " with on-level: $ack_on_level%, " + . "hops left: $msg{hopsleft}") if $main::Debug{insteon}; + $self->level($ack_on_level); # update the level value + if ($ack_on_level == 0) { + $self->SUPER::set('off', $ack_setby); + } elsif ($ack_on_level > 0 and !($self->isa('Insteon::DimmableLight'))) { + $self->SUPER::set('on', $ack_setby); + } else { + $self->SUPER::set($ack_on_level . '%', $ack_setby); + } + # if this were a scene controller, then also propogate the result to all members } - # if this were a scene controller, then also propogate the result to all members + $is_info_request = 1; } elsif ( $cmd eq 'get_engine_version' ) { my $version = $msg{extra}; @@ -454,6 +477,7 @@ sub _is_info_request } &::print_log("[Insteon::BaseObject] received engine version for " . $self->{object_name} . " of $version."); + $is_info_request = 1; } return $is_info_request; From b95a4e9943c9d6cc0f6a1ea6aec325c101382b48 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 01:14:25 -0800 Subject: [PATCH 05/26] Query for ALDB Delta at end of Device Scan and Store Moved the callback contained in the last _on_peek method to the is_info_request routine of the device. Setting the ALDB_Delta will only be done at the completion of a scan, so the aldb success callback can be used. --- lib/Insteon/AllLinkDatabase.pm | 13 ++----------- lib/Insteon/BaseInsteon.pm | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index bb099adc1..baae2323f 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -438,17 +438,8 @@ sub _on_peek &::print_log("[Insteon::ALDB_i1] " . $$self{device}->get_object_name . " completed link memory scan") if $main::Debug{insteon}; - if (defined $$self{_success_callback}) - { - my $callback = $$self{_success_callback}; - # clear it out *before* the eval - $$self{_success_callback} = undef; - package main; - eval ($callback); - &::print_log("[Insteon::ALDB_i1] " . $$self{device}->get_object_name . ": error during scan callback $@") - if $@ and $main::Debug{insteon}; - package Insteon::ALDB_i1; - } + # Put the new ALDB Delta into memory + $self->query_aldb_delta('set'); } else { diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index a344da9bb..d22cc2e33 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -433,8 +433,20 @@ sub _is_info_request if ($self->_aldb->{aldb_delta_action} eq 'set'){ $self->_aldb->aldb_delta($msg{cmd_code}); &::print_log("[Insteon::BaseObject] The ALDB Delta for " - . $self->{object_name} . " updated to " . $self->_aldb->aldb_delta()); - ## run callback + . $self->{object_name} . " has been updated to " . $self->_aldb->aldb_delta()); + # The only time this will be called is after scanning the + # device memory in some form, so we can just piggy back on + # the already existing ALDB callback + if (defined $self->_aldb->{_success_callback}) { + my $callback = $self->_aldb->{_success_callback}; + # clear it out *before* the eval + $self->_aldb->{_success_callback} = undef; + package main; + eval ($callback); + &::print_log("[Insteon::BaseObject] " . $self->get_object_name . ": error during scan callback $@") + if $@ and $main::Debug{insteon}; + package Insteon::BaseObject; + } } else { # Are the ALDB tables in sync? if ($self->_aldb->aldb_delta() eq $msg{cmd_code}){ From 702ba7587ec782719f04823d6ef45f7825a34846 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 01:59:25 -0800 Subject: [PATCH 06/26] Sync All Links, Skip Devices that have not changed Made a mess out of the _get_next_linkscan sub, but it works. --- lib/Insteon.pm | 52 ++++++++++++++++++++++++-------------- lib/Insteon/BaseInsteon.pm | 26 ++++++++++++------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 2f99e7079..848b71eb9 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -75,28 +75,42 @@ sub _get_next_linkscan_failure sub _get_next_linkscan { my($skip_unchanged, $changed_device) = @_; - if ($skip_unchanged){ - if (!defined($changed_device)){ - $current_scan_device = shift @_scan_devices; - #set the call backs - #run query_aldb_delta check - } else { - #if a device is returned it needs to be scanned - $current_scan_device = $changed_device; - } - } else { - $current_scan_device = shift @_scan_devices; - } - - if ($current_scan_device) - { - &main::print_log("[Scan all link tables] Now scanning: " - . $current_scan_device->get_object_name . " (" + ## TODO skip checking ALDB_Delta for devices that are not healthy and force a scan + if ($skip_unchanged && $changed_device) { + ## if a device's aldb_delta has changed it is returned as an object here + $current_scan_device = $changed_device; + &main::print_log("[Scan all link tables] Now scanning: " + . $current_scan_device->get_object_name . " (" . ($_scan_cnt - scalar @_scan_devices) . " of $_scan_cnt)"); # pass first the success callback followed by the failure callback - $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan()','&Insteon::_get_next_linkscan_failure()'); - } else { + $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan('.$skip_unchanged.')','&Insteon::_get_next_linkscan_failure('.$skip_unchanged.')'); + return; + } else { + $current_scan_device = shift @_scan_devices; + } + ## Temporary Test to skip PLM and go faster + if (($skip_unchanged == 2) && ($current_scan_device == &Insteon::active_interface)){ + _get_next_linkscan(2); + return; + } + if ($current_scan_device) { + if ($skip_unchanged){ + ## check if aldb_delta has changed; + if ($current_scan_device != &Insteon::active_interface){ + $current_scan_device->_aldb->{_aldb_unchanged_callback} = '&Insteon::_get_next_linkscan('.$skip_unchanged.')'; + $current_scan_device->_aldb->{_aldb_changed_callback} = '&Insteon::_get_next_linkscan('.$skip_unchanged.', '.$current_scan_device->get_object_name.')'; + $current_scan_device->_aldb->query_aldb_delta("check"); + return; + } + } + &main::print_log("[Scan all link tables] Now scanning: " + . $current_scan_device->get_object_name . " (" + . ($_scan_cnt - scalar @_scan_devices) + . " of $_scan_cnt)"); + # pass first the success callback followed by the failure callback + $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan('.$skip_unchanged.')','&Insteon::_get_next_linkscan_failure('.$skip_unchanged.')'); + } else { &main::print_log("[Scan all link tables] All tables have completed scanning"); my $_scan_failure_cnt = scalar @_scan_device_failures; if ($_scan_failure_cnt) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index d22cc2e33..99a2c3b24 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -429,6 +429,7 @@ sub _is_info_request my $is_info_request; if ($cmd eq 'status_request') { if (defined($self->_aldb->{aldb_delta_action})){ + my $callback = undef; #This status request was requested by query_aldb_delta if ($self->_aldb->{aldb_delta_action} eq 'set'){ $self->_aldb->aldb_delta($msg{cmd_code}); @@ -438,28 +439,35 @@ sub _is_info_request # device memory in some form, so we can just piggy back on # the already existing ALDB callback if (defined $self->_aldb->{_success_callback}) { - my $callback = $self->_aldb->{_success_callback}; - # clear it out *before* the eval + $callback = $self->_aldb->{_success_callback}; $self->_aldb->{_success_callback} = undef; - package main; - eval ($callback); - &::print_log("[Insteon::BaseObject] " . $self->get_object_name . ": error during scan callback $@") - if $@ and $main::Debug{insteon}; - package Insteon::BaseObject; } } else { # Are the ALDB tables in sync? if ($self->_aldb->aldb_delta() eq $msg{cmd_code}){ &::print_log("[Insteon::BaseObject] The ALDB Database for " . $self->{object_name} . " is in sync."); - #Things in sync run unchanged callback + if (defined $self->_aldb->{_aldb_unchanged_callback}) { + $callback = $self->_aldb->{_aldb_unchanged_callback}; + $self->_aldb->{_aldb_unchanged_callback} = undef; + } } else { &::print_log("[Insteon::BaseObject] WARN The ALDB Database for " . $self->{object_name} . " is out of sync."); - #things are out of sync run changed callback + if (defined $self->_aldb->{_aldb_changed_callback}) { + $callback = $self->_aldb->{_aldb_changed_callback}; + $self->_aldb->{_aldb_changed_callback} = undef; + } } } $self->_aldb->{aldb_delta_action} = undef; + if ($callback){ + package main; + eval ($callback); + &::print_log("[Insteon::BaseObject] " . $self->get_object_name . ": error during scan callback $@") + if $@ and $main::Debug{insteon}; + package Insteon::BaseObject; + } } else { #This is a regular status_request my $ack_on_level = (hex($msg{extra}) >= 254) ? 100 : sprintf("%d", hex($msg{extra}) * 100 / 255); From 614d1578f53b2127c22f1674481e77be68b05b23 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 30 Jan 2013 21:41:52 -0800 Subject: [PATCH 07/26] Prevent Adding a Link if ALDB is Out of Sync Changes made to ALDB_i1 Add_link so it will prevent any adding of a link from any voice command. The code looks like crap, references and callbacks stacked on top of references and callbacks. What a mess. Should be able to do the same thing for update and delete using the same method. --- lib/Insteon/AllLinkDatabase.pm | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index baae2323f..a26fb83a7 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -64,7 +64,7 @@ sub scandatetime sub aldb_delta { my ($self, $p_aldb_delta) = @_; - $$self{aldb_delta} = $p_aldb_delta if $p_aldb_delta; + $$self{aldb_delta} = $p_aldb_delta if defined($p_aldb_delta); return $$self{aldb_delta}; } @@ -1320,7 +1320,12 @@ sub add_link { my ($self, $parms_text) = @_; my %link_parms; - if (@_ > 2) + if ($parms_text eq 'ok' or $parms_text eq 'fail'){ + %link_parms = %{$self->{callback_parms}}; + $$self{callback_parms} = undef; + $link_parms{aldb_check} = $parms_text; + } + elsif (@_ > 2) { shift @_; %link_parms = @_; @@ -1340,7 +1345,7 @@ sub add_link else { $device_id = lc $insteon_object->device_id; - } + } my $is_controller = ($link_parms{is_controller}) ? 1 : 0; # check whether the link already exists my $subaddress = ($link_parms{data3}) ? $link_parms{data3} : '00'; @@ -1351,7 +1356,24 @@ sub add_link { $key .= $subaddress; } - if (defined $$self{aldb}{$key}{inuse}) + if (!defined($link_parms{aldb_check}) && (!$$self{device}->isa('Insteon_PLM'))){ + ## Check whether ALDB is in sync + $self->{callback_parms} = \%link_parms; + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'ok')"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'fail')"; + $self->query_aldb_delta("check"); + } elsif ($link_parms{aldb_check} eq "fail"){ + &::print_log("[Insteon::ALDB_i1] WARN: Link NOT added, please rescan this device and sync again."); + if ($link_parms{callback}) + { + package main; + eval($link_parms{callback}); + &::print_log("[Insteon::ALDB_i1] failure occurred in callback eval for " . $$self{device}->get_object_name . ":" . $@) + if $@ and $main::Debug{insteon}; + package Insteon::ALDB_i1; + } + } + elsif (defined $$self{aldb}{$key}{inuse}) { &::print_log("[Insteon::ALDB_i1] WARN: attempt to add link to " . $$self{device}->get_object_name . " that already exists! " . "object=" . $insteon_object->get_object_name . ", group=$group, is_controller=$is_controller"); From 76d920dbdd6f7041ade945e51856b109e85bee29 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 31 Jan 2013 22:47:56 -0800 Subject: [PATCH 08/26] Force Check ALDB_Delta Prior to Deleting Links Copied logic from add_link Also fixed a stupid error in _get_next_linkscan, but code is still a horrible mess, which is my fault. And it still has problems. --- lib/Insteon.pm | 2 +- lib/Insteon/AllLinkDatabase.pm | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 848b71eb9..cee94a8f4 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -109,7 +109,7 @@ sub _get_next_linkscan . ($_scan_cnt - scalar @_scan_devices) . " of $_scan_cnt)"); # pass first the success callback followed by the failure callback - $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan('.$skip_unchanged.')','&Insteon::_get_next_linkscan_failure('.$skip_unchanged.')'); + $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan(0)','&Insteon::_get_next_linkscan_failure(0)'); } else { &main::print_log("[Scan all link tables] All tables have completed scanning"); my $_scan_failure_cnt = scalar @_scan_device_failures; diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index a26fb83a7..90599a114 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -73,7 +73,7 @@ sub query_aldb_delta my ($self, $action) = @_; $$self{aldb_delta_action} = $action; my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'status_request'); - if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; + if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; $self->_send_cmd($message); } @@ -705,7 +705,12 @@ sub delete_link { my ($self, $parms_text) = @_; my %link_parms; - if (@_ > 2) + if ($parms_text eq 'ok' or $parms_text eq 'fail'){ + %link_parms = %{$self->{callback_parms}}; + $$self{callback_parms} = undef; + $link_parms{aldb_check} = $parms_text; + } + elsif (@_ > 2) { shift @_; %link_parms = @_; @@ -716,6 +721,23 @@ sub delete_link } $$self{_success_callback} = ($link_parms{callback}) ? $link_parms{callback} : undef; $$self{_failure_callback} = ($link_parms{failure_callback}) ? $link_parms{failure_callback} : undef; + if (!defined($link_parms{aldb_check}) && (!$$self{device}->isa('Insteon_PLM'))){ + ## Check whether ALDB is in sync + $self->{callback_parms} = \%link_parms; + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'ok')"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'fail')"; + $self->query_aldb_delta("check"); + } elsif ($link_parms{aldb_check} eq "fail"){ + &::print_log("[Insteon::ALDB_i1] WARN: Link NOT added, please rescan this device and sync again."); + if ($link_parms{callback}) + { + package main; + eval($link_parms{callback}); + &::print_log("[Insteon::ALDB_i1] failure occurred in callback eval for " . $$self{device}->get_object_name . ":" . $@) + if $@ and $main::Debug{insteon}; + package Insteon::ALDB_i1; + } + } if ($link_parms{address}) { &main::print_log("[Insteon::ALDB_i1] Now deleting link [0x$link_parms{address}]"); From a3e864edf26e453f38afdce766152bc9b37ae207 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 1 Feb 2013 19:00:25 -0800 Subject: [PATCH 09/26] Fix Typos in Delete_Link, Fix Logic for Checking ALDB Delta Stupid mistake. Fix typos in Delete_Link which used a callback to add_link or referenced adding a link. Fix logic in delete_link and add_link to make sure that an out of sync device is not altered. --- lib/Insteon/AllLinkDatabase.pm | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 90599a114..2510b9fb8 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -724,11 +724,11 @@ sub delete_link if (!defined($link_parms{aldb_check}) && (!$$self{device}->isa('Insteon_PLM'))){ ## Check whether ALDB is in sync $self->{callback_parms} = \%link_parms; - $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'ok')"; - $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::add_link('.$$self{device}->{object_name}."->_aldb, 'fail')"; + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::delete_link('.$$self{device}->{object_name}."->_aldb, 'ok')"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::delete_link('.$$self{device}->{object_name}."->_aldb, 'fail')"; $self->query_aldb_delta("check"); } elsif ($link_parms{aldb_check} eq "fail"){ - &::print_log("[Insteon::ALDB_i1] WARN: Link NOT added, please rescan this device and sync again."); + &::print_log("[Insteon::ALDB_i1] WARN: Link NOT deleted, please rescan this device and sync again."); if ($link_parms{callback}) { package main; @@ -737,16 +737,14 @@ sub delete_link if $@ and $main::Debug{insteon}; package Insteon::ALDB_i1; } - } - if ($link_parms{address}) + } elsif ($link_parms{address} && $link_parms{aldb_check} eq "ok") { &main::print_log("[Insteon::ALDB_i1] Now deleting link [0x$link_parms{address}]"); $$self{_mem_activity} = 'delete'; $$self{pending_aldb}{address} = $link_parms{address}; $self->_peek($link_parms{address},0); - } - else + } elsif ($link_parms{aldb_check} eq "ok") { my $insteon_object = $link_parms{object}; my $deviceid = ($insteon_object) ? $insteon_object->device_id : $link_parms{deviceid}; @@ -1407,8 +1405,7 @@ sub add_link if $@ and $main::Debug{insteon}; package Insteon::ALDB_i1; } - } - else + } elsif ($link_parms{aldb_check} eq "ok") { # strip optional % sign to append on_level my $on_level = $link_parms{on_level}; From 824212a96beae185600f9ffd562f0a8dc1ae38ba Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 1 Feb 2013 17:37:27 -0800 Subject: [PATCH 10/26] Change ALDB health nomenclature corrupt is now out-of-sync --- lib/Insteon/AllLinkDatabase.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 2510b9fb8..f93f88b4a 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -45,7 +45,7 @@ sub _send_cmd sub health { - # corrupt + # out-of-sync # unknown # empty # good @@ -697,7 +697,7 @@ sub scan_link_table $$self{_success_callback} = ($success_callback) ? $success_callback : undef; $$self{_failure_callback} = ($failure_callback) ? $failure_callback : undef; $self->scandatetime(&main::get_tickcount); - $self->health('corrupt'); # allow acknowledge to set otherwise + $self->health('out-of-sync'); # allow acknowledge to set otherwise $self->_peek('0FF8',0); } @@ -1885,7 +1885,7 @@ sub get_first_alllink { my ($self) = @_; $self->scandatetime(&main::get_tickcount); - $self->health('corrupt'); # set as corrupt and allow acknowledge to set otherwise + $self->health('out-of-sync'); # set as out-of-sync and allow acknowledge to set otherwise $$self{device}->queue_message(new Insteon::InsteonMessage('all_link_first_rec', $$self{device})); } From f008fadc94dcd7793df43d804e8c364215948475 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 1 Feb 2013 17:52:04 -0800 Subject: [PATCH 11/26] Update Link Table Health on all Status Requests Setup foundation for skipping aldb_delta check if link table health is bad --- lib/Insteon/BaseInsteon.pm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 99a2c3b24..4b268cad6 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -433,8 +433,8 @@ sub _is_info_request #This status request was requested by query_aldb_delta if ($self->_aldb->{aldb_delta_action} eq 'set'){ $self->_aldb->aldb_delta($msg{cmd_code}); - &::print_log("[Insteon::BaseObject] The ALDB Delta for " - . $self->{object_name} . " has been updated to " . $self->_aldb->aldb_delta()); + &::print_log("[Insteon::BaseObject] The Link Table Version for " + . $self->{object_name} . " has been updated to version number " . $self->_aldb->aldb_delta()); # The only time this will be called is after scanning the # device memory in some form, so we can just piggy back on # the already existing ALDB callback @@ -445,15 +445,16 @@ sub _is_info_request } else { # Are the ALDB tables in sync? if ($self->_aldb->aldb_delta() eq $msg{cmd_code}){ - &::print_log("[Insteon::BaseObject] The ALDB Database for " + &::print_log("[Insteon::BaseObject] The link table for " . $self->{object_name} . " is in sync."); if (defined $self->_aldb->{_aldb_unchanged_callback}) { $callback = $self->_aldb->{_aldb_unchanged_callback}; $self->_aldb->{_aldb_unchanged_callback} = undef; } } else { - &::print_log("[Insteon::BaseObject] WARN The ALDB Database for " - . $self->{object_name} . " is out of sync."); + &::print_log("[Insteon::BaseObject] WARN The link table for " + . $self->{object_name} . " is out-of-sync."); + $self->_aldb->health('out-of-sync'); if (defined $self->_aldb->{_aldb_changed_callback}) { $callback = $self->_aldb->{_aldb_changed_callback}; $self->_aldb->{_aldb_changed_callback} = undef; @@ -471,9 +472,11 @@ sub _is_info_request } else { #This is a regular status_request my $ack_on_level = (hex($msg{extra}) >= 254) ? 100 : sprintf("%d", hex($msg{extra}) * 100 / 255); + $self->_aldb->health('out-of-sync') if($self->_aldb->aldb_delta() ne $msg{cmd_code}); &::print_log("[Insteon::BaseObject] received status for " . $self->{object_name} . " with on-level: $ack_on_level%, " - . "hops left: $msg{hopsleft}") if $main::Debug{insteon}; + . "link table health: " . $self->_aldb->health + . ", hops left: $msg{hopsleft}") if $main::Debug{insteon}; $self->level($ack_on_level); # update the level value if ($ack_on_level == 0) { $self->SUPER::set('off', $ack_setby); From 0250d8265d5f37069ca785223acbef91310d1286 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 1 Feb 2013 18:24:45 -0800 Subject: [PATCH 12/26] Skip requesting ALDB_Delta if MH Link Table is out-of-sync Requesting a status is unnecessary if we already know we are out-of-sync --- lib/Insteon/AllLinkDatabase.pm | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index f93f88b4a..f5d64830f 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -72,9 +72,23 @@ sub query_aldb_delta { my ($self, $action) = @_; $$self{aldb_delta_action} = $action; - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'status_request'); - if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; - $self->_send_cmd($message); + if ($action eq "check" && ($self->health ne "good" || $self->health ne "empty")){ + &::print_log("[Insteon::BaseObject] WARN The link table for " + . $self->{object_name} . " is out-of-sync."); + if (defined $self->{_aldb_changed_callback}) { + package main; + my $callback = $self->{_aldb_changed_callback}; + $self->{_aldb_changed_callback} = undef; + eval ($callback); + &::print_log("[Insteon::BaseObject] " . $self->{device}->get_object_name . ": error during scan callback $@") + if $@ and $main::Debug{insteon}; + package Insteon::BaseObject; + } + } else { + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'status_request'); + if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; + $self->_send_cmd($message); + } } sub restore_string From 82a2b251b53dfbc438f4de2efaf8a910b62904e9 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 1 Feb 2013 21:03:08 -0800 Subject: [PATCH 13/26] Dont Request ALDB_Delta if it is less than 2 Seconds Old Assume that the device link database won't change in less than 2 seconds This is in anticipation of syncing multiple links to the same device in sequence. This way we only check the ALDB_Delta at the start of the sync process. --- lib/Insteon/AllLinkDatabase.pm | 15 ++++++++++++++- lib/Insteon/BaseInsteon.pm | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index f5d64830f..9fcbf02ca 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -72,7 +72,7 @@ sub query_aldb_delta { my ($self, $action) = @_; $$self{aldb_delta_action} = $action; - if ($action eq "check" && ($self->health ne "good" || $self->health ne "empty")){ + if ($action eq "check" && $self->health ne "good" && $self->health ne "empty"){ &::print_log("[Insteon::BaseObject] WARN The link table for " . $self->{object_name} . " is out-of-sync."); if (defined $self->{_aldb_changed_callback}) { @@ -84,6 +84,19 @@ sub query_aldb_delta if $@ and $main::Debug{insteon}; package Insteon::BaseObject; } + } elsif ($action eq "check" && ((&main::get_tickcount - $self->scandatetime()) <= 2000)){ + #if we just did a aldb_query less than 2 seconds ago, don't repeat + &::print_log("[Insteon::BaseObject] The link table for " + . $self->{object_name} . " is in sync."); + if (defined $self->{_aldb_unchanged_callback}) { + package main; + my $callback = $self->{_aldb_unchanged_callback}; + $self->{_aldb_unchanged_callback} = undef; + eval ($callback); + &::print_log("[Insteon::BaseObject] " . $self->{device}->get_object_name . ": error during scan callback $@") + if $@ and $main::Debug{insteon}; + package Insteon::BaseObject; + } } else { my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'status_request'); if (defined($$self{_failure_callback})) {$message->failure_callback($$self{_failure_callback})}; diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 4b268cad6..f162be380 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -433,6 +433,7 @@ sub _is_info_request #This status request was requested by query_aldb_delta if ($self->_aldb->{aldb_delta_action} eq 'set'){ $self->_aldb->aldb_delta($msg{cmd_code}); + $self->_aldb->scandatetime(&main::get_tickcount); &::print_log("[Insteon::BaseObject] The Link Table Version for " . $self->{object_name} . " has been updated to version number " . $self->_aldb->aldb_delta()); # The only time this will be called is after scanning the From 878b44d60635e319f2421bd0a36661ea31018bf3 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Feb 2013 13:28:46 -0800 Subject: [PATCH 14/26] Peek address after add_link and set new aldb_delta Call peek of the address altered by add_address to confirm that it was accurately poked in. Required to enable a scan_one action in _on_peek which reads one address then queries the aldb_delta. Sync links can now sequentially add links while keeping the aldb databases and the aldb_delta in sync. --- lib/Insteon/AllLinkDatabase.pm | 49 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 9fcbf02ca..b288d64b4 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -306,19 +306,9 @@ sub _on_poke $$self{aldb}{$aldbkey}{group} = lc $$self{pending_aldb}{group}; $$self{aldb}{$aldbkey}{address} = $$self{pending_aldb}{address}; } - # clear out mem_activity flag - $$self{_mem_activity} = undef; - if (defined $$self{_success_callback}) - { - my $callback = $$self{_success_callback}; - # clear it out *before* the eval - $$self{_success_callback} = undef; - package main; - eval ($callback); - package Insteon::ALDB_i1; - &::print_log("[Insteon::ALDB_i1] error in link callback: " . $@) - if $@ and $main::Debug{insteon}; - } + # set mem activity to scan one address + $$self{_mem_activity} = "scan_one"; + $self->_peek($$self{pending_aldb}{address},0); } } elsif ($$self{_mem_activity} eq 'update_local') @@ -396,12 +386,12 @@ sub _on_peek { if ($$self{_mem_action} eq 'aldb_peek') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { $$self{_mem_action} = 'aldb_flag'; # if the device is responding to the peek, then init the link table # if at the very start of a scan - if (lc $$self{_mem_msb} eq '0f' and lc $$self{_mem_lsb} eq 'f8') + if (lc $$self{_mem_msb} eq '0f' and lc $$self{_mem_lsb} eq 'f8' and $$self{_mem_activity} ne 'scan_one') { # reinit the aldb hash as there will be a new one $$self{aldb} = undef; @@ -437,7 +427,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_flag') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -502,7 +492,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_group') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -522,7 +512,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_devhi') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -543,7 +533,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_devmid') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -564,7 +554,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_devlo') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -587,7 +577,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_data1') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -610,7 +600,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_data2') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -633,7 +623,7 @@ sub _on_peek } elsif ($$self{_mem_action} eq 'aldb_data3') { - if ($$self{_mem_activity} eq 'scan') + if ($$self{_mem_activity} eq 'scan' || $$self{_mem_activity} eq 'scan_one') { &::print_log("[Insteon::ALDB_i1] DEBUG3: " . $$self{device}->get_object_name . " [0x" . $$self{_mem_msb} . $$self{_mem_lsb} . "] received: " @@ -668,9 +658,16 @@ sub _on_peek { $self->add_empty_address($$self{pending_aldb}{address}); } - my $newaddress = sprintf("%04X", hex($$self{pending_aldb}{address}) - 8); - $$self{pending_aldb} = undef; - $self->_peek($newaddress); + if ($$self{_mem_activity} eq 'scan') { + my $newaddress = sprintf("%04X", hex($$self{pending_aldb}{address}) - 8); + $$self{pending_aldb} = undef; + $self->_peek($newaddress); + } elsif ($$self{_mem_activity} eq 'scan_one') { + $$self{_mem_activity} = undef; + # Put the new ALDB Delta into memory + $self->query_aldb_delta('set'); + #query will call the succcess callback + } } } elsif ($$self{_mem_activity} eq 'update' or $$self{_mem_activity} eq 'add') From 4d3d538bb6edd7ac8df490a6bad970ca0b983579 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Feb 2013 14:16:54 -0800 Subject: [PATCH 15/26] After delete_link scan the address and store ALDB_Delta Copying the concept used in add_link --- lib/Insteon/AllLinkDatabase.pm | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index b288d64b4..a65f9a4d0 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -359,18 +359,9 @@ sub _on_poke } delete $$self{aldb}{$key}; } - - if (defined $$self{_success_callback}) - { - my $callback = $$self{_success_callback}; - # clear it out *before* the eval - $$self{_success_callback} = undef; - package main; - eval ($callback); - &::print_log("[Insteon::ALDB_i1] error in link callback: " . $@) - if $@ and $main::Debug{insteon}; - package Insteon::ALDB_i1; - } + # set mem activity to scan one address + $$self{_mem_activity} = "scan_one"; + $self->_peek($$self{pending_aldb}{address},0); } } From bde2f01e5e422dc4d5e6dbdf99105fde33a1857e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 4 Feb 2013 18:53:33 -0800 Subject: [PATCH 16/26] Only Mark Duplicate in ALDB if address is not the same When scan all links, MH ALDB database is cleared. But for scan_one which only scans a single address, we don't dump the ALDB database. So the fact that an aldbkey already exists does not make it a duplicate, we could be scanning the same address again. It is only a duplicate if the addresses are different. --- lib/Insteon/AllLinkDatabase.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index b5a9b0c1b..81e3acb49 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -643,7 +643,8 @@ sub _on_peek $aldbkey .= $subaddress; } # check for duplicates - if (exists $$self{aldb}{$aldbkey} && $$self{aldb}{$aldbkey}{inuse}) + if (exists $$self{aldb}{$aldbkey} && $$self{aldb}{$aldbkey}{inuse} + && ($$self{aldb}{$aldbkey}{address} ne $$self{pending_aldb}{address})) { $self->add_duplicate_link_address($$self{pending_aldb}{address}); } From ec36ea755158dfbef7c8f172a1ffeae507a2f2e2 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 4 Feb 2013 19:03:08 -0800 Subject: [PATCH 17/26] Call Query ALDB_Delta for scan_one after delete Since we skip peeking anything but the ALDB_Flag. We need to have a call to query_aldb if called for scan_one only. Fix krkeegan/misterhouse#2 --- lib/Insteon/AllLinkDatabase.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 81e3acb49..1f2b4dd15 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -465,6 +465,11 @@ sub _on_peek my $newaddress = sprintf("%04X", hex($$self{_mem_msb} . $$self{_mem_lsb}) - 8); $$self{pending_aldb} = undef; $self->_peek($newaddress); + } elsif ($$self{_mem_activity} eq 'scan_one') { + $$self{_mem_activity} = undef; + # Put the new ALDB Delta into memory + $self->query_aldb_delta('set'); + #query will call the succcess callback } } } From 26e457f84a41430c463cac384232db04168dc5d8 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Feb 2013 17:45:00 -0800 Subject: [PATCH 18/26] Bump ALDB_Delta if Set to 00 The ALDB_Delta gets reset to 00 when devices lose power. The ALDB_Delta also gets reset to 00 when the devices are factory reset. Since we can't distinguish between these two states, we never allow the ALDB_delta to stay at 00. If a devices loses power, the ALDB_delta will change and a full scan will be conducted. At the conclusion of the scan we attempt to query and store the aldb_delta. If the aldb_delta is 00 we find the first empty address on the device. We then peek the aldb_flag(the first byte) of this address We then poke the same value back into the device. We use an empty address to decrease the potential harm from writing an incorrect byte if something goes wrong We then scan this address, being empty, the scan should end after the first byte We then query the ALDB_delta which should now be 01 since we "wrote" a single byte. The value increases even though the data didn't change. I reverted the previous commit as I was headed down the wrong path. Solves krkeegan/misterhouse#3 --- lib/Insteon/AllLinkDatabase.pm | 17 +++++++++++++++++ lib/Insteon/BaseInsteon.pm | 30 +++++++++++++++++++----------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 783cfab54..0209f9f69 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -363,6 +363,12 @@ sub _on_poke $$self{_mem_activity} = "scan_one"; $self->_peek($$self{pending_aldb}{address},0); } + elsif ($$self{_mem_activity} eq 'bump_delta') + { + # set mem activity to scan one address + $$self{_mem_activity} = "scan_one"; + $self->_peek($$self{pending_aldb}{address},0); + } } sub _on_peek @@ -412,6 +418,10 @@ sub _on_peek { $$self{_mem_action} = 'aldb_flag'; } + elsif ($$self{_mem_activity} eq 'bump_delta') + { + $$self{_mem_action} = 'aldb_flag'; + } $message->extra($$self{_mem_lsb}); $message->failure_callback($$self{_failure_callback}); $self->_send_cmd($message); @@ -492,6 +502,13 @@ sub _on_peek $message->failure_callback($$self{_failure_callback}); $self->_send_cmd($message); } + elsif ($$self{_mem_activity} eq 'bump_delta') + { + $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'poke'); + $message->extra($msg{extra}); + $message->failure_callback($$self{_failure_callback}); + $self->_send_cmd($message); + } } elsif ($$self{_mem_action} eq 'aldb_group') { diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 8195e8da2..69cb23304 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -432,17 +432,25 @@ sub _is_info_request my $callback = undef; #This status request was requested by query_aldb_delta if ($self->_aldb->{aldb_delta_action} eq 'set'){ - $self->_aldb->aldb_delta($msg{cmd_code}); - $self->_aldb->scandatetime(&main::get_tickcount); - &::print_log("[Insteon::BaseObject] The Link Table Version for " - . $self->{object_name} . " has been updated to version number " . $self->_aldb->aldb_delta()); - # The only time this will be called is after scanning the - # device memory in some form, so we can just piggy back on - # the already existing ALDB callback - if (defined $self->_aldb->{_success_callback}) { - $callback = $self->_aldb->{_success_callback}; - $self->_aldb->{_success_callback} = undef; - } + if ($msg{cmd_code} eq "00") { + # Force a bump of aldb_delta + $self->_aldb->{_mem_activity} = 'bump_delta'; + # Playing with an empty address should have the least chance for causing issues + $self->_aldb->{pending_aldb}{address} = $self->_aldb->get_first_empty_address(); + $self->_aldb->_peek($self->_aldb->{pending_aldb}{address},0); + } else { + $self->_aldb->aldb_delta($msg{cmd_code}); + $self->_aldb->scandatetime(&main::get_tickcount); + &::print_log("[Insteon::BaseObject] The Link Table Version for " + . $self->{object_name} . " has been updated to version number " . $self->_aldb->aldb_delta()); + # The only time this will be called is after scanning the + # device memory in some form, so we can just piggy back on + # the already existing ALDB callback + if (defined $self->_aldb->{_success_callback}) { + $callback = $self->_aldb->{_success_callback}; + $self->_aldb->{_success_callback} = undef; + } + } } else { # Are the ALDB tables in sync? if ($self->_aldb->aldb_delta() eq $msg{cmd_code}){ From 340fc032d1de178d163b9cf70ee9c56d19f83bd0 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Feb 2013 18:49:32 -0800 Subject: [PATCH 19/26] Enable Aldb_Delta Support for Update_Flags Checks whether Aldb_delta is insync prior to updating flags. If insync, resyncs aldb_delta after update_flags, if out of sync, update flats is still permitted to run, but no subsequent update of aldb_delta is made afterwards. There is no potential for update_flags to overwrite a link entry, so it shouldn't fail if out of sync. Partially fixes krkeegan/misterhouse#5 Need to make a similar patch for update_local. --- lib/Insteon/AllLinkDatabase.pm | 15 ++++++++++----- lib/Insteon/BaseInsteon.pm | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 0209f9f69..d8fdd9a5c 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -1670,12 +1670,17 @@ sub update_local_properties sub update_flags { - my ($self, $flags) = @_; + my ($self, $flags, $aldb_check) = @_; return unless defined $flags; - - $$self{_mem_activity} = 'update_flags'; - $$self{_operating_flags} = $flags; - $self->_peek('0023'); + if (defined($aldb_check)){ + $$self{_mem_activity} = 'update_flags'; + $$self{_operating_flags} = $flags; + $self->_peek('0023'); + } else { + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::update_flags('.$$self{device}->{object_name}."->_aldb, '$flags', 1)"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::update_flags('.$$self{device}->{object_name}."->_aldb, '$flags', 1)"; + $self->query_aldb_delta("check"); + } } sub get_link_record diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 69cb23304..400ffb437 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -551,6 +551,12 @@ sub _process_message } else { + if (($pending_cmd eq 'do_read_ee') && + ($self->_aldb->health eq "good" || $self->_aldb->health eq "empty") && + ($self->isa('Insteon::KeyPadLincRelay') || $self->isa('Insteon::KeyPadLinc'))){ + ## Update_Flags ends up here, set aldb_delta to new value + $self->_aldb->query_aldb_delta("set"); + } $self->is_acknowledged(1); # signal receipt of message to the command stack in case commands are queued $self->_process_command_stack(%msg); From 96381758a8894ac9a9909f452f4324551055e92d Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Feb 2013 19:08:40 -0800 Subject: [PATCH 20/26] Enable Aldb_Delta Support for Update_Local Similar to prior commit for Update_Flags. Completes the fix for krkeegan/misterhouse#5 --- lib/Insteon/AllLinkDatabase.pm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index d8fdd9a5c..94e1d9c66 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -328,6 +328,8 @@ sub _on_poke # update from eeprom--only a kpl issue $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'do_read_ee'); $self->_send_cmd($message); + } elsif ($self->health eq "good" || $self->health eq "empty") { + $self->query_aldb_delta('set'); } } } @@ -1663,9 +1665,15 @@ sub log_alllink_table sub update_local_properties { - my ($self) = @_; + my ($self, $aldb_check) = @_; + if (defined($aldb_check)){ $$self{_mem_activity} = 'update_local'; $self->_peek('0032'); # 0032 is the address for the onlevel + } else { + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::update_local_properties('.$$self{device}->{object_name}."->_aldb, 1)"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::update_local_properties('.$$self{device}->{object_name}."->_aldb, 1)"; + $self->query_aldb_delta("check"); + } } sub update_flags From 0c77e06eeb7a79ecdfe2523668a7c7d0a9c64cbd Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 7 Feb 2013 20:27:02 -0800 Subject: [PATCH 21/26] Add ALDB_Delta Support to Update Links Similar logic to add and delete links. This completes everything necessary for sync_links to work. This may be the last major thing needed for aldb_delta to work --- lib/Insteon/AllLinkDatabase.pm | 87 ++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index 94e1d9c66..d46603a66 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -1502,37 +1502,66 @@ sub add_link sub update_link { - my ($self, %link_parms) = @_; - my $insteon_object = $link_parms{object}; - my $group = $link_parms{group}; - my $is_controller = ($link_parms{is_controller}) ? 1 : 0; - # strip optional % sign to append on_level - my $on_level = $link_parms{on_level}; - $on_level =~ s/(\d+)%?/$1/; - # strip optional s (seconds) to append ramp_rate - my $ramp_rate = $link_parms{ramp_rate}; - $ramp_rate =~ s/(\d)s?/$1/; - &::print_log("[Insteon::ALDB_i1] updating " . $$self{device}->get_object_name . " light level controlled by " . $insteon_object->get_object_name - . " and group: $group with on level: $on_level and ramp rate: $ramp_rate") if $main::Debug{insteon}; - my $data1 = sprintf('%02X',$on_level * 2.55); - $data1 = 'ff' if $on_level eq '100'; - $data1 = '00' if $on_level eq '0'; - my $data2 = ($$self{device}->isa('Insteon::DimmableLight')) ? &Insteon::DimmableLight::convert_ramp($ramp_rate) : '00'; - my $data3 = ($link_parms{data3}) ? $link_parms{data3} : '00'; - my $deviceid = $insteon_object->device_id; - my $subaddress = $data3; - # get the address via lookup into the hash - my $key = lc $deviceid . $group . $is_controller; - # append the device "sub-address" (e.g., a non-root button on a keypadlinc) if it exists - if (!($subaddress eq '00' or $subaddress eq '01')) + my ($self, $parms_text) = @_; + my %link_parms; + if ($parms_text eq 'ok' or $parms_text eq 'fail'){ + %link_parms = %{$self->{callback_parms}}; + $$self{callback_parms} = undef; + $link_parms{aldb_check} = $parms_text; + } else { - $key .= $subaddress; + shift @_; + %link_parms = @_; + } + my $insteon_object = $link_parms{object}; + if (!defined($link_parms{aldb_check}) && (!$$self{device}->isa('Insteon_PLM'))){ + ## Check whether ALDB is in sync + $self->{callback_parms} = \%link_parms; + $$self{_aldb_unchanged_callback} = '&Insteon::ALDB_i1::update_link('.$$self{device}->{object_name}."->_aldb, 'ok')"; + $$self{_aldb_changed_callback} = '&Insteon::ALDB_i1::update_link('.$$self{device}->{object_name}."->_aldb, 'fail')"; + $self->query_aldb_delta("check"); + } elsif ($link_parms{aldb_check} eq "fail"){ + &::print_log("[Insteon::ALDB_i1] WARN: Link NOT updated, please rescan this device and then run sync links again."); + if ($link_parms{callback}) + { + package main; + eval($link_parms{callback}); + &::print_log("[Insteon::ALDB_i1] failure occurred in callback eval for " . $$self{device}->get_object_name . ":" . $@) + if $@ and $main::Debug{insteon}; + package Insteon::ALDB_i1; + } + } elsif ($link_parms{aldb_check} eq "ok") + { + my $group = $link_parms{group}; + my $is_controller = ($link_parms{is_controller}) ? 1 : 0; + # strip optional % sign to append on_level + my $on_level = $link_parms{on_level}; + $on_level =~ s/(\d+)%?/$1/; + # strip optional s (seconds) to append ramp_rate + my $ramp_rate = $link_parms{ramp_rate}; + $ramp_rate =~ s/(\d)s?/$1/; + &::print_log("[Insteon::ALDB_i1] updating " . $$self{device}->get_object_name . " light level controlled by " . $insteon_object->get_object_name + . " and group: $group with on level: $on_level and ramp rate: $ramp_rate") if $main::Debug{insteon}; + my $data1 = sprintf('%02X',$on_level * 2.55); + $data1 = 'ff' if $on_level eq '100'; + $data1 = '00' if $on_level eq '0'; + my $data2 = ($$self{device}->isa('Insteon::DimmableLight')) ? &Insteon::DimmableLight::convert_ramp($ramp_rate) : '00'; + my $data3 = ($link_parms{data3}) ? $link_parms{data3} : '00'; + my $deviceid = $insteon_object->device_id; + my $subaddress = $data3; + # get the address via lookup into the hash + my $key = lc $deviceid . $group . $is_controller; + # append the device "sub-address" (e.g., a non-root button on a keypadlinc) if it exists + if (!($subaddress eq '00' or $subaddress eq '01')) + { + $key .= $subaddress; + } + my $address = $$self{aldb}{$key}{address}; + $$self{_mem_activity} = 'update'; + $$self{_success_callback} = ($link_parms{callback}) ? $link_parms{callback} : undef; + $$self{_failure_callback} = ($link_parms{failure_callback}) ? $link_parms{failure_callback} : undef; + $self->_write_link($address, $deviceid, $group, $is_controller, $data1, $data2, $data3); } - my $address = $$self{aldb}{$key}{address}; - $$self{_mem_activity} = 'update'; - $$self{_success_callback} = ($link_parms{callback}) ? $link_parms{callback} : undef; - $$self{_failure_callback} = ($link_parms{failure_callback}) ? $link_parms{failure_callback} : undef; - $self->_write_link($address, $deviceid, $group, $is_controller, $data1, $data2, $data3); } From 887bd06c772da1c046297dd89190ba40603fc2f1 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sun, 10 Feb 2013 21:09:48 -0800 Subject: [PATCH 22/26] Fix type in log message in Query_ALDB_Delta --- lib/Insteon/AllLinkDatabase.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/AllLinkDatabase.pm b/lib/Insteon/AllLinkDatabase.pm index d46603a66..2b3fa9f0b 100755 --- a/lib/Insteon/AllLinkDatabase.pm +++ b/lib/Insteon/AllLinkDatabase.pm @@ -74,7 +74,7 @@ sub query_aldb_delta $$self{aldb_delta_action} = $action; if ($action eq "check" && $self->health ne "good" && $self->health ne "empty"){ &::print_log("[Insteon::BaseObject] WARN The link table for " - . $self->{object_name} . " is out-of-sync."); + . $self->{device}->get_object_name . " is out-of-sync."); if (defined $self->{_aldb_changed_callback}) { package main; my $callback = $self->{_aldb_changed_callback}; @@ -87,7 +87,7 @@ sub query_aldb_delta } elsif ($action eq "check" && ((&main::get_tickcount - $self->scandatetime()) <= 2000)){ #if we just did a aldb_query less than 2 seconds ago, don't repeat &::print_log("[Insteon::BaseObject] The link table for " - . $self->{object_name} . " is in sync."); + . $self->{device}->get_object_name . " is in sync."); if (defined $self->{_aldb_unchanged_callback}) { package main; my $callback = $self->{_aldb_unchanged_callback}; From d507d5f12d1e320737d7b26942945249f9da2132 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 11 Feb 2013 18:45:25 -0800 Subject: [PATCH 23/26] Fix Callback in _Get_Next_Linkscan to respect Skip Unchanged --- lib/Insteon.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 331a3f2b5..ac4b2a090 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -109,7 +109,7 @@ sub _get_next_linkscan . ($_scan_cnt - scalar @_scan_devices) . " of $_scan_cnt)"); # pass first the success callback followed by the failure callback - $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan(0)','&Insteon::_get_next_linkscan_failure(0)'); + $current_scan_device->scan_link_table('&Insteon::_get_next_linkscan('.$skip_unchanged.')','&Insteon::_get_next_linkscan_failure('.$skip_unchanged.')'); } else { &main::print_log("[Scan all link tables] All tables have completed scanning"); my $_scan_failure_cnt = scalar @_scan_device_failures; From 9d29f51c9d7ad61679f43e4ced46902f24186164 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 11 Feb 2013 18:55:25 -0800 Subject: [PATCH 24/26] Catch Reference to Voice_Cmd if Skip Unchanged is Not Specified --- lib/Insteon.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index ac4b2a090..8eddd3c89 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -13,7 +13,8 @@ my (@_sync_devices,@_sync_device_failures,$current_sync_device); sub scan_all_linktables { - my($skip_unchanged) = @_; + my $skip_unchanged = pop(@_); + $skip_unchanged = 0 if (ref $skip_unchanged || !defined($skip_unchanged)); if ($current_scan_device) { &main::print_log("[Scan all linktables] WARN: link already underway. Ignoring request for new scan ..."); From 86cb28f47e26672c06f77b601c713530818b5171 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 11 Feb 2013 18:08:36 -0800 Subject: [PATCH 25/26] Add Voice Command for Scan Changed Device Links --- lib/Insteon.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 8eddd3c89..3e649d82d 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -325,7 +325,7 @@ sub generate_voice_commands $object_string .= &main::store_object_data($object_name_v, 'Voice_Cmd', 'Insteon', 'Insteon_item_commands'); push @_insteon_device, $object_name if $group eq '01'; # don't allow non-base items to participate } elsif ($object->isa('Insteon_PLM')) { - my $cmd_states = "complete linking as responder,cancel linking,delete link with PLM,scan link table,show link table to log,delete orphan links,AUDIT - delete orphan links,scan all device link tables,sync all links,AUDIT - sync all links"; + my $cmd_states = "complete linking as responder,cancel linking,delete link with PLM,scan link table,show link table to log,delete orphan links,AUDIT - delete orphan links,scan all device link tables,scan changed device link tables, sync all links,AUDIT - sync all links"; $object_string .= "$object_name_v = new Voice_Cmd '$command [$cmd_states]';\n"; $object_string .= "$object_name_v -> tie_event('$object_name->complete_linking_as_responder','complete linking as responder');\n\n"; $object_string .= "$object_name_v -> tie_event('$object_name->initiate_unlinking_as_controller','initiate unlinking');\n\n"; @@ -335,6 +335,7 @@ sub generate_voice_commands $object_string .= "$object_name_v -> tie_event('$object_name->delete_orphan_links','delete orphan links');\n\n"; $object_string .= "$object_name_v -> tie_event('$object_name->delete_orphan_links(1)','AUDIT - delete orphan links');\n\n"; $object_string .= "$object_name_v -> tie_event('&Insteon::scan_all_linktables','scan all device link tables');\n\n"; + $object_string .= "$object_name_v -> tie_event('&Insteon::scan_all_linktables(1)','scan changed device link tables');\n\n"; $object_string .= "$object_name_v -> tie_event('&Insteon::sync_all_links(0)','sync all links');\n\n"; $object_string .= "$object_name_v -> tie_event('&Insteon::sync_all_links(1)','AUDIT - sync all links');\n\n"; $object_string .= &main::store_object_data($object_name_v, 'Voice_Cmd', 'Insteon', 'Insteon_PLM_commands'); From 2c475a1994bef867ba4fd200688aa46b13e47a7a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 11 Feb 2013 18:26:49 -0800 Subject: [PATCH 26/26] Clean up Comments in ALDB_Delta Changes --- lib/Insteon.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 3e649d82d..fe090841c 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -76,7 +76,6 @@ sub _get_next_linkscan_failure sub _get_next_linkscan { my($skip_unchanged, $changed_device) = @_; - ## TODO skip checking ALDB_Delta for devices that are not healthy and force a scan if ($skip_unchanged && $changed_device) { ## if a device's aldb_delta has changed it is returned as an object here $current_scan_device = $changed_device; @@ -90,7 +89,7 @@ sub _get_next_linkscan } else { $current_scan_device = shift @_scan_devices; } - ## Temporary Test to skip PLM and go faster + # Calling scan_all_link(2) will skip scanning the PLM if (($skip_unchanged == 2) && ($current_scan_device == &Insteon::active_interface)){ _get_next_linkscan(2); return;