Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to IOLinc Module, Significant Changes in Configuration #219

Merged
merged 5 commits into from
Jun 8, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 104 additions & 38 deletions lib/Insteon/IOLinc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ INITIAL CONFIGURATION
In user code:

use Insteon::IOLinc;
$io_device = new Insteon::IOLinc('12.34.56:01',$myPLM);
$io_device_sensor = new Insteon::IOLinc('12.34.56:02',$myPLM);
$io_device = new Insteon::IOLinc('12.34.56',$myPLM);

In items.mht:

INSTEON_IOLINC, 12.34.56:01, io_device, io_group
INSTEON_IOLINC, 12.34.56:02, io_device_sensor, io_group
INSTEON_IOLINC, 12.34.56, io_device, io_group

Where io_device is the relay and io_device_sensor is the sensor.
Where commands sent to io_device control the relay, however commands received
from io_device represent the sensor state..

EXAMPLE USAGE

Expand All @@ -26,24 +25,61 @@ Turning off a relay:

Requesting sensor status:

$io_device_sensor->request_status();
$io_device->request_sensor_status();

If the sensor is defined in the user code or mht file as described above, and
linked to MH using the sync_links voice command, the sensor will automatically
send state changes to MisterHouse whenever its state changes. In that instance
the request_status command should not be needed.
The IOLinc is a strange device in that commands sent to it control one aspect
of the device, but commands received from it are from another aspect of the
device.

Print the Current Device Settings to the log:
$io_device->get_operating_flag();

LINKING

As a result of the IOLinc's oddities, when the IOLinc is set as a controller
of another device, that other device will be controlled by the sensor state.
However, when the IOLinc is set as a responder in a link, the relay of the
IOLinc will change with the commands sent by the controller.

STATE REPORTED IN MisterHouse

MisterHouse objects are only designed to hold the state of a single aspect. As a
result of the IOLinc's oddities, the $io_device defined using the examples above
will track the state of the relay only. The state of the sensor can be obtained
using the C<request_sensor_status()> command.

One more oddity is that using the "set" button on the side of the device to
change the state of the relay, will cause MH to perceive this as a change in
the state of the sensor, thus placing the sensor and relay objects out of sync.

SENSOR STATE CHILD OBJECT

To create a device that directly tracks the state of the sensor, you can use
the following code to create a generic child object. The state of the child
object will reflect the state of the sensor and it will be automatically updated
as long as the IOLinc is linked to the PLM. Tie_events can be used on this
child object. However, if you want to directly link an obect to the sensor
be sure to use the normal SCENE_MEMBER code in your mht file with the IOLinc
defined as the controller.

User Code:

$io_device_sensor = new Insteon::IOLinc_sensor($io_device);

Where $io_device is the parent device defined above.

NOTES

This module works with the Insteon IOLinc device from Smarthome. The EZIO device
uses a different set of commands and this code will offer only limited, if any
support at all, for EZIO devices.

The state that the relay is in when the device is linked to the PLM matters if
you are using relay mode Momentary_A.
you are using relay mode Momentary_A.

BUGS

The relay state will not be accurate if you are using a momentary mode.

=over
=cut
Expand Down Expand Up @@ -92,38 +128,38 @@ sub new
$$self{operating_flags} = \%operating_flags;
$$self{message_types} = \%message_types;
bless $self, $class;
if ($self->group ne '02' && !$self->is_root){
::print_log("[Insteon::IOLinc] Warning IOLincs with more than "
. " 1 input and 1 output are not yet supported by this code.");
}
if ($self->is_root){
$$self{momentary_time} = 20;
$$self{relay_linked} = 0;
$$self{trigger_reverse} = 0;
$$self{relay_mode} = 'Latching';
}
$self->restore_data('momentary_time');
$$self{momentary_timer} = new Timer;
return $self;
}

sub set
{
my ($self, $p_state, $p_setby, $p_respond) = @_;
my $link_state = &Insteon::BaseObject::derive_link_state($p_state);
return $self->Insteon::BaseDevice::set($link_state, $p_setby, $p_respond);
#Commands sent by the IOLinc itself represent the sensor
#Commands sent by MH to IOLinc represent the relay
if (ref $p_setby && $p_setby->isa('Insteon::BaseObject') && $p_setby->equals($self)){
::print_log("[Insteon::IOLinc] Received ". $self->get_object_name
. " sensor " . $p_state . " message.");
if (ref $$self{child_sensor}){
$$self{child_sensor}->set_receive($p_state, $p_setby, $p_respond);
}
}
else {
my $link_state = &Insteon::BaseObject::derive_link_state($p_state);
$self->Insteon::BaseDevice::set($link_state, $p_setby, $p_respond);
#$$self{momentary_timer}->set(int($$self{momentary_time/10), '$self->Generic_Item::set('off')');
}
return;
}

sub request_status
sub request_sensor_status
{
my ($self, $requestor) = @_;
if (!($self->is_root)) {
my $parent = $self->get_root();
$$parent{child_status_request_pending} = $self->group;
$$self{m_status_request_pending} = ($requestor) ? $requestor : 1;
my $message = new Insteon::InsteonMessage('insteon_send', $parent, 'status_request', '01');
$parent->_send_cmd($message);
} else {
$self->SUPER::request_status($requestor);
}
$$self{child_status_request_pending} = $self->group;
$$self{m_status_request_pending} = ($requestor) ? $requestor : 1;
my $message = new Insteon::InsteonMessage('insteon_send', $self, 'status_request', '01');
$self->_send_cmd($message);
}

sub _is_info_request
Expand All @@ -133,13 +169,14 @@ sub _is_info_request
my $parent = $self->get_root();
if ($$parent{child_status_request_pending}) {
$is_info_request++;
my $child_obj = Insteon::get_object($self->device_id, '02');
my $child_state = &Insteon::BaseObject::derive_link_state(hex($msg{extra}));
&::print_log("[Insteon::IOLinc] received status for " .
$child_obj->{object_name} . " of: $child_state "
$self->get_object_name . "sensor of: $child_state "
. "hops left: $msg{hopsleft}") if $main::Debug{insteon};
$ack_setby = $$child_obj{m_status_request_pending} if ref $$child_obj{m_status_request_pending};
$child_obj->SUPER::set($child_state, $ack_setby);
$ack_setby = $$self{child_sensor} if ref $$self{child_sensor};
if (ref $$self{child_sensor}){
$$self{child_sensor}->set_receive($child_state, $ack_setby);
}
delete($$parent{child_status_request_pending});
}
elsif ($cmd eq 'get_operating_flags') {
Expand Down Expand Up @@ -168,7 +205,14 @@ sub _is_info_request
sub _process_message {
my ($self,$p_setby,%msg) = @_;
my $clear_message = 0;
if ($msg{command} eq "extended_set_get" && $msg{is_ack}){
my $pending_cmd = ($$self{_prior_msg}) ? $$self{_prior_msg}->command : $msg{command};
my $ack_setby = (ref $$self{m_status_request_pending}) ? $$self{m_status_request_pending} : $p_setby;
if ($msg{is_ack} && $self->_is_info_request($pending_cmd,$ack_setby,%msg)) {
$clear_message = 1;
$$self{m_status_request_pending} = 0;
$self->_process_command_stack(%msg);
}
elsif ($msg{command} eq "extended_set_get" && $msg{is_ack}){
$self->default_hop_count($msg{maxhops}-$msg{hopsleft});
#If this was a get request don't clear until data packet received
main::print_log("[Insteon::IOLinc] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon};
Expand Down Expand Up @@ -239,6 +283,7 @@ sub set_momentary_time
$$root{_ext_set_get_action} = "set";
my $message = new Insteon::InsteonMessage('insteon_ext_send', $root, 'extended_set_get', $extra);
$root->_send_cmd($message);
$$self{momentary_time} = $momentary_time;
return;
}

Expand Down Expand Up @@ -338,6 +383,7 @@ sub set_relay_mode
$parent->set_operating_flag('momentary_a_off');
$parent->set_operating_flag('momentary_b_off');
$parent->set_operating_flag('momentary_c_off');
$$self{momentary_time} = 0;
}
elsif (lc($relay_mode) eq 'momentary_a'){
$parent->set_operating_flag('momentary_b_off');
Expand All @@ -357,6 +403,26 @@ sub set_relay_mode
return;
}

package Insteon::IOLinc_sensor;
use strict;

@Insteon::IOLinc_sensor::ISA = ('Generic_Item');

sub new {
my ($class, $parent) = @_;
my $self = new Generic_Item();
bless $self, $class;
$$self{parent} = $parent;
$$self{parent}{child_sensor} = $self;
return $self;
}

sub set_receive {
my ($self, $p_state, $p_setby, $p_respond) = @_;
$self->SUPER::set($p_state, $p_setby, $p_respond);
}


1;
=back
=cut