Skip to content

Commit

Permalink
Merge pull request #286 from krkeegan/insteon_set_redux
Browse files Browse the repository at this point in the history
Insteon: Rewrite/Rework/Condense Set Routine Code
  • Loading branch information
krkeegan committed Oct 20, 2013
2 parents ebc77b8 + e642f79 commit 6bf1924
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 421 deletions.
256 changes: 97 additions & 159 deletions lib/Insteon/BaseInsteon.pm
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,19 @@ state of either ON or OFF.

sub derive_link_state
{
my ($p_state) = @_;

my ($self, $p_state) = @_;
$p_state = $self if !(ref $self); #Old code made direct calls
#Convert Relative State to Absolute State
if ($p_state =~ /^([+-])(\d+)/) {
my $rel_state = $1 . $2;
my $curr_state = '100';
$curr_state = '0' if ($self->state eq 'off');
$curr_state = $1 if $self->state =~ /(\d{1,3})/;
$p_state = $curr_state + $rel_state;
$p_state = 'on' if ($p_state > 0);
$p_state = 'off' if ($p_state <= 0);
}

my $link_state = 'on';
if ($p_state eq 'off' or $p_state eq 'off_fast')
{
Expand Down Expand Up @@ -345,50 +356,44 @@ sub set
# always reset the is_locally_set property unless set_by is the device
$$self{m_is_locally_set} = 0 unless ref $p_setby and $p_setby eq $self;

# handle invalid state for non-dimmable devices
if (($p_state eq 'dim' or $p_state eq 'bright') and !($self->isa('Insteon::DimmableLight'))) {
$p_state = 'on';
if ($p_state eq 'toggle')
{
$p_state = ($self->state eq 'on')? 'off' : 'on';
}
elsif ($p_state eq 'toggle')
{
if ($self->state eq 'on')
{
$p_state = 'off';
}
elsif ($self->state eq 'off')
{
$p_state = 'on';
}
}

my $setby_name = $p_setby;
$setby_name = $p_setby->get_object_name() if (ref $p_setby and $p_setby->can('get_object_name'));
if (ref $p_setby and (($p_setby eq $self->interface())
or (($p_setby->isa('Insteon::BaseObject'))
and (($p_setby eq $self)
or (&main::set_by_to_target($p_setby) eq $self->interface)))))
{
# don't reset the object w/ the same state if set from the interface
return if (lc $p_state eq lc $self->state) and $self->is_acknowledged
and not(($p_setby->isa('Insteon::BaseObject') and ($p_setby eq $self)));
my $setby_name = $p_setby;
$setby_name = $p_setby->get_object_name() if (ref $p_setby and $p_setby->can('get_object_name'));
if (ref $p_setby and $p_setby eq $self)
{ #If set by device, update MH state,
my $derived_state = $self->derive_link_state($p_state);
&::print_log("[Insteon::BaseObject] " . $self->get_object_name()
. "::set($p_state, $setby_name)") if $main::Debug{insteon};
$self->set_receive($p_state,$p_setby,$p_response) if defined $p_state;
} else {
my $message = $self->derive_message($p_state);
$self->_send_cmd($message);

# $self->_send_cmd(command => $p_state,
# type => (($self->isa('Insteon::Insteon_Link') and !($self->is_root)) ? 'alllink' : 'standard'));
&::print_log("[Insteon::BaseObject] " . $self->get_object_name() . "::set($p_state, $setby_name)")
if $main::Debug{insteon};
$self->is_acknowledged(0);
$$self{pending_state} = $p_state;
$$self{pending_setby} = $p_setby;
$$self{pending_response} = $p_response;
}
$self->level($p_state) if ($self->isa("Insteon::BaseDevice") && $self->can('level')); # update the level value
# $self->SUPER::set($p_state,$p_setby,$p_response) if defined $p_state;
. "::set_receive($derived_state, $setby_name)") if $main::Debug{insteon};
$self->set_receive($derived_state,$p_setby,$p_response);
$self->set_linked_devices($p_state);
}
elsif (ref $p_setby and $p_setby eq $self->interface)
{ #If set by interface, this was a manual status_request response
&::print_log("[Insteon::BaseObject] " . $self->get_object_name()
. "::set_receive($p_state, $setby_name)") if $main::Debug{insteon};
$self->set_receive($p_state,$p_setby,$p_response);
}
else { # Not called by device, send set command
if ($self->is_responder){
my $message = $self->derive_message($p_state);
$self->_send_cmd($message);
&::print_log("[Insteon::BaseObject] " . $self->get_object_name() . "::set($p_state, $setby_name)")
if $main::Debug{insteon};
$self->is_acknowledged(0);
$$self{pending_state} = $p_state;
$$self{pending_setby} = $p_setby;
$$self{pending_response} = $p_response;
}
else {
::print_log("[Insteon::BaseObject] " . $self->get_object_name()
. " is not a responder and cannot be set to a state.");
}
}
$self->level($p_state) if $self->can('level'); # update the level value
} else {
&::print_log("[Insteon::BaseObject] failed state validation with state=$p_state");
}
Expand Down Expand Up @@ -445,6 +450,7 @@ sub set_receive
"less than $window milliseconds") if $main::Debug{insteon};
} else {
$$self{set_milliseconds} = $curr_milli;
$self->level($p_state) if $self->can('level'); # update the level value
$self->SUPER::set($p_state, $p_setby, $p_response);
}
}
Expand Down Expand Up @@ -652,11 +658,11 @@ sub _is_info_request
. "hops left: $msg{hopsleft}") if $main::Debug{insteon};
$self->level($ack_on_level) if $self->can('level'); # update the level value
if ($ack_on_level == 0) {
$self->SUPER::set('off', $ack_setby);
$self->set('off', $ack_setby);
} elsif ($ack_on_level > 0 and !($self->isa('Insteon::DimmableLight'))) {
$self->SUPER::set('on', $ack_setby);
$self->set('on', $ack_setby);
} else {
$self->SUPER::set($ack_on_level . '%', $ack_setby);
$self->set($ack_on_level . '%', $ack_setby);
}
# if this were a scene controller, then also propogate the result to all members
my $callback;
Expand Down Expand Up @@ -886,10 +892,19 @@ sub _process_message
}
elsif ($msg{command} eq 'start_manual_change')
{
# do nothing; although, maybe anticipate change? we should always get a stop
$$self{manual_direction} = $msg{extra};
$$self{manual_start} = ::get_tickcount() - (($msg{maxhops}-$msg{hopsleft})*50);
} elsif ($msg{command} eq 'stop_manual_change') {
# request status so that the final state can be known
$self->request_status($self);
# Determine percent change based on time interval
my $finish_time = &main::get_tickcount - (($msg{maxhops}-$msg{hopsleft})*50);
my $total_time = $finish_time - $$self{manual_start};
my $percent_change = int($total_time / 42);
if ($$self{manual_direction} eq '00') {
$percent_change = "-".$percent_change;
} else {
$percent_change = "+".$percent_change;
}
$self->set($percent_change, $self);
} elsif ($msg{command} eq 'read_write_aldb') {
if ($self->_aldb){
$clear_message = $self->_aldb->on_read_write_aldb(%msg) if $self->_aldb;
Expand Down Expand Up @@ -1021,7 +1036,7 @@ sub _is_valid_state
my ($msg, $substate) = split(/:/, $state, 2);
$msg=lc($msg);

if ($msg=~/^([1]?[0-9]?[0-9])/)
if ($msg=~/^[+-]?([1]?[0-9]?[0-9])/)
{
if ($1 < 1) {
$msg='off';
Expand Down Expand Up @@ -1307,20 +1322,6 @@ sub rate
return $$self{rate};
}

=item C<set_receive(state[,setby,response])>
Updates the device's level if it can level, then calls
C<Insteon::BaseObject::set_receive()>.
=cut

sub set_receive
{
my ($self, $p_state, $p_setby, $p_response) = @_;
$self->level($p_state) if $self->can('level'); # update the level value
$self->SUPER::set_receive($p_state, $p_setby, $p_response);
}

=item C<is_deaf()>
Returns true if the device must be awake in order to respond to messages. Most
Expand Down Expand Up @@ -3096,31 +3097,6 @@ sub _process_sync_queue {
}
}

=item C<set(state[,setby,response])>
Returns -1 if setby was set by this object.
Returns -1 if setby a tied_filter.
Otherwise calls C<set_linked_devices> and returns 0.
=cut

sub set
{
my ($self, $p_state, $p_setby, $p_respond) = @_;
# prevent reciprocal setby loops
return -1 if (ref $p_setby and ($p_setby ne $self) and $p_setby->can('get_set_by') and
$p_setby->{set_by} eq $self);
return -1 if &main::check_for_tied_filters($self, $p_state);

my $link_state = &Insteon::BaseObject::derive_link_state($p_state);

$self->set_linked_devices($link_state);

return 0;
}

=item C<set_linked_devices(state)>
Checks each linked member of device. If the linked member is a C<light_item>,
Expand All @@ -3138,12 +3114,11 @@ sub set_linked_devices
foreach my $member_ref (keys %{$$self{members}})
{
my $member = $$self{members}{$member_ref}{object};
my $on_state = $$self{members}{$member_ref}{on_level};
$on_state = '100%' unless $on_state;
my $local_state = $on_state;
$local_state = 'on' if $local_state eq '100%'
&& $member->isa('Insteon::BaseDevice') && !($member->is_root);
$local_state = 'off' if $local_state eq '0%' or $link_state eq 'off';
# If controller is on, set member to stored on_level
# else set to controller value
my $local_state = $$self{members}{$member_ref}{on_level};
$local_state = '100' unless $local_state;

if ($member->isa('Light_Item'))
{
# if they are Light_Items, then set their on_dim attrib to the member on level
Expand All @@ -3156,10 +3131,13 @@ sub set_linked_devices
my @lights = $member->find_members('Insteon::BaseDevice');
if (@lights)
{
my $light = @lights[0];
my $light = $lights[0];
# remember the current state to support resume
$$self{members}{$member_ref}{resume_state} = $light->state;
$member->manual($light, $ramp_rate);
if ($light->can('derive_link_state') && lc $link_state ne 'on'){
$local_state = $light->derive_link_state($link_state);
}
$light->set_receive($local_state,$self);
}
else
Expand All @@ -3170,20 +3148,17 @@ sub set_linked_devices
}
elsif ($member->isa('Insteon::BaseDevice'))
{
# remember the current state to support resume
# remember the current state to support resume
$$self{members}{$member_ref}{resume_state} = $member->state;
# if they are Insteon_Device objects, then simply set_receive their state to
# the member on level
if (!($member->isa('Insteon::DimmableLight')) and $member->isa('Insteon::BaseLight'))
{
$local_state = &Insteon::BaseObject::derive_link_state($local_state);
}
# if they are Insteon_Device objects, then simply set_receive their state to
# the member on level
if (lc $link_state ne 'on'){
$local_state = $member->derive_link_state($link_state);
}
$member->set_receive($local_state,$self);
}
}
}


}

=item C<set_with_timer(state, time, return_state, additional_return_states)>
Expand Down Expand Up @@ -3239,7 +3214,7 @@ sub update_members
# if they are Light_Items, then locate the Light_Item's Insteon_Device member
my @lights = $member->find_members('Insteon::BaseDevice');
if (@lights) {
$device = @lights[0];
$device = $lights[0];
}
} elsif ($member->isa('Insteon::BaseDevice')) {
$device = $member;
Expand Down Expand Up @@ -3281,22 +3256,6 @@ sub initiate_linking_as_controller
$self->interface()->initiate_linking_as_controller($p_group, $success_callback, $failure_callback);
}

=item C<derive_message([command,extra])>
Generates and returns a basic on/off message from a command.
=cut

sub derive_message
{
my ($self, $p_state, $p_extra) = @_;
if ($self->is_root) {
return $self->Insteon::BaseObject::derive_message($p_state, $p_extra);
} else {
return $self->Insteon::BaseObject::derive_message($p_state, $p_extra);
}
}

=item C<find_members([type])>
Returns a list of objects that are members of device. If type is specified, only
Expand Down Expand Up @@ -3406,28 +3365,6 @@ sub new
return $self;
}

=item C<set(state[,setby,response])>
If C<Insteon::BaseController::set> returns a true value returns that.
Else, calls C<Insteon::BaseObject::set> and returns 0
=cut

sub set
{
my ($self, $p_state, $p_setby, $p_respond) = @_;

my $rslt_code = $self->Insteon::BaseController::set($p_state, $p_setby, $p_respond);
return $rslt_code if $rslt_code;

my $link_state = &Insteon::BaseObject::derive_link_state($p_state);

$self->Insteon::BaseObject::set((($self->is_root) ? $p_state : $link_state), $p_setby, $p_respond);

return 0;
}

=item C<request_status([requestor])>
Requests the current status of the device and calls C<set()> on the response.
Expand Down Expand Up @@ -3521,31 +3458,32 @@ sub new
return $self;
}

=item C<set(state[,setby,response])>
If C<Insteon::BaseController::set> returns a true value returns that.
Else, calls C<Insteon::BaseObject::set> and returns 0
=cut

#Otherwise BaseController will call Generic_Item::Set
sub set
{
my ($self, $p_state, $p_setby, $p_respond) = @_;

my $rslt_code = $self->Insteon::BaseController::set($p_state, $p_setby, $p_respond);
return $rslt_code if $rslt_code;

$self->Insteon::BaseObject::set($p_state, $p_setby, $p_respond);

return 0;
my ($self,$p_state,$p_setby,$p_response) = @_;
$self->Insteon::BaseObject::set($p_state,$p_setby,$p_response);
}

sub is_root
{
return 0;
}

sub is_responder {
return 1;
}

# For IFaceControllers, need to call set_linked_devices
sub is_acknowledged {
my ($self, $p_ack) = @_;
if ($p_ack) {
$self->set_linked_devices($$self{pending_state}) if defined $$self{pending_state};
}
return $self->Insteon::BaseObject::is_acknowledged($p_ack);
}

=item C<get_voice_cmds>
Returns a hash of voice commands where the key is the voice command name and the
Expand Down
2 changes: 1 addition & 1 deletion lib/Insteon/BaseInterface.pm
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ sub on_standard_insteon_received
else
{
&main::print_log("[Insteon::BaseInterface] ERROR: received cleanup message from "
. $setby_object->get_object_name . "that does not correspond to a valid PLM group. Corrupted message is assumed "
. $setby_object->get_object_name . " that does not correspond to a valid PLM group. Corrupted message is assumed "
. "and will be skipped! Was group " . $msg{extra});
$setby_object->corrupt_count_log(1) if $setby_object->can('corrupt_count_log');
}
Expand Down
Loading

0 comments on commit 6bf1924

Please sign in to comment.