diff --git a/lib/Insteon/IOLinc.pm b/lib/Insteon/IOLinc.pm index e6990a467..11837ab72 100755 --- a/lib/Insteon/IOLinc.pm +++ b/lib/Insteon/IOLinc.pm @@ -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 @@ -26,16 +25,49 @@ 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 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 @@ -43,7 +75,11 @@ 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 @@ -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 @@ -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') { @@ -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}; @@ -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; } @@ -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'); @@ -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