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

Add Support for IP Enabled Insteon Access Points #375

Merged
merged 7 commits into from
Mar 13, 2014
171 changes: 141 additions & 30 deletions lib/Insteon_PLM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ use Insteon::BaseInsteon;
use Insteon::AllLinkDatabase;
use Insteon::MessageDecoder;

@Insteon_PLM::ISA = ('Serial_Item','Insteon::BaseInterface');
#@Insteon_PLM::ISA = ('Serial_Item','Socket_Item','Insteon::BaseInterface');
my $PLM_socket = undef;


my %prefix = (
Expand Down Expand Up @@ -71,6 +72,10 @@ Creates a new serial port connection.

sub serial_startup {
my ($instance) = @_;
my $PLM_use_tcp =0;
$PLM_use_tcp = $::config_parms{$instance . "_use_TCP"};
if ($PLM_use_tcp == 1) {return;}

my $port = $::config_parms{$instance . "_serial_port"};
if (!defined($port)) {
main::print_log("WARN: ".$instance."_serial_port missing from INI params!");
Expand All @@ -92,8 +97,27 @@ sub new {
my ($class, $port_name, $p_deviceid) = @_;
$port_name = 'Insteon_PLM' if !$port_name;
my $port = $::config_parms{$port_name . "_serial_port"};
if (!defined($port)) {
main::print_log("WARN: ".$port_name."_serial_port missing from INI params!");
my $PLM_use_tcp =0;
$PLM_use_tcp = $::config_parms{$port_name . "_use_TCP"};
my $PLM_tcp_host = 0;
my $PLM_tcp_port = 0;


if ($PLM_use_tcp == 1)
{
@Insteon_PLM::ISA = ('Socket_Item','Insteon::BaseInterface');
$PLM_tcp_host = $::config_parms{$port_name . "_TCP_host"};
$PLM_tcp_port = $::config_parms{$port_name . "_TCP_port"};
&::print_log("[Insteon_PLM] 2412N using TCP, tcp_host=$PLM_tcp_host, tcp_port=$PLM_tcp_port");
}
else
{
if (!defined($port)) {
main::print_log("WARN: ".$port_name."_serial_port missing from INI params!");
}
@Insteon_PLM::ISA = ('Serial_Item','Insteon::BaseInterface');
$PLM_use_tcp =0;
&::print_log("[Insteon_PLM] 2412[US] using serial, serial_port=$port");
}

my $self = new Insteon::BaseInterface();
Expand All @@ -102,12 +126,23 @@ sub new {
$$self{state_now} = '';
$$self{port_name} = $port_name;
$$self{port} = $port;
$$self{use_tcp} = $PLM_use_tcp;
$$self{tcp_host} = $PLM_tcp_host;
$$self{tcp_port} = $PLM_tcp_port;
$$self{last_command} = '';
$$self{_prior_data_fragment} = '';
bless $self, $class;
$self->restore_data('debug', 'corrupt_count_log');
$$self{corrupt_count_log} = 0;
$$self{aldb} = new Insteon::ALDB_PLM($self);
if ($PLM_use_tcp == 1)
{
my $tcp_hostport = "$PLM_tcp_host:$PLM_tcp_port";

$PLM_socket = new Socket_Item(undef, undef, $tcp_hostport, 'Insteon PLM 2412N', 'tcp', 'raw');
start $PLM_socket;
$$self{socket} = $PLM_socket;
}

&Insteon::add($self);

Expand Down Expand Up @@ -186,9 +221,28 @@ calles C<process_queue()>.
sub check_for_data {

my ($self) = @_;
my $PLM_use_tcp =0;
#$PLM_use_tcp = $::config_parms{$self . "_use_TCP"};
$PLM_use_tcp = $$self{use_tcp};
my $port_name = $$self{port_name};
&::check_for_generic_serial_data($port_name) if $::Serial_Ports{$port_name}{object};
my $data = $::Serial_Ports{$port_name}{data};
my $data = undef;
if ($PLM_use_tcp == 1)
{
if ((not active $PLM_socket) and (($main::Second % 6) == 0) and $::New_Second)
{
&::print_log("[Insteon PLM] resetting socket connection");
start $PLM_socket;
}
$data = said $PLM_socket;
#&::print_log("[Insteon PLM] data recieved $data") if $data;

}
else
{
&::check_for_generic_serial_data($port_name) if $::Serial_Ports{$port_name}{object};
$data = $::Serial_Ports{$port_name}{data};
}

# always check for data first; if it exists, then process; otherwise check if pending commands exist
if ($data)
{
Expand Down Expand Up @@ -381,9 +435,22 @@ Causes a message to be sent to the serial port.
sub _send_cmd {
my ($self, $message, $cmd_timeout) = @_;
my $instance = $$self{port_name};
if (!(ref $main::Serial_Ports{$instance}{object})) {
my $PLM_use_tcp = $$self{use_tcp};
if ($PLM_use_tcp == 1)
{
#stop $PLM_socket;
if (not connected $PLM_socket)
{
&::print_log("[Insteon PLM] starting socket connection ");
start $PLM_socket;
}
}
else
{
if (!(ref $main::Serial_Ports{$instance}{object})) {
print "WARN: Insteon_PLM serial port not initialized!\n";
return;
}
}
unshift(@{$$self{command_history}},$::Time);
$self->transmit_in_progress(1);
Expand Down Expand Up @@ -425,8 +492,16 @@ sub _send_cmd {
&::print_log( "[Insteon_PLM] DEBUG3: Sending PLM raw data: ".lc($command)) if $debug_obj->debuglevel(3, 'insteon');
&::print_log( "[Insteon_PLM] DEBUG4:\n".Insteon::MessageDecoder::plm_decode($command)) if $debug_obj->debuglevel(4, 'insteon');
my $data = pack("H*",$command);
$main::Serial_Ports{$instance}{object}->write($data) if $main::Serial_Ports{$instance};

if ($PLM_use_tcp == 1)
{
my $port_name = $PLM_socket->{port_name};
my $sentBytes = $main::Socket_Ports{$port_name}{sock}->send($data) if $main::Socket_Ports{$port_name}{sock};
#print "Insteon_2412N $sentBytes bytes sent ($data)[$command]\n";
}
else
{
$main::Serial_Ports{$instance}{object}->write($data) if $main::Serial_Ports{$instance};
}

if ($delay) {
$self->_set_timeout('xmit',$delay * 1000);
Expand Down Expand Up @@ -527,7 +602,9 @@ sub _parse_data {
package main;
eval ($self->active_message->success_callback);
&::print_log("[Insteon_PLM] WARN1: Error encountered during ack callback: " . $@)
if $@ and $self->active_message->setby->debuglevel(1, 'insteon');
if ($@ && $self->active_message->can('setby')
&& ref $self->active_message->setby
&& $self->active_message->setby->debuglevel(1, 'insteon'));
package Insteon_PLM;
}
# clear the active message because we're done
Expand Down Expand Up @@ -566,7 +643,9 @@ sub _parse_data {
package main;
eval ($callback);
&::print_log("[Insteon_PLM] WARN1: Error encountered during ack callback: " . $@)
if $@ and $self->active_message->setby->debuglevel(1, 'insteon');
if ($@ && $self->active_message->can('setby')
&& ref $self->active_message->setby
&& $self->active_message->setby->debuglevel(1, 'insteon'));
package Insteon_PLM;
}
}
Expand Down Expand Up @@ -840,27 +919,29 @@ sub _parse_data {
{ #ALL-Link Cleanup Status Report
&::print_log( "[Insteon_PLM] DEBUG4:\n".Insteon::MessageDecoder::plm_decode($parsed_data)) if $self->debuglevel(4, 'insteon');
my $cleanup_ack = substr($message_data,0,2);
if ($cleanup_ack eq '15')
{
&::print_log("[Insteon_PLM] WARN1: All-link cleanup failure for scene: "
. $self->active_message->setby->get_object_name . ". Retrying in 1 second.")
if $self->active_message->setby->debuglevel(1, 'insteon');
$self->retry_active_message();
# except that we should cause a bit of a delay to let things settle out
$self->_set_timeout('xmit', 1000);
$process_next_command = 0;
}
else
{
my $message_to_string = ($self->active_message) ? $self->active_message->to_string() : "";
&::print_log("[Insteon_PLM] Received all-link cleanup success: $message_to_string")
if $self->active_message->setby->debuglevel(1, 'insteon');
if (ref $self->active_message && ref $self->active_message->setby){
my $object = $self->active_message->setby;
$object->is_acknowledged(1);
$object->_process_command_stack();
if (ref $self->active_message){
if ($cleanup_ack eq '15')
{
&::print_log("[Insteon_PLM] WARN1: All-link cleanup failure for scene: "
. $self->active_message->setby->get_object_name . ". Retrying in 1 second.")
if $self->active_message->setby->debuglevel(1, 'insteon');
$self->retry_active_message();
# except that we should cause a bit of a delay to let things settle out
$self->_set_timeout('xmit', 1000);
$process_next_command = 0;
}
else
{
my $message_to_string = ($self->active_message) ? $self->active_message->to_string() : "";
&::print_log("[Insteon_PLM] Received all-link cleanup success: $message_to_string")
if $self->active_message->setby->debuglevel(1, 'insteon');
if (ref $self->active_message && ref $self->active_message->setby){
my $object = $self->active_message->setby;
$object->is_acknowledged(1);
$object->_process_command_stack();
}
$self->clear_active_message();
}
$self->clear_active_message();
}
}
elsif (substr($parsed_data,0,2) eq '15')
Expand Down Expand Up @@ -976,6 +1057,36 @@ Identifies the port on which the PLM is attached. Example:

Insteon_PLM_serial_port=/dev/ttyS4

=item Insteon_PLM_use_TCP

Setting this to 1, will enable MisterHouse to use a networked PLM such as the
Insteon Hub. This functionality seems fairly stable, but has not been
extensively tested.

You will also need to set values for C<Insteon_PLM_TCP_host> and
C<Insteon_PLM_TCP_port>.

There are a few quirks when using a networked PLM, they include:

The communication may be slightly slower with the network PLM. In order to
prevent MisterHouse from clobbering the device it is recommended that you
set the C<Insteon_PLM_xmit_delay> to 1 second. Testing may reveal that slightly
lower delays are also acceptable.

Changes made using the hub's web interface will not be understood by MisterHouse.
Device states may become out of sync. (It is possible that future coding may
be able to overcome this limiation)

=item Insteon_PLM_TCP_host

If using a network PLM, set this to the IP address of the PLM. See
C<Insteon_PLM_use_TCP>.

=item Insteon_PLM_TCP_port

If using a network PLM, set this to the port address of the PLM. Generally, the
port number is 9761. See C<Insteon_PLM_use_TCP>.

=item Insteon_PLM_xmit_delay

Sets the minimum amount of seconds that must elapse between sending Insteon messages
Expand Down