From 0e979b37b3d961e1e9405c018e65d21cd5127add Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 28 Jan 2013 09:25:50 -0800 Subject: [PATCH 01/86] Add milliseconds to Message Decoder Output Messages arrive very very fast. Adding milliseconds to the print log helps debug what is going on. --- lib/Insteon_PLM.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index ac24168e5..267721fc6 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -309,7 +309,7 @@ sub _send_cmd { } &::print_log( "[Insteon_PLM] DEBUG3: Sending PLM raw data: ".lc($command)) if $main::Debug{insteon} >= 3; - &::print_log( "[Insteon_PLM] DEBUG4:\n".Insteon::MessageDecoder::plm_decode($command)) if $main::Debug{insteon} >= 4; + &::print_log( "[Insteon_PLM] DEBUG4: " . substr(&main::get_tickcount-1,-6) . "\n" .Insteon::MessageDecoder::plm_decode($command)) if $main::Debug{insteon} >= 4; my $data = pack("H*",$command); $main::Serial_Ports{$instance}{object}->write($data) if $main::Serial_Ports{$instance}; @@ -343,7 +343,7 @@ sub _parse_data { } &::print_log( "[Insteon_PLM] DEBUG3: Received PLM raw data: $data") if $main::Debug{insteon} >= 3; - &::print_log( "[Insteon_PLM] DEBUG4:\n".Insteon::MessageDecoder::plm_decode($data)) if $main::Debug{insteon} >= 4; + &::print_log( "[Insteon_PLM] DEBUG4: ". substr(&main::get_tickcount-1,-6) . "\n" .Insteon::MessageDecoder::plm_decode($data)) if $main::Debug{insteon} >= 4; # begin by pulling out any PLM ack/nacks my $prev_cmd = ''; @@ -707,4 +707,4 @@ sub firmware { } -1; \ No newline at end of file +1; From f92fc5fc2c1fae669823d715d799e4bf350daba0 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 6 Feb 2013 18:55:35 -0800 Subject: [PATCH 02/86] Fix decimal places in milliseconds Trailing zeros were previously omitted. Printing the milliseconds helps a lot in debugging corrupt messages --- lib/Insteon_PLM.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index 267721fc6..2e96cd5e0 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -309,7 +309,7 @@ sub _send_cmd { } &::print_log( "[Insteon_PLM] DEBUG3: Sending PLM raw data: ".lc($command)) if $main::Debug{insteon} >= 3; - &::print_log( "[Insteon_PLM] DEBUG4: " . substr(&main::get_tickcount-1,-6) . "\n" .Insteon::MessageDecoder::plm_decode($command)) if $main::Debug{insteon} >= 4; + &::print_log( "[Insteon_PLM] DEBUG4: Milliseconds " . substr(sprintf('%.2f', &main::get_tickcount),-6) . "\n" .Insteon::MessageDecoder::plm_decode($command)) if $main::Debug{insteon} >= 4; my $data = pack("H*",$command); $main::Serial_Ports{$instance}{object}->write($data) if $main::Serial_Ports{$instance}; @@ -343,7 +343,7 @@ sub _parse_data { } &::print_log( "[Insteon_PLM] DEBUG3: Received PLM raw data: $data") if $main::Debug{insteon} >= 3; - &::print_log( "[Insteon_PLM] DEBUG4: ". substr(&main::get_tickcount-1,-6) . "\n" .Insteon::MessageDecoder::plm_decode($data)) if $main::Debug{insteon} >= 4; + &::print_log( "[Insteon_PLM] DEBUG4: Milliseconds ". substr(sprintf('%.2f', &main::get_tickcount),-6) . "\n" .Insteon::MessageDecoder::plm_decode($data)) if $main::Debug{insteon} >= 4; # begin by pulling out any PLM ack/nacks my $prev_cmd = ''; From d1a3c3c01d79b56a3779a1a539906bb77b14e6dd Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 12:22:58 -0800 Subject: [PATCH 03/86] Move Old Insteon Thermostat Code to Insteon lib --- lib/{Insteon_Thermostat.pm => Insteon/Thermostat.pm} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{Insteon_Thermostat.pm => Insteon/Thermostat.pm} (100%) diff --git a/lib/Insteon_Thermostat.pm b/lib/Insteon/Thermostat.pm similarity index 100% rename from lib/Insteon_Thermostat.pm rename to lib/Insteon/Thermostat.pm From abf31522940968835434db38749cbe246afa778f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 12:40:05 -0800 Subject: [PATCH 04/86] Recode Initialization of Inteon Thermostat Object --- lib/Insteon/Thermostat.pm | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index bd4885e49..d5eb0ce1e 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -10,7 +10,7 @@ In user code: In items.mht: -IPLT, 12.34.56, thermostat, HVAC, plm +INSTEON_THERMOSTAT, 12.34.56, thermostat, HVAC BUGS @@ -94,20 +94,21 @@ All of the functions available: - Manage aldb - should be able to adjust setpoints based on plm scene. <- may be overkill =cut -use strict; +package Insteon::Thermostat; -package Insteon_Thermostat; +use strict; +use Insteon::BaseInsteon; -@Insteon_Thermostat::ISA = ('Insteon_Device'); +@Insteon::Thermostat::ISA = ('Insteon::DeviceController','Insteon::BaseDevice'); # -------------------- START OF SUBROUTINES -------------------- # -------------------------------------------------------------- sub new { - my ($class, $p_interface, $p_deviceid, $p_devcat) = @_; + my ($class, $p_deviceid, $p_interface) = @_; - my $self = $class->SUPER::new($p_interface, $p_deviceid, $p_devcat); + my $self = new Insteon::BaseDevice($p_deviceid,$p_interface) bless $self, $class; $$self{temp} = undef; $$self{mode} = undef; From 364c9324c5fac1b431f01942102628a27bbaf201 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 13:17:20 -0800 Subject: [PATCH 05/86] Convert all calls to _send_cmd to use Message Class --- lib/Insteon/Thermostat.pm | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index d5eb0ce1e..fe31267ad 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -122,8 +122,8 @@ sub new { sub poll_mode { my ($self) = @_; - my $subcmd = '02'; - $self->_send_cmd(command => 'thermostat_get_mode', type => 'standard', extra => $subcmd, 'is_synchronous' => 1); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_mode', '02'); + $self->_send_cmd($message); return; } @@ -150,7 +150,8 @@ sub mode{ print "Insteon_Thermostat: Invalid Mode state: $state\n"; return(); } - $self->_send_cmd(command => 'thermostat_control', type => 'standard', extra => $mode); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $mode); + $self->_send_cmd($message); } sub fan{ @@ -160,15 +161,16 @@ sub fan{ my $fan; if (($state eq 'on') or ($state eq 'fan_on')) { $fan = '07'; - $state = 'fan_on'; + $state = 'fan_on'; } elsif ($state eq 'auto' or $state eq 'off' or $state eq 'fan_auto') { $fan = '08'; - $state = 'fan_auto'; + $state = 'fan_auto'; } else { print "Insteon_Thermostat: Invalid Fan state: $state\n"; return(); } - $self->_send_cmd(command => 'thermostat_control', type => 'standard', extra => $fan); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $fan); + $self->_send_cmd($message); } sub cool_setpoint{ @@ -178,8 +180,8 @@ sub cool_setpoint{ print "$::Time_Date: [Insteon_Thermostat] ERROR: cool_setpoint $temp not numeric\n"; return; } - - $self->_send_cmd(command => 'thermostat_setpoint_cool', type => 'standard', extra => sprintf('%02X',($temp*2))); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); + $self->_send_cmd($message); } sub heat_setpoint{ @@ -189,14 +191,14 @@ sub heat_setpoint{ print "$::Time_Date: [Insteon_Thermostat] ERROR: heat_setpoint $temp not numeric\n"; return; } - - $self->_send_cmd(command => 'thermostat_setpoint_heat', type => 'standard', extra => sprintf('%02X',($temp*2))); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); + $self->_send_cmd($message); } sub poll_temp { my ($self) = @_; - my $subcmd = '00'; - $self->_send_cmd(command => 'thermostat_get_zone_temp', type => 'standard', extra => $subcmd, 'is_synchronous' => 1); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_zone_temp', '00'); + $self->_send_cmd($message); return; } @@ -212,8 +214,8 @@ sub get_temp() { sub poll_setpoint { my ($self) = @_; $self->poll_mode(); - my $subcmd = '20'; - $self->_send_cmd(command => 'thermostat_get_zone_setpoint', type => 'standard', extra => $subcmd, 'is_synchronous' => 1); + my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_zone_setpoint', '20'); + $self->_send_cmd($message); return; } From 95780d9f508781782896beed41b0697c256d795a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 13:36:58 -0800 Subject: [PATCH 06/86] Convert Print Log Messages to the Proper Format --- lib/Insteon/Thermostat.pm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index fe31267ad..ea70b098e 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -130,7 +130,7 @@ sub poll_mode { sub mode{ my ($self, $state) = @_; $state = lc($state); - print "$::Time_Date: Insteon_Thermostat -> Mode $state\n" unless $main::config_parms{no_log} =~/Insteon_Thermostat/; + main::print_log("[Insteon_Thermostat] Mode $state") if $main::Debug{insteon}; my $mode; if ($state eq 'off') { $mode = "09"; @@ -147,7 +147,7 @@ sub mode{ } elsif ($state eq 'program_auto') { $mode = "0c"; } else { - print "Insteon_Thermostat: Invalid Mode state: $state\n"; + main::print_log("[Insteon_Thermostat] ERROR: Invalid Mode state: $state"); return(); } my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $mode); @@ -157,7 +157,7 @@ sub mode{ sub fan{ my ($self, $state) = @_; $state = lc($state); - print "$::Time_Date: Insteon_Thermostat -> Fan $state\n" unless $main::config_parms{no_log} =~/Insteon_Thermostat/; + main::print_log("[Insteon_Thermostat] Fan $state") if $main::Debug{insteon}; my $fan; if (($state eq 'on') or ($state eq 'fan_on')) { $fan = '07'; @@ -166,7 +166,7 @@ sub fan{ $fan = '08'; $state = 'fan_auto'; } else { - print "Insteon_Thermostat: Invalid Fan state: $state\n"; + main::print_log("[Insteon_Thermostat] ERROR: Invalid Fan state: $state"); return(); } my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $fan); @@ -175,9 +175,9 @@ sub fan{ sub cool_setpoint{ my ($self, $temp) = @_; - print "$::Time_Date: [Insteon_Thermostat] Cool setpoint -> $temp\n" unless $main::config_parms{no_log} =~/Insteon_Thermostat/; + main::print_log("[Insteon_Thermostat] Cool setpoint -> $temp") if $main::Debug{insteon}; if($temp !~ /^\d+$/){ - print "$::Time_Date: [Insteon_Thermostat] ERROR: cool_setpoint $temp not numeric\n"; + main::print_log("[Insteon_Thermostat] ERROR: cool_setpoint $temp not numeric"); return; } my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); @@ -186,9 +186,9 @@ sub cool_setpoint{ sub heat_setpoint{ my ($self, $temp) = @_; - print "$::Time_Date: [Insteon_Thermostat] Heat setpoint -> $temp\n" unless $main::config_parms{no_log} =~/Insteon_Thermostat/; + main::print_log("[Insteon_Thermostat] Heat setpoint -> $temp") if $main::Debug{insteon}; if($temp !~ /^\d+$/){ - print "$::Time_Date: [Insteon_Thermostat] ERROR: heat_setpoint $temp not numeric\n"; + main::print_log("[Insteon_Thermostat] ERROR: heat_setpoint $temp not numeric"); return; } my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); @@ -282,7 +282,7 @@ sub _is_info_request { or $cmd eq 'thermostat_get_mode' or $cmd eq 'thermostat_get_temp') ? 1 : 0; if ($is_info_request) { my $val = $msg{extra}; - &::print_log("[Insteon_Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; + main::print_log("[Insteon_Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; if ($cmd eq 'thermostat_get_temp' or $cmd eq 'thermostat_get_zone_temp') { $val = (hex $val) / 2; # returned value is twice the real value if (exists $$self{'temp'} and ($$self{'temp'} != $val)) { From ac0e056b76385ea1d4536106f7ec73cfadf3dbea Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 21:54:53 -0800 Subject: [PATCH 07/86] Add Thermostat Message Types --- lib/Insteon/Thermostat.pm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index ea70b098e..0e9c90d1c 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -105,6 +105,20 @@ use Insteon::BaseInsteon; # -------------------- START OF SUBROUTINES -------------------- # -------------------------------------------------------------- +my %message_types = ( + %Insteon::BaseDevice::message_types, + thermostat_temp_up => 0x68, + thermostat_temp_down => 0x69, + thermostat_get_zone_temp => 0x6a, + thermostat_get_zone_setpoint => 0x6a, + thermostat_get_zone_humidity => 0x6a, + thermostat_control => 0x6b, + thermostat_get_mode => 0x6b, + thermostat_get_temp => 0x6b, + thermostat_setpoint_cool => 0x6c, + thermostat_setpoint_heat => 0x6d +); + sub new { my ($class, $p_deviceid, $p_interface) = @_; From 96baeabbc4d0a74619ca35cc045093a654e6e462 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 22:10:31 -0800 Subject: [PATCH 08/86] Revise Read_Table_A to Point to Thermostat.pm --- lib/read_table_A.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/read_table_A.pl b/lib/read_table_A.pl index f86834765..e4c96d264 100644 --- a/lib/read_table_A.pl +++ b/lib/read_table_A.pl @@ -150,10 +150,10 @@ sub read_table_A { } } elsif($type eq 'IPLT' or $type eq 'INSTEON_THERMOSTAT') { - require 'Insteon_Thermostat.pm'; + require Insteon::Thermostat; ($address, $name, $grouplist, $object, @other) = @item_info; $other = join ', ', (map {"'$_'"} @other); # Quote data - $object = "Insteon_Thermostat(\$$object, \'$address\', $other)"; + $object = "Insteon::Thermostat(\'$address\', $other)"; } elsif($type eq "INSTEON_IRRIGATION") { require 'Insteon_Irrigation.pm'; From 06f3fa8e923979756236afaaf8e764e3c12d3ef7 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 2 Mar 2013 22:14:07 -0800 Subject: [PATCH 09/86] Fix Missing Semicolon --- lib/Insteon/Thermostat.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 0e9c90d1c..5b2f20f07 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -122,7 +122,7 @@ my %message_types = ( sub new { my ($class, $p_deviceid, $p_interface) = @_; - my $self = new Insteon::BaseDevice($p_deviceid,$p_interface) + my $self = new Insteon::BaseDevice($p_deviceid,$p_interface); bless $self, $class; $$self{temp} = undef; $$self{mode} = undef; From cc9cc32b54eac64ada9cf75e449f10c62ebc76d5 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 4 Mar 2013 19:25:33 -0800 Subject: [PATCH 10/86] Fix Message Creation, Add Awaiting Ack for Thermostat --- lib/Insteon/BaseInsteon.pm | 2 ++ lib/Insteon/Thermostat.pm | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index c57e7abe5..7e8c42678 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -516,6 +516,7 @@ sub _process_message } else { +main::print_log("[krk2] " . $self->get_object_name); # allow non-synchronous messages to also use the _is_info_request hook $self->_is_info_request($pending_cmd,$p_setby,%msg); $self->is_acknowledged(1); @@ -597,6 +598,7 @@ sub _process_command_stack or $message->command eq 'status_request' or $message->command eq 'do_read_ee' or $message->command eq 'set_address_msb' + or $message->command eq 'thermostat_get_mode' ) { $$self{awaiting_ack} = 1; diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 5b2f20f07..556e67194 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -130,13 +130,14 @@ sub new { $$self{heat_sp} = undef; $$self{cool_sp} = undef; $self->restore_data('temp','mode','fan_mode','heat_sp','cool_sp'); - $$self{m_pending_setpoint} = undef; + $$self{m_pending_setpoint} = undef; + $$self{message_types} = \%message_types; return $self; } sub poll_mode { my ($self) = @_; - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_mode', '02'); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_mode', '02'); $self->_send_cmd($message); return; } @@ -164,7 +165,7 @@ sub mode{ main::print_log("[Insteon_Thermostat] ERROR: Invalid Mode state: $state"); return(); } - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $mode); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $mode); $self->_send_cmd($message); } @@ -183,7 +184,7 @@ sub fan{ main::print_log("[Insteon_Thermostat] ERROR: Invalid Fan state: $state"); return(); } - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_control', $fan); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $fan); $self->_send_cmd($message); } @@ -194,7 +195,7 @@ sub cool_setpoint{ main::print_log("[Insteon_Thermostat] ERROR: cool_setpoint $temp not numeric"); return; } - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); $self->_send_cmd($message); } @@ -205,13 +206,13 @@ sub heat_setpoint{ main::print_log("[Insteon_Thermostat] ERROR: heat_setpoint $temp not numeric"); return; } - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); $self->_send_cmd($message); } sub poll_temp { my ($self) = @_; - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_zone_temp', '00'); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_temp', '00'); $self->_send_cmd($message); return; } @@ -228,7 +229,7 @@ sub get_temp() { sub poll_setpoint { my ($self) = @_; $self->poll_mode(); - my $message = new Insteon::InsteonMessage('insteon_send', $$self{device}, 'thermostat_get_zone_setpoint', '20'); + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_setpoint', '20'); $self->_send_cmd($message); return; } From ad0d3b798ba53e63226c50cae755b82c4c68aef3 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 4 Mar 2013 20:09:55 -0800 Subject: [PATCH 11/86] Condense Duplicate Thermostat Message Types --- lib/Insteon/BaseInsteon.pm | 3 ++- lib/Insteon/Thermostat.pm | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 7e8c42678..e21132cf1 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -598,7 +598,8 @@ sub _process_command_stack or $message->command eq 'status_request' or $message->command eq 'do_read_ee' or $message->command eq 'set_address_msb' - or $message->command eq 'thermostat_get_mode' + or $message->command eq 'thermostat_control' + or $message->command eq 'thermostat_get_zone_info' ) { $$self{awaiting_ack} = 1; diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 556e67194..080644869 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -109,12 +109,8 @@ my %message_types = ( %Insteon::BaseDevice::message_types, thermostat_temp_up => 0x68, thermostat_temp_down => 0x69, - thermostat_get_zone_temp => 0x6a, - thermostat_get_zone_setpoint => 0x6a, - thermostat_get_zone_humidity => 0x6a, + thermostat_get_zone_info => 0x6a, thermostat_control => 0x6b, - thermostat_get_mode => 0x6b, - thermostat_get_temp => 0x6b, thermostat_setpoint_cool => 0x6c, thermostat_setpoint_heat => 0x6d ); @@ -137,7 +133,8 @@ sub new { sub poll_mode { my ($self) = @_; - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_mode', '02'); + $$self{_control_action} = "mode"; + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', '02'); $self->_send_cmd($message); return; } @@ -212,7 +209,8 @@ sub heat_setpoint{ sub poll_temp { my ($self) = @_; - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_temp', '00'); + $$self{_zone_action} = "temp"; + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_info', '00'); $self->_send_cmd($message); return; } @@ -229,7 +227,8 @@ sub get_temp() { sub poll_setpoint { my ($self) = @_; $self->poll_mode(); - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_setpoint', '20'); + $$self{_zone_info} = "setpoint"; + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_info', '20'); $self->_send_cmd($message); return; } @@ -292,19 +291,18 @@ sub get_fan_mode() { sub _is_info_request { my ($self, $cmd, $ack_setby, %msg) = @_; - my $is_info_request = ($cmd eq 'thermostat_get_zone_temp' - or $cmd eq 'thermostat_get_zone_setpoint' or $cmd eq 'thermostat_get_zone_humidity' - or $cmd eq 'thermostat_get_mode' or $cmd eq 'thermostat_get_temp') ? 1 : 0; + my $is_info_request = ($cmd eq 'thermostat_get_zone_info' + or $cmd eq 'thermostat_control') ? 1 : 0; if ($is_info_request) { my $val = $msg{extra}; main::print_log("[Insteon_Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; - if ($cmd eq 'thermostat_get_temp' or $cmd eq 'thermostat_get_zone_temp') { + if ($$self{_zone_info} eq "temp") { $val = (hex $val) / 2; # returned value is twice the real value if (exists $$self{'temp'} and ($$self{'temp'} != $val)) { $self->set_receive('temp_change'); } $$self{'temp'} = $val; - } elsif ($cmd eq 'thermostat_get_mode') { + } elsif ($$self{_control_action} eq "mode") { if ($val eq '00') { $self->_mode('off'); } elsif ($val eq '01') { @@ -324,7 +322,7 @@ sub _is_info_request { } elsif ($val eq '08') { $self->_fan_mode('fan_auto'); } - } elsif ($cmd eq 'thermostat_get_zone_setpoint') { + } elsif ($$self{_zone_info} eq 'setpoint') { $val = (hex $val) / 2; # returned value is twice the real value # in auto modes, expect direct message with cool_setpoint to follow if ($self->get_mode() eq 'auto' or 'program_auto') { @@ -338,7 +336,8 @@ sub _is_info_request { } } - + $$self{_control_action} = undef; + $$self{_zone_action} = undef; return $is_info_request; } @@ -398,7 +397,7 @@ sub _process_message &::print_log("[Insteon_Thermostat] device category: $msg{devcat} received for " . $self->{object_name}); #stop ping timer now that we have a devcat; possibly may want to change this behavior to allow recurring pings $$self{ping_timer}->stop(); - } elsif ($msg{command} eq 'thermostat_get_zone_setpoint' && $$self{m_pending_setpoint}) { + } elsif ($$self{_zone_info} eq 'setpoint' && $$self{m_pending_setpoint}) { # we got our cool setpoint in auto mode my $val = (hex $msg{extra})/2; $self->_cool_sp($val); From 7a59f691237d195b407c1d7fc7d6a5ffb6dd4041 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Mar 2013 17:45:25 -0800 Subject: [PATCH 12/86] Fix Library Name in Print_Log Messages --- lib/Insteon/Thermostat.pm | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 080644869..8dac67758 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -142,7 +142,7 @@ sub poll_mode { sub mode{ my ($self, $state) = @_; $state = lc($state); - main::print_log("[Insteon_Thermostat] Mode $state") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Mode $state") if $main::Debug{insteon}; my $mode; if ($state eq 'off') { $mode = "09"; @@ -159,7 +159,7 @@ sub mode{ } elsif ($state eq 'program_auto') { $mode = "0c"; } else { - main::print_log("[Insteon_Thermostat] ERROR: Invalid Mode state: $state"); + main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); return(); } my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $mode); @@ -169,7 +169,7 @@ sub mode{ sub fan{ my ($self, $state) = @_; $state = lc($state); - main::print_log("[Insteon_Thermostat] Fan $state") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Fan $state") if $main::Debug{insteon}; my $fan; if (($state eq 'on') or ($state eq 'fan_on')) { $fan = '07'; @@ -178,7 +178,7 @@ sub fan{ $fan = '08'; $state = 'fan_auto'; } else { - main::print_log("[Insteon_Thermostat] ERROR: Invalid Fan state: $state"); + main::print_log("[Insteon::Thermostat] ERROR: Invalid Fan state: $state"); return(); } my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $fan); @@ -187,9 +187,9 @@ sub fan{ sub cool_setpoint{ my ($self, $temp) = @_; - main::print_log("[Insteon_Thermostat] Cool setpoint -> $temp") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Cool setpoint -> $temp") if $main::Debug{insteon}; if($temp !~ /^\d+$/){ - main::print_log("[Insteon_Thermostat] ERROR: cool_setpoint $temp not numeric"); + main::print_log("[Insteon::Thermostat] ERROR: cool_setpoint $temp not numeric"); return; } my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); @@ -198,9 +198,9 @@ sub cool_setpoint{ sub heat_setpoint{ my ($self, $temp) = @_; - main::print_log("[Insteon_Thermostat] Heat setpoint -> $temp") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Heat setpoint -> $temp") if $main::Debug{insteon}; if($temp !~ /^\d+$/){ - main::print_log("[Insteon_Thermostat] ERROR: heat_setpoint $temp not numeric"); + main::print_log("[Insteon::Thermostat] ERROR: heat_setpoint $temp not numeric"); return; } my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); @@ -295,7 +295,7 @@ sub _is_info_request { or $cmd eq 'thermostat_control') ? 1 : 0; if ($is_info_request) { my $val = $msg{extra}; - main::print_log("[Insteon_Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; if ($$self{_zone_info} eq "temp") { $val = (hex $val) / 2; # returned value is twice the real value if (exists $$self{'temp'} and ($$self{'temp'} != $val)) { @@ -349,7 +349,7 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $p_state = undef; -# &::print_log("[Insteon_Thermostat] _process_message Type: ".$msg{type}. +# &::print_log("[Insteon::Thermostat] _process_message Type: ".$msg{type}. # " Command: (" . $msg{command} . " CMD2: " .$msg{extra}) if $main::Debug{insteon}; #XXX # the current approach assumes that links from other controllers to some responder @@ -371,30 +371,30 @@ sub _process_message $self->is_acknowledged(1); # signal receipt of message to the command stack in case commands are queued $self->_process_command_stack(%msg); - &::print_log("[Insteon_Thermostat] received command/state (awaiting) acknowledge from " . $self->{object_name} + &::print_log("[Insteon::Thermostat] received command/state (awaiting) acknowledge from " . $self->{object_name} . ": $pending_cmd and data: $msg{extra}") if $main::Debug{insteon}; } } else { $self->is_acknowledged(1); # signal receipt of message to the command stack in case commands are queued $self->_process_command_stack(%msg); - &::print_log("[Insteon_Thermostat] received command/state acknowledge from " . $self->{object_name} + &::print_log("[Insteon::Thermostat] received command/state acknowledge from " . $self->{object_name} . ": " . (($msg{command}) ? $msg{command} : "(unknown)") . " and data: $msg{extra}") if $main::Debug{insteon}; } } elsif ($msg{is_nack}) { if ($$self{awaiting_ack}) { - &::print_log("[Insteon_Thermostat] WARN!! encountered a nack message for " . $self->{object_name} + &::print_log("[Insteon::Thermostat] WARN!! encountered a nack message for " . $self->{object_name} . " ... waiting for retry"); } else { - &::print_log("[Insteon_Thermostat] WARN!! encountered a nack message for " . $self->{object_name} + &::print_log("[Insteon::Thermostat] WARN!! encountered a nack message for " . $self->{object_name} . " ... skipping"); $self->is_acknowledged(0); $self->_process_command_stack(%msg); } } elsif ($msg{type} eq 'broadcast') { $self->devcat($msg{devcat}); - &::print_log("[Insteon_Thermostat] device category: $msg{devcat} received for " . $self->{object_name}); + &::print_log("[Insteon::Thermostat] device category: $msg{devcat} received for " . $self->{object_name}); #stop ping timer now that we have a devcat; possibly may want to change this behavior to allow recurring pings $$self{ping_timer}->stop(); } elsif ($$self{_zone_info} eq 'setpoint' && $$self{m_pending_setpoint}) { From 0914b7ac21b0dab1f23a44f8909a9f1338d9fd48 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Mar 2013 17:55:25 -0800 Subject: [PATCH 13/86] Remove Duplicate Entries in _is_info_request and _process_message Test for unique items first, then call the routine in SUPER to handle the remaining --- lib/Insteon/BaseInsteon.pm | 1 - lib/Insteon/Thermostat.pm | 86 +++++++++----------------------------- 2 files changed, 19 insertions(+), 68 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index e21132cf1..b7ff7c8e4 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -516,7 +516,6 @@ sub _process_message } else { -main::print_log("[krk2] " . $self->get_object_name); # allow non-synchronous messages to also use the _is_info_request hook $self->_is_info_request($pending_cmd,$p_setby,%msg); $self->is_acknowledged(1); diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 8dac67758..afa24cc27 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -334,86 +334,38 @@ sub _is_info_request { $self->_cool_sp($val); } } - + $$self{_control_action} = undef; + $$self{_zone_action} = undef; + } + else #This was not a thermostat info_request + { + #Check if this was a generic info_request + $is_info_request = $self->SUPER::_is_info_request($cmd, $ack_setby, %msg); } - $$self{_control_action} = undef; - $$self{_zone_action} = undef; return $is_info_request; } -# Need to handle some of these messages differently than Insteon_Device -# Trimming what I know we don't need, leaving what I'm unsure of. Still an excess -# of duplicated code. +## Unique messages handled first, non-unique sent to SUPER sub _process_message { my ($self,$p_setby,%msg) = @_; - my $p_state = undef; -# &::print_log("[Insteon::Thermostat] _process_message Type: ".$msg{type}. -# " Command: (" . $msg{command} . " CMD2: " .$msg{extra}) if $main::Debug{insteon}; #XXX - - # the current approach assumes that links from other controllers to some responder - # would be seen by the plm by also direct linking the controller as a responder - # and not putting the plm into monitor mode. This means that updating the state - # of the responder based upon the link controller's request is handled - # by Insteon_Link. - $$self{m_is_locally_set} = 1 if $msg{source} eq lc $self->device_id; - if ($msg{is_ack}) { - if ($$self{awaiting_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 ($self->_is_info_request($pending_cmd,$ack_setby,%msg)) { - $self->is_acknowledged(1); - $$self{m_status_request_pending} = 0; - $self->_process_command_stack(%msg); - } else { - $self->is_acknowledged(1); - # signal receipt of message to the command stack in case commands are queued - $self->_process_command_stack(%msg); - &::print_log("[Insteon::Thermostat] received command/state (awaiting) acknowledge from " . $self->{object_name} - . ": $pending_cmd and data: $msg{extra}") if $main::Debug{insteon}; - } - } else { - $self->is_acknowledged(1); - # signal receipt of message to the command stack in case commands are queued - $self->_process_command_stack(%msg); - &::print_log("[Insteon::Thermostat] received command/state acknowledge from " . $self->{object_name} - . ": " . (($msg{command}) ? $msg{command} : "(unknown)") - . " and data: $msg{extra}") if $main::Debug{insteon}; - } - } elsif ($msg{is_nack}) { - if ($$self{awaiting_ack}) { - &::print_log("[Insteon::Thermostat] WARN!! encountered a nack message for " . $self->{object_name} - . " ... waiting for retry"); - } else { - &::print_log("[Insteon::Thermostat] WARN!! encountered a nack message for " . $self->{object_name} - . " ... skipping"); - $self->is_acknowledged(0); - $self->_process_command_stack(%msg); - } - } elsif ($msg{type} eq 'broadcast') { - $self->devcat($msg{devcat}); - &::print_log("[Insteon::Thermostat] device category: $msg{devcat} received for " . $self->{object_name}); - #stop ping timer now that we have a devcat; possibly may want to change this behavior to allow recurring pings - $$self{ping_timer}->stop(); - } elsif ($$self{_zone_info} eq 'setpoint' && $$self{m_pending_setpoint}) { - # we got our cool setpoint in auto mode - my $val = (hex $msg{extra})/2; - $self->_cool_sp($val); - $$self{m_setpoint_pending} = 0; + my $clear_message = 0; + if ($$self{_zone_info} eq 'setpoint' && $$self{m_pending_setpoint}) { + # we got our cool setpoint in auto mode + my $val = (hex $msg{extra})/2; + $self->_cool_sp($val); + $$self{m_setpoint_pending} = 0; + $clear_message = 1; } else { - ## TO-DO: make sure that the state passed by command is something that is reasonable to set - $p_state = $msg{command}; - $$self{_pending_cleanup} = 1 if $msg{type} eq 'alllink'; -# $self->set($p_state, $p_setby) unless (lc($self->state) eq lc($p_state)) and - $self->set($p_state, $self) unless (lc($self->state) eq lc($p_state)) and - ($msg{type} eq 'cleanup' and $$self{_pending_cleanup}); - $$self{_pending_cleanup} = 0 if $msg{type} eq 'cleanup'; + $clear_message = $self->SUPER::_process_message($p_setby,%msg); } + return $clear_message; } # Overload methods we don't use, but would otherwise cause Insteon traffic. sub request_status { return 0 } +sub level { return 0 } + 1; From 181fc280faada372aa75a5c598f3abe33c9ca835 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 5 Mar 2013 16:05:25 -0800 Subject: [PATCH 14/86] Fix Typo in _zone_action tracking, allow for subsequent setpoint messages --- lib/Insteon/Thermostat.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index afa24cc27..ff2867516 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -296,7 +296,7 @@ sub _is_info_request { if ($is_info_request) { my $val = $msg{extra}; main::print_log("[Insteon::Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; - if ($$self{_zone_info} eq "temp") { + if ($$self{_zone_action} eq "temp") { $val = (hex $val) / 2; # returned value is twice the real value if (exists $$self{'temp'} and ($$self{'temp'} != $val)) { $self->set_receive('temp_change'); @@ -333,6 +333,11 @@ sub _is_info_request { } elsif ($self->get_mode() eq 'cool' or 'program_cool') { $self->_cool_sp($val); } + } elsif ($$self{'m_pending_setpoint'} == 1) { + #This is the second message with the cool_setpoint + $val = (hex $val) / 2; + $self->_cool_sp($val); + $$self{'m_pending_setpoint'} = undef; } $$self{_control_action} = undef; $$self{_zone_action} = undef; From 53cf854165d893442b8071216b93e6da50944a2b Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 6 Mar 2013 17:45:25 -0800 Subject: [PATCH 15/86] Add Documentation to Existing Code --- lib/Insteon/Thermostat.pm | 149 +++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 49 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index ff2867516..9884c43e8 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -1,27 +1,18 @@ -=begin comment +=head1 NAME -AUTHORS -Gregg Liming -Brian Warren - -INITIAL CONFIGURATION -In user code: - $thermostat = new Insteon_Thermostat($myPLM, '12.34.56'); - -In items.mht: - -INSTEON_THERMOSTAT, 12.34.56, thermostat, HVAC +B - Insteon Thermostat -BUGS +=head1 DESCRIPTION +Enables support for an Insteon Thermostat. -EXAMPLE USAGE -see code/examples/Insteon_thermostat.pl for more. - -Creating the object: +=head1 SYNOPSIS - $thermostat = new Insteon_Thermostat($myPLM, '12.34.56'); +In user code: + $thermostat = new Insteon_Thermostat($myPLM, '12.34.56'); +In items.mht: + INSTEON_THERMOSTAT, 12.34.56, thermostat, HVAC Poll for temperature changes. @@ -29,7 +20,6 @@ Poll for temperature changes. $thermostat->poll_temp(); } - Watch for temperature changes. if (state_now $thermostat eq 'temp_change') { @@ -57,41 +47,42 @@ All of the states that may be set: fan_mode_change: Fan mode changed (call get_fan_mode() to get value). -All of the functions available: - mode(): - Sets system mode to argument: 'off', 'heat', 'cool', 'auto', - 'program_heat', 'program_cool', 'program_auto' - poll_mode(): - Causes thermostat to return mode; detected as state change if mode changes - get_mode(): - Returns the last mode returned by poll_mode(). - fan(): - Sets fan to 'on' or 'auto' - get_fan_mode(): - Returns the current fan mode (fan_on or fan_auto) - poll_setpoint(): - Causes thermostat to return setpoint(s); detected as state change if setpoint changes - Returns setpoint based on mode, auto modes return both heat and cool. - cool_setpoint(): - Sets a new cool setpoint. - get_cool_sp(): - Returns the current cool setpoint. - heat_setpoint(): - Sets a new heat setpoint. - get_heat_sp(): - Returns the current heat setpoint. - poll_temp(): - Causes thermostat to return temp; detected as state change - get_temp(): - Returns the current temperature at the thermostat. - - -#TODO +see code/examples/Insteon_thermostat.pl for more. + +=head1 BUGS + +Initial code for Venstar thermostats, which use Insteon engine version i1, only +provided basic features. The new Insteon 2441TH thermostats use the i2cs engine +and only allow the polling, but not setting, of the thermostat attributes using +i2 code. As such, I am unable to test or provide enhancements to certain i1 +only aspects. + +=head1 AUTHOR + +Initial Code by: +Gregg Liming +Brian Warren + +Enhanced to i2 by: +Kevin Rober Keegan + +=head1 TODO + - Look at possible bugs when starting from factory defaults There seemed to be an issue with the setpoints changing when changing modes until they were set programatically. - Test fan modes and associated state_changes - Manage aldb - should be able to adjust setpoints based on plm scene. <- may be overkill + +=head1 INHERITS + +B + +B + +=head1 Methods + +=over =cut package Insteon::Thermostat; @@ -131,6 +122,10 @@ sub new { return $self; } +=item C + +Causes thermostat to return mode; detected as state change if mode changes +=cut sub poll_mode { my ($self) = @_; $$self{_control_action} = "mode"; @@ -139,6 +134,11 @@ sub poll_mode { return; } +=item C + +Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', +'program_cool', 'program_auto' +=cut sub mode{ my ($self, $state) = @_; $state = lc($state); @@ -166,6 +166,10 @@ sub mode{ $self->_send_cmd($message); } +=item C + +Sets fan to 'on' or 'auto' +=cut sub fan{ my ($self, $state) = @_; $state = lc($state); @@ -185,6 +189,10 @@ sub fan{ $self->_send_cmd($message); } +=item C + +Sets a new cool setpoint. +=cut sub cool_setpoint{ my ($self, $temp) = @_; main::print_log("[Insteon::Thermostat] Cool setpoint -> $temp") if $main::Debug{insteon}; @@ -196,6 +204,10 @@ sub cool_setpoint{ $self->_send_cmd($message); } +=item C + +Sets a new heat setpoint. +=cut sub heat_setpoint{ my ($self, $temp) = @_; main::print_log("[Insteon::Thermostat] Heat setpoint -> $temp") if $main::Debug{insteon}; @@ -207,6 +219,10 @@ sub heat_setpoint{ $self->_send_cmd($message); } +=item C + +Causes thermostat to return temp; detected as state change. +=cut sub poll_temp { my ($self) = @_; $$self{_zone_action} = "temp"; @@ -215,11 +231,20 @@ sub poll_temp { return; } +=item C + +Returns the current temperature at the thermostat. +=cut sub get_temp() { my ($self) = @_; return $$self{'temp'}; } +=item C + +Causes thermostat to return setpoint(s); detected as state change if setpoint changes. +Returns setpoint based on mode, auto modes return both heat and cool. +=cut # The setpoint is returned in 2 messages while in the auto modes. # The heat setpoint is returned in the ACK, which is followed by # a direct message containing the cool setpoint. Because of this, @@ -233,11 +258,19 @@ sub poll_setpoint { return; } +=item C + +Returns the current heat setpoint. +=cut sub get_heat_sp() { my ($self) = @_; return $$self{'heat_sp'}; } +=item C + +Returns the current cool setpoint. +=cut sub get_cool_sp() { my ($self) = @_; return $$self{'cool_sp'}; @@ -279,11 +312,19 @@ sub _mode() { return $$self{'mode'}; } +=item C + +Returns the last mode returned by C. +=cut sub get_mode() { my ($self) = @_; return $$self{'mode'}; } +=item C + +Returns the current fan mode (fan_on or fan_auto) +=cut sub get_fan_mode() { my ($self) = @_; return $$self{'fan_mode'}; @@ -374,3 +415,13 @@ sub request_status { return 0 } sub level { return 0 } 1; +=back + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=cut From d3544c0bb6d0cef78ddfca43822be3dd8ebee2e4 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 7 Mar 2013 17:45:25 -0800 Subject: [PATCH 16/86] Test out Extended Message for Mode --- lib/Insteon/Thermostat.pm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 9884c43e8..a6ad76886 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -162,8 +162,13 @@ sub mode{ main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); return(); } - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $mode); - $self->_send_cmd($message); + if ($self->_aldb->isa('Insteon::ALDB_i2'){ + my $extra = $mode . "00000000000000000000000000"; + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'thermostat_control', $extra); + } else { + my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $mode); + } + $self->_send_cmd($message); } =item C From 1aee18dfc310f84bac849069574334452eaad48d Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 7 Mar 2013 17:55:25 -0800 Subject: [PATCH 17/86] Create simple_message(), Use for all subs that set data simple_message() is a stop-gap to bring support of i1, i2, & i2cs thermostats up to the level provided before Insteon Redux. This should allow for quick merging into the stable codebase before release 3.0. Next steps will be to split the thermostat class into an i1 and i2 class. --- lib/Insteon/Thermostat.pm | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index a6ad76886..972c88228 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -162,13 +162,7 @@ sub mode{ main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); return(); } - if ($self->_aldb->isa('Insteon::ALDB_i2'){ - my $extra = $mode . "00000000000000000000000000"; - my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'thermostat_control', $extra); - } else { - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $mode); - } - $self->_send_cmd($message); + $self->_send_cmd($self->simple_message('thermostat_control', $mode)); } =item C @@ -190,8 +184,7 @@ sub fan{ main::print_log("[Insteon::Thermostat] ERROR: Invalid Fan state: $state"); return(); } - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_control', $fan); - $self->_send_cmd($message); + $self->_send_cmd($self->simple_message('thermostat_control', $fan)); } =item C @@ -205,8 +198,7 @@ sub cool_setpoint{ main::print_log("[Insteon::Thermostat] ERROR: cool_setpoint $temp not numeric"); return; } - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_cool', sprintf('%02X',($temp*2))); - $self->_send_cmd($message); + $self->_send_cmd($self->simple_message('thermostat_setpoint_cool', sprintf('%02X',($temp*2)))); } =item C @@ -220,8 +212,7 @@ sub heat_setpoint{ main::print_log("[Insteon::Thermostat] ERROR: heat_setpoint $temp not numeric"); return; } - my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_setpoint_heat', sprintf('%02X',($temp*2))); - $self->_send_cmd($message); + $self->_send_cmd($self->simple_message('thermostat_setpoint_heat', sprintf('%02X',($temp*2)))); } =item C @@ -414,6 +405,20 @@ sub _process_message return $clear_message; } +## Creates either a Standard or Extended Message depending on the device type +## Can be used to create different classes later +sub simple_message { + my ($self,$type,$extra) = @_; + my $message; + if ($self->_aldb->isa('Insteon::ALDB_i2')){ + $extra = $extra . "0000000000000000000000000000"; + $message = new Insteon::InsteonMessage('insteon_ext_send', $self, $type, $extra); + } else { + $message = new Insteon::InsteonMessage('insteon_send', $self, $type, $extra); + } + return $message; +} + # Overload methods we don't use, but would otherwise cause Insteon traffic. sub request_status { return 0 } From 1079eeda01a113f9e5d2acff2fbe01109ff39d2e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 7 Mar 2013 20:02:24 -0800 Subject: [PATCH 18/86] Final cleanup of Insteon Thermostat --- lib/Insteon/Thermostat.pm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 972c88228..24d0be61b 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -137,7 +137,8 @@ sub poll_mode { =item C Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', -'program_cool', 'program_auto' +'program_cool', 'program_auto'. The 2441TH thermostat does not have program_heat + or program_cool. =cut sub mode{ my ($self, $state) = @_; @@ -158,6 +159,7 @@ sub mode{ $mode = "0b"; } elsif ($state eq 'program_auto') { $mode = "0c"; + $mode = "0a" if $self->_aldb->isa('Insteon::ALDB_i2'); } else { main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); return(); @@ -248,7 +250,7 @@ Returns setpoint based on mode, auto modes return both heat and cool. sub poll_setpoint { my ($self) = @_; $self->poll_mode(); - $$self{_zone_info} = "setpoint"; + $$self{_zone_action} = "setpoint"; my $message = new Insteon::InsteonMessage('insteon_send', $self, 'thermostat_get_zone_info', '20'); $self->_send_cmd($message); return; @@ -310,7 +312,7 @@ sub _mode() { =item C -Returns the last mode returned by C. +Returns the last mode returned by C I2 devices will report auto for both auto and program_auto. =cut sub get_mode() { my ($self) = @_; @@ -359,7 +361,8 @@ sub _is_info_request { } elsif ($val eq '08') { $self->_fan_mode('fan_auto'); } - } elsif ($$self{_zone_info} eq 'setpoint') { + $$self{_control_action} = undef; + } elsif ($$self{_zone_action} eq 'setpoint') { $val = (hex $val) / 2; # returned value is twice the real value # in auto modes, expect direct message with cool_setpoint to follow if ($self->get_mode() eq 'auto' or 'program_auto') { @@ -370,14 +373,13 @@ sub _is_info_request { } elsif ($self->get_mode() eq 'cool' or 'program_cool') { $self->_cool_sp($val); } + $$self{_zone_action} = undef; } elsif ($$self{'m_pending_setpoint'} == 1) { #This is the second message with the cool_setpoint $val = (hex $val) / 2; $self->_cool_sp($val); $$self{'m_pending_setpoint'} = undef; } - $$self{_control_action} = undef; - $$self{_zone_action} = undef; } else #This was not a thermostat info_request { @@ -393,7 +395,7 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; - if ($$self{_zone_info} eq 'setpoint' && $$self{m_pending_setpoint}) { + if ($$self{_zone_action} eq 'setpoint' && $$self{m_pending_setpoint}) { # we got our cool setpoint in auto mode my $val = (hex $msg{extra})/2; $self->_cool_sp($val); From 012b54f1d411982d25188d98808958c1babdc526 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 8 Mar 2013 23:36:20 -0800 Subject: [PATCH 19/86] Add Initial Thermo i1 & i2 Subclasses Objects are reblessed when aldb version is checked --- lib/Insteon/BaseInsteon.pm | 7 +++++++ lib/Insteon/Thermostat.pm | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index dfdcbe5f7..475891fbd 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -1434,6 +1434,13 @@ sub check_aldb_version if $@ and $main::Debug{insteon}; package Insteon::BaseDevice; } + + if ($self->isa('Insteon::Thermostat')&& $self->_aldb->aldb_version() eq "I2"){ + bless $self, 'Insteon::Thermo_i2'; + } + elsif ($self->isa('Insteon::Thermostat')&& $self->_aldb->aldb_version() eq "I1"){ + bless $self, 'Insteon::Thermo_i1'; + } } diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 24d0be61b..6d8805363 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -108,7 +108,7 @@ my %message_types = ( sub new { my ($class, $p_deviceid, $p_interface) = @_; - +print "[KRK] $class, $p_deviceid, $p_interface"; my $self = new Insteon::BaseDevice($p_deviceid,$p_interface); bless $self, $class; $$self{temp} = undef; @@ -426,6 +426,19 @@ sub request_status { return 0 } sub level { return 0 } + +package Insteon::Thermo_i1; +use strict; + +@Insteon::Thermo_i1::ISA = ('Insteon::Thermostat'); + + +package Insteon::Thermo_i2; +use strict; + +@Insteon::Thermo_i2::ISA = ('Insteon::Thermostat'); + + 1; =back From 54f08ff7c318973e3824a7f3166ac79501bb009a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 11 Mar 2013 22:43:46 -0700 Subject: [PATCH 20/86] Create Thermostat Broadcast Dummy Object The 2441th Insteon thermostat will broadcast changes to all devices registered as a responder to group EF. Created a dummy object under thermostat that is registered as a subdevice on group EF of the thermostat. Sync links properly handles adding the link to the device, but has an error adding it to the PLM. PLM responder link is not necessary, but will solve in future commit. Moved reclass of thermostat to i1 and i2 to Insteon.pm. Will add subchildren to same sub in future. --- lib/Insteon.pm | 25 +++++++++++++++++++++++++ lib/Insteon/BaseInsteon.pm | 7 ------- lib/Insteon/Thermostat.pm | 32 +++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 14ccd1946..c173e9cb3 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -556,6 +556,30 @@ sub check_all_aldb_versions main::print_log("[Insteon] DEBUG4 Checking aldb version of all devices completed") if ($main::Debug{insteon} >= 4); } +sub check_thermo_versions +{ + main::print_log("[Insteon] DEBUG4 Checking thermostat versions") if ($main::Debug{insteon} >= 4); + + my @thermo_devices = (); + push @thermo_devices, Insteon::find_members("Insteon::Thermostat"); + foreach my $thermo_device (@thermo_devices) + { + main::print_log("[Insteon] DEBUG4 Checking thermostat version for " + . $thermo_device->get_object_name()) + if ($main::Debug{insteon} >= 4); + if ($thermo_device->isa('Insteon::Thermostat') && + $thermo_device->_aldb->aldb_version() eq "I2"){ + bless $thermo_device, 'Insteon::Thermo_i2'; + $thermo_device->init(); + } + elsif ($thermo_device->isa('Insteon::Thermostat') + && $thermo_device->_aldb->aldb_version() eq "I1"){ + bless $thermo_device, 'Insteon::Thermo_i1'; + } + } + main::print_log("[Insteon] DEBUG4 Checking thermostat version of all devices completed") if ($main::Debug{insteon} >= 4); +} + package InsteonManager; @@ -582,6 +606,7 @@ sub _active_interface $init_complete = 0; &main::MainLoop_pre_add_hook(\&Insteon::init, 1); &main::Reload_post_add_hook(\&Insteon::generate_voice_commands, 1); + &main::Reload_post_add_hook(\&Insteon::check_thermo_versions, 1); } $$self{active_interface} = $interface if $interface; return $$self{active_interface}; diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index 475891fbd..dfdcbe5f7 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -1434,13 +1434,6 @@ sub check_aldb_version if $@ and $main::Debug{insteon}; package Insteon::BaseDevice; } - - if ($self->isa('Insteon::Thermostat')&& $self->_aldb->aldb_version() eq "I2"){ - bless $self, 'Insteon::Thermo_i2'; - } - elsif ($self->isa('Insteon::Thermostat')&& $self->_aldb->aldb_version() eq "I1"){ - bless $self, 'Insteon::Thermo_i1'; - } } diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 6d8805363..b846c1c9e 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -108,7 +108,6 @@ my %message_types = ( sub new { my ($class, $p_deviceid, $p_interface) = @_; -print "[KRK] $class, $p_deviceid, $p_interface"; my $self = new Insteon::BaseDevice($p_deviceid,$p_interface); bless $self, $class; $$self{temp} = undef; @@ -438,6 +437,37 @@ use strict; @Insteon::Thermo_i2::ISA = ('Insteon::Thermostat'); +sub init { + my ($self) = @_; + my $dev_id = $self->device_id(); + $dev_id =~ /(\w\w)(\w\w)(\w\w)/; + $dev_id = "$1.$2.$3"; + $$self{bcast} = new Insteon::Thermo_bcast("$dev_id".':EF'); + Insteon::add($$self{bcast}); + + ## Child objects + #my $obj_group = ::get_object_by_name('HVAC'); + #$obj_group->add($$self{bcast}); + #&main::register_object_by_name($self->get_object_name ."{bcast}",$$self{bcast}); + #$$self{bcast}->{category} = "sample"; + #$$self{bcast}->{filename} = "sample"; + #$$self{bcast}->{object_name} = $self->get_object_name ."{bcast}"; +} + +package Insteon::Thermo_bcast; +use strict; + +@Insteon::Thermo_bcast::ISA = ('Insteon::Thermostat'); + +###This is basically a dummy object, it is designed to allow a link from group +###EF to be added as part of sync links. + +sub new { + my ($class, $p_deviceid) = @_; + my $self = new Insteon::Thermostat($p_deviceid); + bless $self, $class; + return $self; +} 1; =back From e4d977a65c0a79d49b0767cbb70d07bbcd46ae97 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 12 Mar 2013 21:14:52 -0700 Subject: [PATCH 21/86] Continue to work on adding Broadcast Device --- lib/Insteon/Thermostat.pm | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index b846c1c9e..d2d4f9569 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -90,7 +90,7 @@ package Insteon::Thermostat; use strict; use Insteon::BaseInsteon; -@Insteon::Thermostat::ISA = ('Insteon::DeviceController','Insteon::BaseDevice'); +@Insteon::Thermostat::ISA = ('Insteon::BaseDevice','Insteon::DeviceController'); # -------------------- START OF SUBROUTINES -------------------- @@ -439,32 +439,40 @@ use strict; sub init { my ($self) = @_; + + ## Create the broadcast dummy item my $dev_id = $self->device_id(); $dev_id =~ /(\w\w)(\w\w)(\w\w)/; $dev_id = "$1.$2.$3"; - $$self{bcast} = new Insteon::Thermo_bcast("$dev_id".':EF'); + $$self{bcast} = new Insteon::Thermo_i2_bcast("$dev_id".':EF'); + # Add bcast object to list of Insteon objects Insteon::add($$self{bcast}); + # Register bcast object with MH + &main::register_object_by_name('$' . $self->get_object_name ."{bcast}",$$self{bcast}); + $$self{bcast}->{object_name} = '$' . $self->get_object_name ."{bcast}"; - ## Child objects + ## Create the child objects + my @child_objs = ("mode", "fan", "temp", "humidity", "setpoint_h", "setpoint_c"); #my $obj_group = ::get_object_by_name('HVAC'); #$obj_group->add($$self{bcast}); #&main::register_object_by_name($self->get_object_name ."{bcast}",$$self{bcast}); #$$self{bcast}->{category} = "sample"; #$$self{bcast}->{filename} = "sample"; - #$$self{bcast}->{object_name} = $self->get_object_name ."{bcast}"; + #$$self{bcast}->{object_name} = '$' . $self->get_object_name ."{bcast}"; } -package Insteon::Thermo_bcast; +package Insteon::Thermo_i2_bcast; use strict; -@Insteon::Thermo_bcast::ISA = ('Insteon::Thermostat'); +@Insteon::Thermo_i2_bcast::ISA = ('Insteon::BaseDevice', 'Insteon::DeviceController'); ###This is basically a dummy object, it is designed to allow a link from group -###EF to be added as part of sync links. +###EF to be added as part of sync links. Group EF is the broadcast group used +###by the 2441th thermostat to announce changes. sub new { my ($class, $p_deviceid) = @_; - my $self = new Insteon::Thermostat($p_deviceid); + my $self = new Insteon::BaseDevice($p_deviceid); bless $self, $class; return $self; } From cb07a83bbf2fd9c8a72f9f3dea8f80b8d5798e39 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 17:55:25 -0800 Subject: [PATCH 22/86] Change Insteon Thermostat i2 Broadcast Item Name --- lib/Insteon/Thermostat.pm | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index d2d4f9569..28038b7f8 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -444,12 +444,14 @@ sub init { my $dev_id = $self->device_id(); $dev_id =~ /(\w\w)(\w\w)(\w\w)/; $dev_id = "$1.$2.$3"; - $$self{bcast} = new Insteon::Thermo_i2_bcast("$dev_id".':EF'); + $$self{bcast_item} = new Insteon::Thermo_i2_bcast("$dev_id".':EF'); + # Add bcast object to list of Insteon objects - Insteon::add($$self{bcast}); + Insteon::add($$self{bcast_item}); + # Register bcast object with MH - &main::register_object_by_name('$' . $self->get_object_name ."{bcast}",$$self{bcast}); - $$self{bcast}->{object_name} = '$' . $self->get_object_name ."{bcast}"; + &main::register_object_by_name('$' . $self->get_object_name ."{bcast_item}",$$self{bcast_item}); + $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; ## Create the child objects my @child_objs = ("mode", "fan", "temp", "humidity", "setpoint_h", "setpoint_c"); From 55d578afd327cde0009a3422f5f2e4d073ab5ddb Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:05:25 -0800 Subject: [PATCH 23/86] Create Insteon Thermostat i2 Child Device Objects --- lib/Insteon/Thermostat.pm | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 28038b7f8..482923902 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -479,6 +479,78 @@ sub new { return $self; } +package Insteon::Thermo_i2_mode; +use strict; + +@Insteon::Thermo_i2_mode::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + +package Insteon::Thermo_i2_fan; +use strict; + +@Insteon::Thermo_i2_fan::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + +package Insteon::Thermo_i2_temp; +use strict; + +@Insteon::Thermo_i2_temp::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + +package Insteon::Thermo_i2_humidity; +use strict; + +@Insteon::Thermo_i2_humidity::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + +package Insteon::Thermo_i2_setpoint_h; +use strict; + +@Insteon::Thermo_i2_setpoint_h::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + +package Insteon::Thermo_i2_setpoint_c; +use strict; + +@Insteon::Thermo_i2_setpoint_c::ISA = ('Generic_Item'); + +sub new { + my ($class) = @_; + my $self = new Generic_Item(); + bless $self, $class; + return $self; +} + 1; =back From f052ccc1be69ffcc59dbe21c1f9097edbc501058 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:15:25 -0800 Subject: [PATCH 24/86] Create Insteon Thermostat i2 Child Items on Init --- lib/Insteon/Thermostat.pm | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 482923902..5b63705fe 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -454,13 +454,27 @@ sub init { $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; ## Create the child objects - my @child_objs = ("mode", "fan", "temp", "humidity", "setpoint_h", "setpoint_c"); - #my $obj_group = ::get_object_by_name('HVAC'); - #$obj_group->add($$self{bcast}); - #&main::register_object_by_name($self->get_object_name ."{bcast}",$$self{bcast}); - #$$self{bcast}->{category} = "sample"; - #$$self{bcast}->{filename} = "sample"; - #$$self{bcast}->{object_name} = '$' . $self->get_object_name ."{bcast}"; + my @child_objs = ('mode_item', 'fan_item', 'temp_item', 'humidity_item', + 'setpoint_h_item', 'setpoint_c_item'); + foreach my $obj (@child_objs) { + $$self{$obj} = new Insteon::Thermo_i2_mode() if ($obj eq 'mode_item'); + $$self{$obj} = new Insteon::Thermo_i2_fan() if ($obj eq 'fan_item'); + $$self{$obj} = new Insteon::Thermo_i2_temp() if ($obj eq 'temp_item'); + $$self{$obj} = new Insteon::Thermo_i2_humidity() if ($obj eq 'humidity_item'); + $$self{$obj} = new Insteon::Thermo_i2_setpoint_h() if ($obj eq 'setpoint_h_item'); + $$self{$obj} = new Insteon::Thermo_i2_setpoint_c() if ($obj eq 'setpoint_c_item'); + + # Register child object with MH + &main::register_object_by_name('$' . $self->get_object_name ."{$obj}",$$self{$obj}); + $$self{$obj}->{object_name} = '$' . $self->get_object_name ."{$obj}"; + $$self{$obj}{parent} = $self; + + #Add child to the same groups as parent + foreach my $parent_group (::list_groups_by_object($self)){ + $parent_group->add($$self{$obj}); + } + } +} } package Insteon::Thermo_i2_bcast; From 5873c95a88fde211bd395c4238f47d9cff979db3 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:25:25 -0800 Subject: [PATCH 25/86] Insteon::Thermo_i2 Inject Broadcast Setting Message into Sync_links The device requires that a specific flag be enabled for it to provide broadcast updates. Now, when sync_links is called, this flag will be automatically set. This is done by injecting code into the sync_links sub before calling the main sync_links in BaseDevice --- lib/Insteon/Thermostat.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 5b63705fe..d13944d12 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -475,6 +475,14 @@ sub init { } } } + +sub sync_links{ + my ($self, $audit_mode, $callback, $failure_callback) = @_; + #Make sure thermostat is set to broadcast changes + ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; + ## send command unless $audit_mode; + # Call the main sync_links code + return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } package Insteon::Thermo_i2_bcast; From 60af877ab9036116e1a20fd01d3ff309e9d33779 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:35:25 -0800 Subject: [PATCH 26/86] Insteon::Thermo_i2 Add Message Types --- lib/Insteon/Thermostat.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index d13944d12..952b3de76 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -437,8 +437,14 @@ use strict; @Insteon::Thermo_i2::ISA = ('Insteon::Thermostat'); +my %message_types = ( + %Insteon::Thermostat::message_types, + extended_set_get => 0x2e +); + sub init { my ($self) = @_; + $$self{message_types} = \%message_types ## Create the broadcast dummy item my $dev_id = $self->device_id(); From aea4e57b7b68749d472ad3b918e8bbce5026f1c9 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:45:25 -0800 Subject: [PATCH 27/86] Insteon::Thermo_i2 Set Broadcast Flag with Sync Links --- lib/Insteon/BaseInsteon.pm | 1 + lib/Insteon/Thermostat.pm | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/BaseInsteon.pm b/lib/Insteon/BaseInsteon.pm index dfdcbe5f7..91a58398e 100755 --- a/lib/Insteon/BaseInsteon.pm +++ b/lib/Insteon/BaseInsteon.pm @@ -740,6 +740,7 @@ sub _process_command_stack or $message->command eq 'read_write_aldb' or $message->command eq 'thermostat_control' or $message->command eq 'thermostat_get_zone_info' + or $message->command eq 'extended_set_get' ) { $$self{awaiting_ack} = 1; diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 952b3de76..03e3b55c7 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -444,7 +444,7 @@ my %message_types = ( sub init { my ($self) = @_; - $$self{message_types} = \%message_types + $$self{message_types} = \%message_types; ## Create the broadcast dummy item my $dev_id = $self->device_id(); @@ -486,7 +486,9 @@ sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; #Make sure thermostat is set to broadcast changes ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; - ## send command unless $audit_mode; + my $extra = "000008000000000000000000000000"; + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $self->_send_cmd($message); # Call the main sync_links code return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } From 65ed4bf87ad2ef246e4fd2f9cc118d264a917cd2 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:50:25 -0800 Subject: [PATCH 28/86] Insteon::Thermo_i2 Fix Debug Logging --- lib/Insteon.pm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index c173e9cb3..166d5e61a 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -558,26 +558,29 @@ sub check_all_aldb_versions sub check_thermo_versions { - main::print_log("[Insteon] DEBUG4 Checking thermostat versions") if ($main::Debug{insteon} >= 4); + #main::print_log("[Insteon] DEBUG4 Checking thermostat versions") if ($main::Debug{insteon} >= 4); my @thermo_devices = (); push @thermo_devices, Insteon::find_members("Insteon::Thermostat"); foreach my $thermo_device (@thermo_devices) { - main::print_log("[Insteon] DEBUG4 Checking thermostat version for " - . $thermo_device->get_object_name()) - if ($main::Debug{insteon} >= 4); if ($thermo_device->isa('Insteon::Thermostat') && $thermo_device->_aldb->aldb_version() eq "I2"){ + main::print_log("[Insteon] DEBUG4 Setting thermostat " + . $thermo_device->get_object_name() . " to i2") + if ($main::Debug{insteon} >= 4); bless $thermo_device, 'Insteon::Thermo_i2'; $thermo_device->init(); } elsif ($thermo_device->isa('Insteon::Thermostat') && $thermo_device->_aldb->aldb_version() eq "I1"){ + main::print_log("[Insteon] DEBUG4 Setting thermostat " + . $thermo_device->get_object_name() . " to i2") + if ($main::Debug{insteon} >= 4); bless $thermo_device, 'Insteon::Thermo_i1'; } } - main::print_log("[Insteon] DEBUG4 Checking thermostat version of all devices completed") if ($main::Debug{insteon} >= 4); + #main::print_log("[Insteon] DEBUG4 Checking thermostat version of all devices completed") if ($main::Debug{insteon} >= 4); } From 4f543821f14bf18b597cb9a5e0264b8ba68edcf4 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 18:55:25 -0800 Subject: [PATCH 29/86] Insteon::Thermo_i2 Fix inheritance problem in message_types --- lib/Insteon/Thermostat.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 03e3b55c7..2bcd542b0 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -96,7 +96,7 @@ use Insteon::BaseInsteon; # -------------------- START OF SUBROUTINES -------------------- # -------------------------------------------------------------- -my %message_types = ( +our %message_types = ( %Insteon::BaseDevice::message_types, thermostat_temp_up => 0x68, thermostat_temp_down => 0x69, @@ -437,7 +437,7 @@ use strict; @Insteon::Thermo_i2::ISA = ('Insteon::Thermostat'); -my %message_types = ( +our %message_types = ( %Insteon::Thermostat::message_types, extended_set_get => 0x2e ); From feb2c895c80d495f5521507bca55ddfef0b5982a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 13 Mar 2013 20:21:14 -0700 Subject: [PATCH 30/86] Insteon::Thermo_i2 Child default states and tie parent state to child Add default states to child items. Tie changes in the parent item so that they cause state updates in the children Fix error with get_setpoint when used in auto mode. --- lib/Insteon/Thermostat.pm | 43 +++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 2bcd542b0..a29c376f9 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -369,15 +369,11 @@ sub _is_info_request { $$self{'m_pending_setpoint'} = 1; } elsif ($self->get_mode() eq 'heat' or 'program_heat') { $self->_heat_sp($val); + $$self{_zone_action} = undef; } elsif ($self->get_mode() eq 'cool' or 'program_cool') { $self->_cool_sp($val); + $$self{_zone_action} = undef; } - $$self{_zone_action} = undef; - } elsif ($$self{'m_pending_setpoint'} == 1) { - #This is the second message with the cool_setpoint - $val = (hex $val) / 2; - $self->_cool_sp($val); - $$self{'m_pending_setpoint'} = undef; } } else #This was not a thermostat info_request @@ -396,9 +392,11 @@ sub _process_message my $clear_message = 0; if ($$self{_zone_action} eq 'setpoint' && $$self{m_pending_setpoint}) { # we got our cool setpoint in auto mode + main::print_log("[Insteon::Thermostat] Processing data for $msg{command} with value: $msg{extra}") if $main::Debug{insteon}; my $val = (hex $msg{extra})/2; $self->_cool_sp($val); $$self{m_setpoint_pending} = 0; + $$self{_zone_action} = undef; $clear_message = 1; } else { $clear_message = $self->SUPER::_process_message($p_setby,%msg); @@ -480,6 +478,15 @@ sub init { $parent_group->add($$self{$obj}); } } + #Set child saved states + $$self{temp_item}->set($self->get_temp()); + $$self{setpoint_h_item}->set($self->get_heat_sp()); + $$self{setpoint_c_item}->set($self->get_cool_sp()); + $$self{fan_item}->set($self->get_fan_mode()); + $$self{mode_item}->set($self->get_mode()); + + #Tie changes in parent item to children + $self -> tie_event ('Insteon::Thermo_i2::parent_event(\''.$$self{object_name} . '\', "$state")'); } sub sync_links{ @@ -493,6 +500,26 @@ sub sync_links{ return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } +sub parent_event { + my ($self, $p_state) = @_; + $self = ::get_object_by_name($self); + if ($p_state eq 'temp_change'){ + $$self{temp_item}->set($self->get_temp()); + } + elsif ($p_state eq 'heat_setpoint_change'){ + $$self{setpoint_h_item}->set($self->get_heat_sp()); + } + elsif ($p_state eq 'cool_setpoint_change'){ + $$self{setpoint_c_item}->set($self->get_cool_sp()); + } + elsif ($p_state eq 'fan_mode_change'){ + $$self{fan_item}->set($self->get_fan_mode()); + } + elsif ($p_state eq 'mode_change'){ + $$self{mode_item}->set($self->get_mode()); + } +} + package Insteon::Thermo_i2_bcast; use strict; @@ -518,6 +545,7 @@ sub new { my ($class) = @_; my $self = new Generic_Item(); bless $self, $class; + @{$$self{states}} = ('Off', 'Heat', 'Cool', 'Auto'); return $self; } @@ -530,6 +558,7 @@ sub new { my ($class) = @_; my $self = new Generic_Item(); bless $self, $class; + @{$$self{states}} = ('Auto', 'On'); return $self; } @@ -566,6 +595,7 @@ sub new { my ($class) = @_; my $self = new Generic_Item(); bless $self, $class; + @{$$self{states}} = ('Cooler' , 'Warmer'); return $self; } @@ -578,6 +608,7 @@ sub new { my ($class) = @_; my $self = new Generic_Item(); bless $self, $class; + @{$$self{states}} = ('Cooler', 'Warmer'); return $self; } From 9a1a05b80374f8292ac392718412fa9aa96ae248 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 15:45:25 -0800 Subject: [PATCH 31/86] Insteon::Thermo_i2 Only put child objects into non-recursive groups Child objects should only be put into the groups which the parent is directly a member of, not the parent groups of the member. --- lib/Insteon/Thermostat.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index a29c376f9..bc9559fb3 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -474,7 +474,7 @@ sub init { $$self{$obj}{parent} = $self; #Add child to the same groups as parent - foreach my $parent_group (::list_groups_by_object($self)){ + foreach my $parent_group (::list_groups_by_object($self,1)){ $parent_group->add($$self{$obj}); } } From 59d0113bfef89f6f81136eb39c76fd9da2a1ef28 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 20:56:30 -0700 Subject: [PATCH 32/86] Insteon:Thermo_i2 Add Poll All Request, and Processing One of the 0x2E commands for the i2 devices provides a single response that contains almost all of the data we would need to get started. Not sure how often this message would need to be called, as we will be getting status updates. --- lib/Insteon/Thermostat.pm | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index bc9559fb3..7a82a9e25 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -518,6 +518,108 @@ sub parent_event { elsif ($p_state eq 'mode_change'){ $$self{mode_item}->set($self->get_mode()); } + elsif ($p_state eq 'humid_change'){ + $$self{humidity_item}->set($$self{humid}); + } +} + +sub poll_simple{ + my ($self) = @_; + my $extra = "020000000000000000000000000000"; + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$message{add_crc16} = 1; + $self->_send_cmd($message); +} + +sub _process_message { + my ($self,$p_setby,%msg) = @_; + my $clear_message = 0; + if ($msg{command} eq "extended_set_get" && $msg{is_ack}){ + ##Don't clear until data packet received + main::print_log("[Insteon::Thermo_i2] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; + } + elsif ($msg{command} eq "extended_set_get" && $msg{is_extended}) { + if (substr($msg{extra},0,4) eq "0201") { + main::print_log("[Insteon::Thermo_i2] Extended Set/Get Data ". + "Received for ". $self->get_object_name) if $main::Debug{insteon}; + #0 = 2 #14 = Cool SP + #2 = 1 #16 = humidity + #3 = day #18 = temp in Celsius High byte + #6 = hour #20 = temp low byte + #8 = minute #22 = status flag + #10 = second #24 = Heat SP + #12 = Sys_mode * 16 + Fan_mode + my $mode = hex(substr($msg{extra}, 12, 2)); + my $fan_mode = ($mode % 16); + $self->dec_mode(($mode - $fan_mode) / 16); + $self->dec_fan($fan_mode); + $self->hex_cool(substr($msg{extra}, 14, 2)); + $self->hex_humid(substr($msg{extra}, 16, 2)); + $self->hex_long_temp(substr($msg{extra}, 18, 4)); + $self->hex_status(substr($msg{extra}, 22, 2)); + $self->hex_heat(substr($msg{extra}, 24, 2)); + $clear_message = 1; + $self->_process_command_stack(%msg); + } else { + main::print_log("[Insteon::Thermo_i2] WARN: Corrupt Extended " + ."Set/Get Data Received for ". $self->get_object_name) if $main::Debug{insteon}; + } + } + else { + $clear_message = $self->SUPER::_process_message($p_setby,%msg); + } + return $clear_message; +} + +sub dec_mode{ + my ($self, $dec_mode) = @_; + my $mode; + $mode = 'Off' if ($dec_mode == 0); + $mode = 'Auto' if ($dec_mode == 1); + $mode = 'Heat' if ($dec_mode == 2); + $mode = 'Cool' if ($dec_mode == 3); + $mode = 'Program' if ($dec_mode == 4); + $self->_mode($mode); +} +sub dec_fan{ + my ($self, $dec_fan) = @_; + my $fan; + $fan = 'Auto' if ($dec_fan == 0); + $fan = 'Always On' if ($dec_fan == 1); + $self->_fan_mode($fan); +} + +sub hex_cool{ + my ($self, $hex_cool) = @_; + $self->_cool_sp(hex($hex_cool)); +} +sub hex_humid{ + my ($self, $hex_humid) = @_; + $self->_humid(hex($hex_humid)); +} +sub hex_long_temp{ + my ($self, $hex_temp) = @_; + my $temp_cel = (hex($hex_temp)/10); + ## ATM I am going to assume farenheit b/c that is what I have + # in future, can pull setting bit from thermometer + $$self{temp} = (($temp_cel*9)/5 +32); + $self->set_receive('temp_change'); +} +sub hex_status{ + ### Not sure about this one yet, was 80 when set to auto but no activity +} +sub hex_heat{ + my ($self, $hex_heat) = @_; + $self->_heat_sp(hex($hex_heat)); +} + +sub _humid { + my ($self,$p_state) = @_; + if ($p_state ne $$self{humid}) { + $$self{humid} = $p_state; + $self->set_receive('humid_change'); + } + return $$self{humid}; } package Insteon::Thermo_i2_bcast; From 1c7c0e8fe0dc6735b5b0a9bda24662039ff9eb6a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 15:45:25 -0800 Subject: [PATCH 33/86] Modify list_groups_by_object to allow for non-recursive listing list_groups_by_object identifies all of the groups that an object is a member of. Currently, if an object is a member of a child group, the child and parent group will be returned in the list. This change allows for only the groups which the object is a direct member of to be returned. --- bin/mh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/mh b/bin/mh index 5e23a889a..1f7d5ee3f 100755 --- a/bin/mh +++ b/bin/mh @@ -3186,12 +3186,12 @@ sub list_files_by_webname { } sub list_groups_by_object { - my ($object) = @_; + my ($object, $no_child_members) = @_; my @groups; for my $group_name (&list_objects_by_type('Group')) { my $group = &get_object_by_name($group_name); # print "testing group=$group_name -> $group\n"; - for my $object2 (list $group) { + for my $object2 ($group->list(undef, undef,$no_child_members)) { push @groups, $group if $object eq $object2; } } From ac56b79f95972fb48e460aaf2dca4f32975fe9a8 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 22:36:36 -0700 Subject: [PATCH 34/86] Insteon::Thermo_i2 Save and Restore Humidity State --- lib/Insteon/Thermostat.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 7a82a9e25..64b929a2c 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -478,12 +478,16 @@ sub init { $parent_group->add($$self{$obj}); } } + #Set saved state unique to i2 + $self->restore_data('humid'); + #Set child saved states $$self{temp_item}->set($self->get_temp()); $$self{setpoint_h_item}->set($self->get_heat_sp()); $$self{setpoint_c_item}->set($self->get_cool_sp()); $$self{fan_item}->set($self->get_fan_mode()); $$self{mode_item}->set($self->get_mode()); + $$self{humidity_item}->set($$self{humid}); #Tie changes in parent item to children $self -> tie_event ('Insteon::Thermo_i2::parent_event(\''.$$self{object_name} . '\', "$state")'); From 88d779144a2ff0074d5619a094efc3a41a6bf1bb Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 22:37:41 -0700 Subject: [PATCH 35/86] Insteon::Thermo_i2 Add Status Message Types --- lib/Insteon/Thermostat.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 64b929a2c..9715f0acb 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -437,7 +437,12 @@ use strict; our %message_types = ( %Insteon::Thermostat::message_types, - extended_set_get => 0x2e + extended_set_get => 0x2e, + status_temp => 0x6e, + status_humid => 0x6f, + status_mode => 0x70, + status_cool => 0x71, + status_heat => 0x72 ); sub init { From 85135d832efac34de9277c719a70926f3c135384 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 22:38:28 -0700 Subject: [PATCH 36/86] Insteon::Thermo_i2 Add subs to interpret status mode and temp Insteon is so annoying, why can't it pick a single format for the same data? Anyways, temp in status message is tempx2, instead of long temp form. In addition, for reasons beyond comprehension, the mode in a status message conveys the same data, but uses a different flag than a extended get response. So stupid. --- lib/Insteon/Thermostat.pm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 9715f0acb..e6fb23491 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -590,6 +590,22 @@ sub dec_mode{ $mode = 'Program' if ($dec_mode == 4); $self->_mode($mode); } + +sub status_mode{ + my ($self, $status_mode) = @_; + my $mode; + my $conv_mode = (hex($status_mode)%16); + $mode = 'Off' if ($conv_mode == 0); + $mode = 'Heat' if ($conv_mode == 1); + $mode = 'Cool' if ($conv_mode == 2); + $mode = 'Auto' if ($conv_mode == 3); + $mode = 'Program' if ($conv_mode == 4); + $self->_mode($mode); + my $fan_mode; + $fan_mode = (hex($status_mode) >= 16) ? 'Always On' : 'Auto'; + $self->_fan_mode($fan_mode); +} + sub dec_fan{ my ($self, $dec_fan) = @_; my $fan; @@ -614,6 +630,13 @@ sub hex_long_temp{ $$self{temp} = (($temp_cel*9)/5 +32); $self->set_receive('temp_change'); } + +sub hex_short_temp{ + my ($self, $hex_temp) = @_; + $$self{temp} = (hex($hex_temp)/2); + $self->set_receive('temp_change'); +} + sub hex_status{ ### Not sure about this one yet, was 80 when set to auto but no activity } From fb2e38dd79b12f259ca93d71cbf3cf7acf0eda25 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 14 Mar 2013 22:40:40 -0700 Subject: [PATCH 37/86] Insteon::Thermo_i2 add steps in _process_message to catch and process status messages --- lib/Insteon/Thermostat.pm | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index e6fb23491..1c1b89ba1 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -574,6 +574,31 @@ sub _process_message { ."Set/Get Data Received for ". $self->get_object_name) if $main::Debug{insteon}; } } + elsif ($msg{command} eq "status_temp" && !$msg{is_ack}){ + main::print_log("[Insteon::Thermo_i2] Received Status Temp Message ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->hex_short_temp($msg{extra}); + } + elsif ($msg{command} eq "status_mode" && !$msg{is_ack}){ + main::print_log("[Insteon::Thermo_i2] Received Status Mode Message ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->status_mode($msg{extra}); + } + elsif ($msg{command} eq "status_cool" && !$msg{is_ack}){ + main::print_log("[Insteon::Thermo_i2] Received Status Cool Message ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->hex_cool($msg{extra}); + } + elsif ($msg{command} eq "status_humid" && !$msg{is_ack}){ + main::print_log("[Insteon::Thermo_i2] Received Status Humid Message ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->hex_humid($msg{extra}); + } + elsif ($msg{command} eq "status_heat" && !$msg{is_ack}){ + main::print_log("[Insteon::Thermo_i2] Received Status Heat Message ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->hex_heat($msg{extra}); + } else { $clear_message = $self->SUPER::_process_message($p_setby,%msg); } From 5f47ec1b1256b534d26019a9a75aa6b3f8160d28 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 18 Mar 2013 21:59:07 -0700 Subject: [PATCH 38/86] Insteon:Thermostat Distinguish Between Extended Set/Get Messages that Need to be Cleared --- lib/Insteon/Thermostat.pm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 1c1b89ba1..77c7c6ee6 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -504,6 +504,7 @@ sub sync_links{ ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; my $extra = "000008000000000000000000000000"; my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$self{_ext_set_get_action} = 'set'; $self->_send_cmd($message); # Call the main sync_links code return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); @@ -544,8 +545,14 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; if ($msg{command} eq "extended_set_get" && $msg{is_ack}){ - ##Don't clear until data packet received + #If this was a get request don't clear until data packet received main::print_log("[Insteon::Thermo_i2] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; + if ($$self{_ext_set_get_action} eq 'set'){ + main::print_log("[Insteon::Thermo_i2] Clearing active message") if $main::Debug{insteon}; + $clear_message = 1; + $$self{_ext_set_get_action} = undef; + $self->_process_command_stack(%msg); + } } elsif ($msg{command} eq "extended_set_get" && $msg{is_extended}) { if (substr($msg{extra},0,4) eq "0201") { From 2908e6cd251836ab90d96b00c25c1677714bdc9f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 18 Mar 2013 22:00:12 -0700 Subject: [PATCH 39/86] Insteon:Thermo_i2 Add Set Routine to Children --- lib/Insteon/Thermostat.pm | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 77c7c6ee6..6cc9aff43 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -715,6 +715,21 @@ sub new { return $self; } +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2] Received set mode request to " + . $p_state . " for device " . $self->get_object_name); + } +} + + package Insteon::Thermo_i2_fan; use strict; @@ -728,6 +743,20 @@ sub new { return $self; } +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2] Received set mode request to " + . $p_state . " for device " . $self->get_object_name); + } +} + package Insteon::Thermo_i2_temp; use strict; @@ -765,6 +794,20 @@ sub new { return $self; } +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2] Received set mode request to " + . $p_state . " for device " . $self->get_object_name); + } +} + package Insteon::Thermo_i2_setpoint_c; use strict; @@ -778,6 +821,20 @@ sub new { return $self; } +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2] Received set mode request to " + . $p_state . " for device " . $self->get_object_name); + } +} + 1; =back From 3f94bc043940799a4ff724262229959ac0419c91 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:28:54 -0700 Subject: [PATCH 40/86] Insteon::Thermostat Split Mode Routine into i1 and i2 Specific Routines For whatever reason the commands are different between the two --- lib/Insteon/Thermostat.pm | 98 +++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 6cc9aff43..b6f8929ad 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -133,39 +133,6 @@ sub poll_mode { return; } -=item C - -Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', -'program_cool', 'program_auto'. The 2441TH thermostat does not have program_heat - or program_cool. -=cut -sub mode{ - my ($self, $state) = @_; - $state = lc($state); - main::print_log("[Insteon::Thermostat] Mode $state") if $main::Debug{insteon}; - my $mode; - if ($state eq 'off') { - $mode = "09"; - } elsif ($state eq 'heat') { - $mode = "04"; - } elsif ($state eq 'cool') { - $mode = "05"; - } elsif ($state eq 'auto') { - $mode = "06"; - } elsif ($state eq 'program_heat') { - $mode = "0a"; - } elsif ($state eq 'program_cool') { - $mode = "0b"; - } elsif ($state eq 'program_auto') { - $mode = "0c"; - $mode = "0a" if $self->_aldb->isa('Insteon::ALDB_i2'); - } else { - main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); - return(); - } - $self->_send_cmd($self->simple_message('thermostat_control', $mode)); -} - =item C Sets fan to 'on' or 'auto' @@ -429,6 +396,39 @@ use strict; @Insteon::Thermo_i1::ISA = ('Insteon::Thermostat'); +=item C + +Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', +'program_cool', 'program_auto'. The 2441TH thermostat does not have program_heat + or program_cool. +=cut +sub mode{ + my ($self, $state) = @_; + $state = lc($state); + main::print_log("[Insteon::Thermostat] Mode $state") if $main::Debug{insteon}; + my $mode; + if ($state eq 'off') { + $mode = "09"; + } elsif ($state eq 'heat') { + $mode = "04"; + } elsif ($state eq 'cool') { + $mode = "05"; + } elsif ($state eq 'auto') { + $mode = "06"; + } elsif ($state eq 'program_heat') { + $mode = "0a"; + } elsif ($state eq 'program_cool') { + $mode = "0b"; + } elsif ($state eq 'program_auto') { + $mode = "0c"; + } else { + main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); + return(); + } + $$self{_control_action} = "mode"; + $self->_send_cmd($self->simple_message('thermostat_control', $mode)); +} + package Insteon::Thermo_i2; use strict; @@ -686,6 +686,36 @@ sub _humid { return $$self{humid}; } +=item C + +Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', +'program_cool', 'program_auto'. The 2441TH thermostat does not have program_heat + or program_cool. +=cut +sub mode{ + my ($self, $state) = @_; + $state = lc($state); + main::print_log("[Insteon::Thermostat] Mode $state") if $main::Debug{insteon}; + my $mode; + if ($state eq 'off') { + $mode = "09"; + } elsif ($state eq 'heat') { + $mode = "04"; + } elsif ($state eq 'cool') { + $mode = "05"; + } elsif ($state eq 'auto') { + $mode = "06"; + } elsif ($state eq 'program') { + $mode = "0a" if $self->_aldb->isa('Insteon::ALDB_i2'); + } else { + main::print_log("[Insteon::Thermostat] ERROR: Invalid Mode state: $state"); + return(); + } + $$self{_control_action} = "mode"; + $self->_send_cmd($self->simple_message('thermostat_control', $mode)); +} + + package Insteon::Thermo_i2_bcast; use strict; @@ -711,7 +741,7 @@ sub new { my ($class) = @_; my $self = new Generic_Item(); bless $self, $class; - @{$$self{states}} = ('Off', 'Heat', 'Cool', 'Auto'); + @{$$self{states}} = ('Off', 'Heat', 'Cool', 'Auto', 'Program'); return $self; } From 7b7e3883102732575f647118b0f6a9e6ff045312 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:31:43 -0700 Subject: [PATCH 41/86] Insteon::Thermostat Split _is_info_request for Mode Change into i1 and i2 The response from the devices use different bytes to signify their modes --- lib/Insteon/Thermostat.pm | 89 ++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index b6f8929ad..f8c7349e8 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -296,38 +296,16 @@ sub get_fan_mode() { sub _is_info_request { my ($self, $cmd, $ack_setby, %msg) = @_; - my $is_info_request = ($cmd eq 'thermostat_get_zone_info' - or $cmd eq 'thermostat_control') ? 1 : 0; + my $is_info_request = ($cmd eq 'thermostat_get_zone_info') ? 1 : 0; if ($is_info_request) { my $val = $msg{extra}; - main::print_log("[Insteon::Thermostat] Processing data for $cmd with value: $val") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermostat] Processing is_info_request for $cmd with value: $val") if $main::Debug{insteon}; if ($$self{_zone_action} eq "temp") { $val = (hex $val) / 2; # returned value is twice the real value if (exists $$self{'temp'} and ($$self{'temp'} != $val)) { $self->set_receive('temp_change'); } $$self{'temp'} = $val; - } elsif ($$self{_control_action} eq "mode") { - if ($val eq '00') { - $self->_mode('off'); - } elsif ($val eq '01') { - $self->_mode('heat'); - } elsif ($val eq '02') { - $self->_mode('cool'); - } elsif ($val eq '03') { - $self->_mode('auto'); - } elsif ($val eq '04') { - $self->_fan_mode('fan_on'); - } elsif ($val eq '05') { - $self->_mode('program_auto'); - } elsif ($val eq '06') { - $self->_mode('program_heat'); - } elsif ($val eq '07') { - $self->_mode('program_cool'); - } elsif ($val eq '08') { - $self->_fan_mode('fan_auto'); - } - $$self{_control_action} = undef; } elsif ($$self{_zone_action} eq 'setpoint') { $val = (hex $val) / 2; # returned value is twice the real value # in auto modes, expect direct message with cool_setpoint to follow @@ -429,6 +407,41 @@ sub mode{ $self->_send_cmd($self->simple_message('thermostat_control', $mode)); } +sub _is_info_request { + my ($self, $cmd, $ack_setby, %msg) = @_; + my $is_info_request; + if ($cmd eq 'thermostat_control' && $$self{_control_action} eq "mode") { + my $val = $msg{extra}; + main::print_log("[Insteon::Thermo_i1] Processing is_info_request for $cmd with value: $val") if $main::Debug{insteon}; + if ($val eq '00') { + $self->_mode('off'); + } elsif ($val eq '01') { + $self->_mode('heat'); + } elsif ($val eq '02') { + $self->_mode('cool'); + } elsif ($val eq '03') { + $self->_mode('auto'); + } elsif ($val eq '04') { + $self->_fan_mode('fan_on'); + } elsif ($val eq '05') { + $self->_mode('program_auto'); + } elsif ($val eq '06') { + $self->_mode('program_heat'); + } elsif ($val eq '07') { + $self->_mode('program_cool'); + } elsif ($val eq '08') { + $self->_fan_mode('fan_auto'); + } + $$self{_control_action} = undef; + $is_info_request = 1; + } + else #This was not a thermo_1 info_request + { + #Check if this was a generic info_request + $is_info_request = $self->SUPER::_is_info_request($cmd, $ack_setby, %msg); + } + return $is_info_request; +} package Insteon::Thermo_i2; use strict; @@ -612,6 +625,34 @@ sub _process_message { return $clear_message; } +sub _is_info_request { + my ($self, $cmd, $ack_setby, %msg) = @_; + my $is_info_request; + if ($cmd eq 'thermostat_control' && $$self{_control_action} eq "mode") { + my $val = $msg{extra}; + main::print_log("[Insteon::Thermo_i2] Processing is_info_request for $cmd with value: $val") if $main::Debug{insteon}; + if ($val eq '09') { + $self->_mode('Off'); + } elsif ($val eq '04') { + $self->_mode('Heat'); + } elsif ($val eq '05') { + $self->_mode('Cool'); + } elsif ($val eq '06') { + $self->_mode('Auto'); + } elsif ($val eq '0a') { + $self->_mode('Program'); + } + $$self{_control_action} = undef; + $is_info_request = 1; + } + else #This was not a thermo_i2 info_request + { + #Check if this was a generic info_request + $is_info_request = $self->SUPER::_is_info_request($cmd, $ack_setby, %msg); + } + return $is_info_request; +} + sub dec_mode{ my ($self, $dec_mode) = @_; my $mode; From 1ea9faa70384d000d50c24f715e59b84757f6ed5 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:34:20 -0700 Subject: [PATCH 42/86] Insteon::Thermo_i2 Create set_receive Routines in Child Objects Need to distinguish between a command that we send to set the device and commands received in which the device notifies MH of a new state --- lib/Insteon/Thermostat.pm | 50 +++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index f8c7349e8..dd82aad48 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -500,12 +500,12 @@ sub init { $self->restore_data('humid'); #Set child saved states - $$self{temp_item}->set($self->get_temp()); - $$self{setpoint_h_item}->set($self->get_heat_sp()); - $$self{setpoint_c_item}->set($self->get_cool_sp()); - $$self{fan_item}->set($self->get_fan_mode()); - $$self{mode_item}->set($self->get_mode()); - $$self{humidity_item}->set($$self{humid}); + $$self{temp_item}->set_receive($self->get_temp()); + $$self{setpoint_h_item}->set_receive($self->get_heat_sp()); + $$self{setpoint_c_item}->set_receive($self->get_cool_sp()); + $$self{fan_item}->set_receive($self->get_fan_mode()); + $$self{mode_item}->set_receive($self->get_mode()); + $$self{humidity_item}->set_receive($$self{humid}); #Tie changes in parent item to children $self -> tie_event ('Insteon::Thermo_i2::parent_event(\''.$$self{object_name} . '\', "$state")'); @@ -527,22 +527,22 @@ sub parent_event { my ($self, $p_state) = @_; $self = ::get_object_by_name($self); if ($p_state eq 'temp_change'){ - $$self{temp_item}->set($self->get_temp()); + $$self{temp_item}->set_receive($self->get_temp()); } elsif ($p_state eq 'heat_setpoint_change'){ - $$self{setpoint_h_item}->set($self->get_heat_sp()); + $$self{setpoint_h_item}->set_receive($self->get_heat_sp()); } elsif ($p_state eq 'cool_setpoint_change'){ - $$self{setpoint_c_item}->set($self->get_cool_sp()); + $$self{setpoint_c_item}->set_receive($self->get_cool_sp()); } elsif ($p_state eq 'fan_mode_change'){ - $$self{fan_item}->set($self->get_fan_mode()); + $$self{fan_item}->set_receive($self->get_fan_mode()); } elsif ($p_state eq 'mode_change'){ - $$self{mode_item}->set($self->get_mode()); + $$self{mode_item}->set_receive($self->get_mode()); } elsif ($p_state eq 'humid_change'){ - $$self{humidity_item}->set($$self{humid}); + $$self{humidity_item}->set_receive($$self{humid}); } } @@ -800,6 +800,10 @@ sub set { } } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} package Insteon::Thermo_i2_fan; use strict; @@ -828,6 +832,10 @@ sub set { } } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} package Insteon::Thermo_i2_temp; use strict; @@ -840,6 +848,10 @@ sub new { return $self; } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} package Insteon::Thermo_i2_humidity; use strict; @@ -852,6 +864,11 @@ sub new { return $self; } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} + package Insteon::Thermo_i2_setpoint_h; use strict; @@ -879,6 +896,11 @@ sub set { } } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} + package Insteon::Thermo_i2_setpoint_c; use strict; @@ -906,6 +928,10 @@ sub set { } } +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} 1; =back From f861c331a58412741d5becb0313d48b3df977af0 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:36:42 -0700 Subject: [PATCH 43/86] Insteon::Thermo_i2 Allow Changes to Child Objects to be Propogated to Parent Item --- lib/Insteon/Thermostat.pm | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index dd82aad48..bcda5c40a 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -797,6 +797,7 @@ sub set { if ($found_state){ ::print_log("[Insteon::Thermo_i2] Received set mode request to " . $p_state . " for device " . $self->get_object_name); + $$self{parent}->mode($p_state); } } @@ -827,8 +828,9 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received set mode request to " + ::print_log("[Insteon::Thermo_i2] Received set fan to " . $p_state . " for device " . $self->get_object_name); + $$self{parent}->fan($p_state); } } @@ -891,8 +893,14 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received set mode request to " + ::print_log("[Insteon::Thermo_i2] Received request to set heat setpoint " . $p_state . " for device " . $self->get_object_name); + if (lc($p_state) eq 'cooler'){ + $$self{parent}->heat_setpoint($$self{parent}->get_heat_sp - 1); + } + elsif (lc($p_state) eq 'warmer'){ + $$self{parent}->heat_setpoint($$self{parent}->get_heat_sp + 1); + } } } @@ -923,8 +931,14 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received set mode request to " + ::print_log("[Insteon::Thermo_i2] Received request to set cool setpoint " . $p_state . " for device " . $self->get_object_name); + if (lc($p_state) eq 'cooler'){ + $$self{parent}->cool_setpoint($$self{parent}->get_cool_sp - 1); + } + elsif (lc($p_state) eq 'warmer'){ + $$self{parent}->cool_setpoint($$self{parent}->get_cool_sp + 1); + } } } From c44c80e42205e4df32853bfaedabbb3c4a03605b Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:37:46 -0700 Subject: [PATCH 44/86] Insteon::Thermo_i2 Catch ACK of Setpoint Changes and Update State --- lib/Insteon/Thermostat.pm | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index bcda5c40a..bc09a61dc 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -335,7 +335,19 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; - if ($$self{_zone_action} eq 'setpoint' && $$self{m_pending_setpoint}) { + if ($msg{command} eq "thermostat_setpoint_cool" && $msg{is_ack}){ + main::print_log("[Insteon::Thermostat] Received ACK of cool setpoint ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->_cool_sp((hex($msg{extra})/2)); + $clear_message = 1; + } + elsif ($msg{command} eq "thermostat_setpoint_heat" && $msg{is_ack}){ + main::print_log("[Insteon::Thermostat] Received ACK of heat setpoint ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->_heat_sp((hex($msg{extra})/2)); + $clear_message = 1; + } + elsif ($$self{_zone_action} eq 'setpoint' && $$self{m_pending_setpoint}) { # we got our cool setpoint in auto mode main::print_log("[Insteon::Thermostat] Processing data for $msg{command} with value: $msg{extra}") if $main::Debug{insteon}; my $val = (hex $msg{extra})/2; From f8c55ec51dc80c1fbecc80e5d48de46b53797dd2 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 21:46:17 -0700 Subject: [PATCH 45/86] Insteon::Thermostat Split simple_message into i1 and i2 --- lib/Insteon/Thermostat.pm | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index bc09a61dc..9cce2f867 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -361,20 +361,6 @@ sub _process_message return $clear_message; } -## Creates either a Standard or Extended Message depending on the device type -## Can be used to create different classes later -sub simple_message { - my ($self,$type,$extra) = @_; - my $message; - if ($self->_aldb->isa('Insteon::ALDB_i2')){ - $extra = $extra . "0000000000000000000000000000"; - $message = new Insteon::InsteonMessage('insteon_ext_send', $self, $type, $extra); - } else { - $message = new Insteon::InsteonMessage('insteon_send', $self, $type, $extra); - } - return $message; -} - # Overload methods we don't use, but would otherwise cause Insteon traffic. sub request_status { return 0 } @@ -455,6 +441,14 @@ sub _is_info_request { return $is_info_request; } +## Creates a simple Standard Message +sub simple_message { + my ($self,$type,$extra) = @_; + my $message; + $message = new Insteon::InsteonMessage('insteon_send', $self, $type, $extra); + return $message; +} + package Insteon::Thermo_i2; use strict; @@ -768,6 +762,14 @@ sub mode{ $self->_send_cmd($self->simple_message('thermostat_control', $mode)); } +## Creates an Extended Message +sub simple_message { + my ($self,$type,$extra) = @_; + my $message; + $extra = $extra . "0000000000000000000000000000"; + $message = new Insteon::InsteonMessage('insteon_ext_send', $self, $type, $extra); + return $message; +} package Insteon::Thermo_i2_bcast; use strict; From 779bbb34ce0f491ec78e0943358ec121d16500f6 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 19 Mar 2013 22:33:10 -0700 Subject: [PATCH 46/86] Insteon::Thermo_i2 Added sync_time Function Syncs the thermostat time to your computer time, as simple as that --- lib/Insteon/Thermostat.pm | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 9cce2f867..224f660ab 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -595,6 +595,26 @@ sub _process_message { $self->hex_heat(substr($msg{extra}, 24, 2)); $clear_message = 1; $self->_process_command_stack(%msg); + if ($$self{set_time}){ + #This poll was requested as part of sync_time + my $message; + my $extra; + my @time_array = localtime(time); + my @req_items = ($time_array[6], $time_array[2], + $time_array[1], $time_array[0]); + my $time_str = ''; + foreach (@req_items){ + $time_str .= sprintf("%02d", $_); + } + $extra = $extra . "0202". $time_str . substr($msg{extra}, 12, 18); + #This will include the prior CRC16 message, but it will + #get overwritten with the correct value in Message.pm + $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$message{add_crc16} = 1; + $$self{_ext_set_get_action} = 'set'; + $$self{set_time} = undef; + $self->_send_cmd($message); + } } else { main::print_log("[Insteon::Thermo_i2] WARN: Corrupt Extended " ."Set/Get Data Received for ". $self->get_object_name) if $main::Debug{insteon}; @@ -771,6 +791,15 @@ sub simple_message { return $message; } +sub sync_time { + my ($self) = @_; + #In order to set the time, we need to know the current value of other data + #points such as mode and what not becuase we can't just set the time without + #setting these variables too. + $$self{set_time} = 1; + $self->poll_simple(); +} + package Insteon::Thermo_i2_bcast; use strict; From 304b7026445f300e759090f688a5b381c21a903e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 20 Mar 2013 22:12:16 -0700 Subject: [PATCH 47/86] Insteon::Thermostat Add Hop Tracking --- lib/Insteon/Thermostat.pm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 224f660ab..1de662ee3 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -336,18 +336,21 @@ sub _process_message my ($self,$p_setby,%msg) = @_; my $clear_message = 0; if ($msg{command} eq "thermostat_setpoint_cool" && $msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermostat] Received ACK of cool setpoint ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->_cool_sp((hex($msg{extra})/2)); $clear_message = 1; } elsif ($msg{command} eq "thermostat_setpoint_heat" && $msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermostat] Received ACK of heat setpoint ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->_heat_sp((hex($msg{extra})/2)); $clear_message = 1; } elsif ($$self{_zone_action} eq 'setpoint' && $$self{m_pending_setpoint}) { + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); # we got our cool setpoint in auto mode main::print_log("[Insteon::Thermostat] Processing data for $msg{command} with value: $msg{extra}") if $main::Debug{insteon}; my $val = (hex $msg{extra})/2; @@ -564,6 +567,7 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; if ($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::Thermo_i2] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; if ($$self{_ext_set_get_action} eq 'set'){ @@ -575,6 +579,7 @@ sub _process_message { } elsif ($msg{command} eq "extended_set_get" && $msg{is_extended}) { if (substr($msg{extra},0,4) eq "0201") { + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Extended Set/Get Data ". "Received for ". $self->get_object_name) if $main::Debug{insteon}; #0 = 2 #14 = Cool SP @@ -621,26 +626,31 @@ sub _process_message { } } elsif ($msg{command} eq "status_temp" && !$msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Received Status Temp Message ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_short_temp($msg{extra}); } elsif ($msg{command} eq "status_mode" && !$msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Received Status Mode Message ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->status_mode($msg{extra}); } elsif ($msg{command} eq "status_cool" && !$msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Received Status Cool Message ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_cool($msg{extra}); } elsif ($msg{command} eq "status_humid" && !$msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Received Status Humid Message ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_humid($msg{extra}); } elsif ($msg{command} eq "status_heat" && !$msg{is_ack}){ + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermo_i2] Received Status Heat Message ". "for ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_heat($msg{extra}); From cd2310897b74720753705656759720f747391cf4 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 21 Mar 2013 20:02:39 -0700 Subject: [PATCH 48/86] Insteon::Thermo_i2 Change to sync_time flag and convert time to hex Set_time is a flay already used in MH, likely for idle time. Changed flag to sync_time instead. Also time values needed to be hex encoded. --- lib/Insteon/Thermostat.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 1de662ee3..eeb0e5ff0 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -600,7 +600,7 @@ sub _process_message { $self->hex_heat(substr($msg{extra}, 24, 2)); $clear_message = 1; $self->_process_command_stack(%msg); - if ($$self{set_time}){ + if ($$self{sync_time}){ #This poll was requested as part of sync_time my $message; my $extra; @@ -609,7 +609,7 @@ sub _process_message { $time_array[1], $time_array[0]); my $time_str = ''; foreach (@req_items){ - $time_str .= sprintf("%02d", $_); + $time_str .= sprintf("%02x", $_); } $extra = $extra . "0202". $time_str . substr($msg{extra}, 12, 18); #This will include the prior CRC16 message, but it will @@ -617,7 +617,7 @@ sub _process_message { $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); $$message{add_crc16} = 1; $$self{_ext_set_get_action} = 'set'; - $$self{set_time} = undef; + $$self{sync_time} = undef; $self->_send_cmd($message); } } else { @@ -806,7 +806,7 @@ sub sync_time { #In order to set the time, we need to know the current value of other data #points such as mode and what not becuase we can't just set the time without #setting these variables too. - $$self{set_time} = 1; + $$self{sync_time} = 1; $self->poll_simple(); } From 1b55b88c7f2ddc2c80c35ddeee3c5d199dd101e1 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 26 Mar 2013 18:15:20 -0700 Subject: [PATCH 49/86] Insteon::Thermo_i2 Reorganize i2 init routine and clean up spacing in i2 package --- lib/Insteon/Thermostat.pm | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index eeb0e5ff0..70bdcead6 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -470,8 +470,14 @@ our %message_types = ( sub init { my ($self) = @_; $$self{message_types} = \%message_types; + #Set saved state unique to i2 devices + $self->restore_data('humid'); - ## Create the broadcast dummy item + # Create the broadcast dummy item + # This may not belong here. Maybe this should go into read table A? + # Otherwise, users cannot define insteon scenes containing this device. + # While rare a thermostat may be set to provide broadcast updates to + # another device my $dev_id = $self->device_id(); $dev_id =~ /(\w\w)(\w\w)(\w\w)/; $dev_id = "$1.$2.$3"; @@ -505,8 +511,6 @@ sub init { $parent_group->add($$self{$obj}); } } - #Set saved state unique to i2 - $self->restore_data('humid'); #Set child saved states $$self{temp_item}->set_receive($self->get_temp()); @@ -727,10 +731,12 @@ sub hex_cool{ my ($self, $hex_cool) = @_; $self->_cool_sp(hex($hex_cool)); } + sub hex_humid{ my ($self, $hex_humid) = @_; $self->_humid(hex($hex_humid)); } + sub hex_long_temp{ my ($self, $hex_temp) = @_; my $temp_cel = (hex($hex_temp)/10); @@ -749,6 +755,7 @@ sub hex_short_temp{ sub hex_status{ ### Not sure about this one yet, was 80 when set to auto but no activity } + sub hex_heat{ my ($self, $hex_heat) = @_; $self->_heat_sp(hex($hex_heat)); @@ -769,6 +776,7 @@ Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', 'program_cool', 'program_auto'. The 2441TH thermostat does not have program_heat or program_cool. =cut + sub mode{ my ($self, $state) = @_; $state = lc($state); From 6c555d09a259d0173a9fd3ebefc61b6068fbe77e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 26 Mar 2013 18:16:43 -0700 Subject: [PATCH 50/86] Insteon::Thermo_i2 Move Child Objects to First-Level MH Objects Use eval to create true Top-Level objects with user friendly variable names. This may be frowned upon in perl programming, but is is already used in a number of places within MH, so it can't be that bad. :-p As noted in the comments, objects which will be linked to insteon scenes should not be created in this manner. A big thanks to Gregg in 2007 who posted a comment that led me to this solution. http://misterhouse.10964.n7.nabble.com/Coding-question-Dynamic-variables-with-strict-refs-td6300.html --- lib/Insteon/Thermostat.pm | 59 +++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 70bdcead6..2070bb510 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -490,37 +490,48 @@ sub init { &main::register_object_by_name('$' . $self->get_object_name ."{bcast_item}",$$self{bcast_item}); $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; - ## Create the child objects - my @child_objs = ('mode_item', 'fan_item', 'temp_item', 'humidity_item', - 'setpoint_h_item', 'setpoint_c_item'); - foreach my $obj (@child_objs) { - $$self{$obj} = new Insteon::Thermo_i2_mode() if ($obj eq 'mode_item'); - $$self{$obj} = new Insteon::Thermo_i2_fan() if ($obj eq 'fan_item'); - $$self{$obj} = new Insteon::Thermo_i2_temp() if ($obj eq 'temp_item'); - $$self{$obj} = new Insteon::Thermo_i2_humidity() if ($obj eq 'humidity_item'); - $$self{$obj} = new Insteon::Thermo_i2_setpoint_h() if ($obj eq 'setpoint_h_item'); - $$self{$obj} = new Insteon::Thermo_i2_setpoint_c() if ($obj eq 'setpoint_c_item'); - + # Define the child objects + # These we can create ourselves as they cannot be used in an insteon scene + my %child_objs = ( + mode_item => "new Insteon::Thermo_i2_mode(".$self->get_object_name.")", + fan_item => "new Insteon::Thermo_i2_fan(".$self->get_object_name.")", + temp_item => "new Insteon::Thermo_i2_temp(".$self->get_object_name.")", + humidity_item => "new Insteon::Thermo_i2_humidity(".$self->get_object_name.")", + setpoint_h_item => "new Insteon::Thermo_i2_setpoint_h(".$self->get_object_name.")", + setpoint_c_item => "new Insteon::Thermo_i2_setpoint_c(".$self->get_object_name.")" + ); + + #Now create them + foreach my $child_type (keys %child_objs) { + #Name the Child Object + my $child_name = $self->get_object_name . "_" . $child_type; + + #Define the code to run in eval + my $eval_cmd; + $eval_cmd = "use vars '$child_name';\n"; + $eval_cmd .= "$child_name = $child_objs{$child_type};\n"; # Register child object with MH - &main::register_object_by_name('$' . $self->get_object_name ."{$obj}",$$self{$obj}); - $$self{$obj}->{object_name} = '$' . $self->get_object_name ."{$obj}"; - $$self{$obj}{parent} = $self; + $eval_cmd .= "&main::register_object_by_name('$child_name',$child_name);\n"; + + #Run the eval command + package main; + eval($eval_cmd); + if ($@) { + ::print_log( "[Insteon::Thermo_i2] Error in init eval command:\n $@ \n---\n $eval_cmd"); + } + package Insteon::Thermo_i2; + + #Get the newly created child object + $$self{$child_type} = ::get_object_by_name($child_name); + $$self{$child_type}->{object_name} = "$child_name"; #Add child to the same groups as parent foreach my $parent_group (::list_groups_by_object($self,1)){ - $parent_group->add($$self{$obj}); + $parent_group->add($$self{$child_type}); } } - #Set child saved states - $$self{temp_item}->set_receive($self->get_temp()); - $$self{setpoint_h_item}->set_receive($self->get_heat_sp()); - $$self{setpoint_c_item}->set_receive($self->get_cool_sp()); - $$self{fan_item}->set_receive($self->get_fan_mode()); - $$self{mode_item}->set_receive($self->get_mode()); - $$self{humidity_item}->set_receive($$self{humid}); - - #Tie changes in parent item to children + #Create tie so that changes in parent update the child $self -> tie_event ('Insteon::Thermo_i2::parent_event(\''.$$self{object_name} . '\', "$state")'); } From 43ddcdfea5ad2478eeffec67d050be943f88b172 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 26 Mar 2013 18:21:56 -0700 Subject: [PATCH 51/86] Insteon::Thermo_i2 Define Parent and set Initial Value in Child Object new Routines --- lib/Insteon/Thermostat.pm | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 2070bb510..443fdfed5 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -851,10 +851,12 @@ use strict; @Insteon::Thermo_i2_mode::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class,$parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; @{$$self{states}} = ('Off', 'Heat', 'Cool', 'Auto', 'Program'); + $self->set_receive($$self{parent}->get_mode()); return $self; } @@ -884,10 +886,12 @@ use strict; @Insteon::Thermo_i2_fan::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class, $parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; @{$$self{states}} = ('Auto', 'On'); + $self->set_receive($$self{parent}->get_fan_mode()); return $self; } @@ -910,15 +914,18 @@ sub set_receive { my ($self, $p_state) = @_; $self->SUPER::set($p_state); } + package Insteon::Thermo_i2_temp; use strict; @Insteon::Thermo_i2_temp::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class, $parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; + $self->set_receive($$self{parent}->get_temp()); return $self; } @@ -932,9 +939,11 @@ use strict; @Insteon::Thermo_i2_humidity::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class, $parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; + $self->set_receive($$self{parent}{humid}); return $self; } @@ -949,10 +958,12 @@ use strict; @Insteon::Thermo_i2_setpoint_h::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class, $parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; @{$$self{states}} = ('Cooler' , 'Warmer'); + $self->set_receive($$self{parent}->get_heat_sp()); return $self; } @@ -987,10 +998,12 @@ use strict; @Insteon::Thermo_i2_setpoint_c::ISA = ('Generic_Item'); sub new { - my ($class) = @_; + my ($class, $parent) = @_; my $self = new Generic_Item(); bless $self, $class; + $$self{parent} = $parent; @{$$self{states}} = ('Cooler', 'Warmer'); + $self->set_receive($$self{parent}->get_cool_sp()); return $self; } From dd9501ee4957202913cc77db45c8591597bee112 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 17:18:26 -0700 Subject: [PATCH 52/86] Insteon_Thermo_i2: Remove Complex Code Creating Child Objects The code is just too difficult to try and make work. Either the child objects have to be stored in a hash, which results in awful looking veriable names that are not user friendly to the basic user. The removed code made user friendly variable names, but these variables could not be used in other user code because the init code was not eval'd until runtime. --- lib/Insteon/Thermostat.pm | 44 --------------------------------------- 1 file changed, 44 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 443fdfed5..d5f1b5992 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -489,50 +489,6 @@ sub init { # Register bcast object with MH &main::register_object_by_name('$' . $self->get_object_name ."{bcast_item}",$$self{bcast_item}); $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; - - # Define the child objects - # These we can create ourselves as they cannot be used in an insteon scene - my %child_objs = ( - mode_item => "new Insteon::Thermo_i2_mode(".$self->get_object_name.")", - fan_item => "new Insteon::Thermo_i2_fan(".$self->get_object_name.")", - temp_item => "new Insteon::Thermo_i2_temp(".$self->get_object_name.")", - humidity_item => "new Insteon::Thermo_i2_humidity(".$self->get_object_name.")", - setpoint_h_item => "new Insteon::Thermo_i2_setpoint_h(".$self->get_object_name.")", - setpoint_c_item => "new Insteon::Thermo_i2_setpoint_c(".$self->get_object_name.")" - ); - - #Now create them - foreach my $child_type (keys %child_objs) { - #Name the Child Object - my $child_name = $self->get_object_name . "_" . $child_type; - - #Define the code to run in eval - my $eval_cmd; - $eval_cmd = "use vars '$child_name';\n"; - $eval_cmd .= "$child_name = $child_objs{$child_type};\n"; - # Register child object with MH - $eval_cmd .= "&main::register_object_by_name('$child_name',$child_name);\n"; - - #Run the eval command - package main; - eval($eval_cmd); - if ($@) { - ::print_log( "[Insteon::Thermo_i2] Error in init eval command:\n $@ \n---\n $eval_cmd"); - } - package Insteon::Thermo_i2; - - #Get the newly created child object - $$self{$child_type} = ::get_object_by_name($child_name); - $$self{$child_type}->{object_name} = "$child_name"; - - #Add child to the same groups as parent - foreach my $parent_group (::list_groups_by_object($self,1)){ - $parent_group->add($$self{$child_type}); - } - } - - #Create tie so that changes in parent update the child - $self -> tie_event ('Insteon::Thermo_i2::parent_event(\''.$$self{object_name} . '\', "$state")'); } sub sync_links{ From ae540383ee61f8c5dd06cefdf06521cecf4075aa Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 17:21:17 -0700 Subject: [PATCH 53/86] Insteon_thermo_i2: Remove Parent_Event Routine Not needed without tied children. --- lib/Insteon/Thermostat.pm | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index d5f1b5992..5d6533e75 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -503,29 +503,6 @@ sub sync_links{ return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } -sub parent_event { - my ($self, $p_state) = @_; - $self = ::get_object_by_name($self); - if ($p_state eq 'temp_change'){ - $$self{temp_item}->set_receive($self->get_temp()); - } - elsif ($p_state eq 'heat_setpoint_change'){ - $$self{setpoint_h_item}->set_receive($self->get_heat_sp()); - } - elsif ($p_state eq 'cool_setpoint_change'){ - $$self{setpoint_c_item}->set_receive($self->get_cool_sp()); - } - elsif ($p_state eq 'fan_mode_change'){ - $$self{fan_item}->set_receive($self->get_fan_mode()); - } - elsif ($p_state eq 'mode_change'){ - $$self{mode_item}->set_receive($self->get_mode()); - } - elsif ($p_state eq 'humid_change'){ - $$self{humidity_item}->set_receive($$self{humid}); - } -} - sub poll_simple{ my ($self) = @_; my $extra = "020000000000000000000000000000"; From 6558d86e3a3db751369f0744549d3cb25f74decd Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 17:42:38 -0700 Subject: [PATCH 54/86] Insteon_thermo_i2: Change Name of Child Objects While designed for i2 devices, the child objects should work with i1 devices as well, except the humidity object which I don't believe exits for i1 devices. --- lib/Insteon/Thermostat.pm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 5d6533e75..587adbd5f 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -778,10 +778,10 @@ sub new { return $self; } -package Insteon::Thermo_i2_mode; +package Insteon::Thermo_mode; use strict; -@Insteon::Thermo_i2_mode::ISA = ('Generic_Item'); +@Insteon::Thermo_mode::ISA = ('Generic_Item'); sub new { my ($class,$parent) = @_; @@ -813,10 +813,10 @@ sub set_receive { $self->SUPER::set($p_state); } -package Insteon::Thermo_i2_fan; +package Insteon::Thermo_fan; use strict; -@Insteon::Thermo_i2_fan::ISA = ('Generic_Item'); +@Insteon::Thermo_fan::ISA = ('Generic_Item'); sub new { my ($class, $parent) = @_; @@ -848,10 +848,10 @@ sub set_receive { $self->SUPER::set($p_state); } -package Insteon::Thermo_i2_temp; +package Insteon::Thermo_temp; use strict; -@Insteon::Thermo_i2_temp::ISA = ('Generic_Item'); +@Insteon::Thermo_temp::ISA = ('Generic_Item'); sub new { my ($class, $parent) = @_; @@ -866,10 +866,10 @@ sub set_receive { my ($self, $p_state) = @_; $self->SUPER::set($p_state); } -package Insteon::Thermo_i2_humidity; +package Insteon::Thermo_humidity; use strict; -@Insteon::Thermo_i2_humidity::ISA = ('Generic_Item'); +@Insteon::Thermo_humidity::ISA = ('Generic_Item'); sub new { my ($class, $parent) = @_; @@ -885,10 +885,10 @@ sub set_receive { $self->SUPER::set($p_state); } -package Insteon::Thermo_i2_setpoint_h; +package Insteon::Thermo_setpoint_h; use strict; -@Insteon::Thermo_i2_setpoint_h::ISA = ('Generic_Item'); +@Insteon::Thermo_setpoint_h::ISA = ('Generic_Item'); sub new { my ($class, $parent) = @_; @@ -925,10 +925,10 @@ sub set_receive { $self->SUPER::set($p_state); } -package Insteon::Thermo_i2_setpoint_c; +package Insteon::Thermo_setpoint_c; use strict; -@Insteon::Thermo_i2_setpoint_c::ISA = ('Generic_Item'); +@Insteon::Thermo_setpoint_c::ISA = ('Generic_Item'); sub new { my ($class, $parent) = @_; From ed339a7662a4a57ab96396cfb56282ec837c0977 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 17:45:03 -0700 Subject: [PATCH 55/86] Insteon_thermo_it: Cleanup Example Thermostat Code File Fix to match current code base --- code/examples/Insteon_thermostat.pl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/code/examples/Insteon_thermostat.pl b/code/examples/Insteon_thermostat.pl index 4f9ad29bf..bd6cf6ce5 100755 --- a/code/examples/Insteon_thermostat.pl +++ b/code/examples/Insteon_thermostat.pl @@ -1,13 +1,13 @@ # Category=HVAC -$v_test_thermostat = new Voice_Cmd("Send Thermostat cmd [ping,poll_mode,mode_off,mode_heat,mode_cool,mode_auto,mode_pgm_heat,mode_pgm_cool,mode_pgm_auto,fan_on,fan_auto,poll_temp,poll_setpoint]"); +$v_test_thermostat = new Voice_Cmd("Send Thermostat cmd [poll_mode,mode_off,mode_heat,mode_cool,mode_auto,mode_pgm_heat,mode_pgm_cool,mode_pgm_auto,fan_on,fan_auto,poll_temp,poll_setpoint]"); # Create the Object in user code: #use Insteon_Thermostat; -#$thermostat = new Insteon_Thermostat($plm,'12.34.56'); +#$thermostat = new Insteon_Thermostat('12.34.56', $plm); # or in items.mht (read_table_A) -#IPLT, 12.34.56,, thermostat, HVAC, plm +#INSTEON_THERMOSTAT, 12.34.56,, thermostat, HVAC # poll_setpoint also runs poll_mode if ($Startup || $Reload) { @@ -29,9 +29,7 @@ if (my $state = said $v_test_thermostat) { - if ($state eq 'ping') { - $thermostat->ping(); - }elsif ($state eq 'poll_mode') { + if ($state eq 'poll_mode') { $thermostat->poll_mode(); }elsif ($state eq 'poll_temp') { $thermostat->poll_temp(); From 1842025592a4a594b89cf55bff64496c00b00d8d Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 17:45:45 -0700 Subject: [PATCH 56/86] Insteon_thermo_i2: Add Example to Thermostat Code File for Child Objects Add example entry into code file to demonstrate how to create child objects that are tied to parent item --- code/examples/Insteon_thermostat.pl | 56 ++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/code/examples/Insteon_thermostat.pl b/code/examples/Insteon_thermostat.pl index bd6cf6ce5..71400fe67 100755 --- a/code/examples/Insteon_thermostat.pl +++ b/code/examples/Insteon_thermostat.pl @@ -87,4 +87,58 @@ $thermostat->heat_setpoint(66); $thermostat->cool_setpoint(82); $thermostat->poll_setpoint(); -} +} + +## The examples show how a Generic Item can be Used to Track and Display +## individual data points in the thermostat. + +#Define the Children +$thermo_temp = new Insteon::Thermo_temp($thermostat); +$thermo_fan = new Insteon::Thermo_fan($thermostat); +$thermo_mode = new Insteon::Thermo_mode($thermostat); +$thermo_humidity = new Insteon::Thermo_humidity($thermostat); +$thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); +$thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); + +#Add the Children to the HVAC Group +$HVAC->add($thermo_temp); +$HVAC->add($thermo_fan); +$HVAC->add($thermo_mode); +$HVAC->add($thermo_humidity); +$HVAC->add($thermo_setpoint_h); +$HVAC->add($thermo_setpoint_c); + +if ($Reload){ + #Create tie so that changes in parent update the child + $thermostat -> tie_event ('::thermo_parent_event($object->get_object_name, "$state")'); + + #set Children to initial states + $thermo_temp->set_receive($thermostat->get_temp()); + $thermo_setpoint_h->set_receive($thermostat->get_heat_sp()); + $thermo_setpoint_c->set_receive($thermostat->get_cool_sp()); + $thermo_fan->set_receive($thermostat->get_fan_mode()); + $thermo_mode->set_receive($thermostat->get_mode()); +} + +sub thermo_parent_event { + my ($self, $p_state) = @_; + $self = ::get_object_by_name($self); + if ($p_state eq 'temp_change'){ + $thermo_temp->set_receive($self->get_temp(), $self); + } + elsif ($p_state eq 'heat_setpoint_change'){ + $thermo_setpoint_h->set_receive($self->get_heat_sp(), $self); + } + elsif ($p_state eq 'cool_setpoint_change'){ + $thermo_setpoint_c->set_receive($self->get_cool_sp(), $self); + } + elsif ($p_state eq 'fan_mode_change'){ + $thermo_fan->set_receive($self->get_fan_mode(), $self); + } + elsif ($p_state eq 'mode_change'){ + $thermo_mode->set_receive($self->get_mode(), $self); + } + elsif ($p_state eq 'humid_change'){ + $thermo_humidity->set_receive($$self{humid}, $self); + } +} From 80f67ebe548c4b9856d1a5cfb7fdadb573037aaf Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 19:40:25 -0700 Subject: [PATCH 57/86] Insteon_thermo_i2: Fix Comments in Example Code --- code/examples/Insteon_thermostat.pl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/examples/Insteon_thermostat.pl b/code/examples/Insteon_thermostat.pl index 71400fe67..88408e944 100755 --- a/code/examples/Insteon_thermostat.pl +++ b/code/examples/Insteon_thermostat.pl @@ -89,8 +89,10 @@ $thermostat->poll_setpoint(); } -## The examples show how a Generic Item can be Used to Track and Display -## individual data points in the thermostat. +## The examples show how the defined child objects can be Used to Track and Display +## individual data points in the thermostat. Each of the child objects will +## display and permit the adjusting (if applicable) of one data point such as +## fan mode or cool setpoint. #Define the Children $thermo_temp = new Insteon::Thermo_temp($thermostat); From 220f503a04b215d802b23d90cf6f2ee961dd2b9e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 19:45:25 -0700 Subject: [PATCH 58/86] Insteon_thermo_i2: Remove Complex Tie Example from Example User Code Will add to individual child objects instead --- code/examples/Insteon_thermostat.pl | 35 ----------------------------- 1 file changed, 35 deletions(-) diff --git a/code/examples/Insteon_thermostat.pl b/code/examples/Insteon_thermostat.pl index 88408e944..5b7e83650 100755 --- a/code/examples/Insteon_thermostat.pl +++ b/code/examples/Insteon_thermostat.pl @@ -109,38 +109,3 @@ $HVAC->add($thermo_humidity); $HVAC->add($thermo_setpoint_h); $HVAC->add($thermo_setpoint_c); - -if ($Reload){ - #Create tie so that changes in parent update the child - $thermostat -> tie_event ('::thermo_parent_event($object->get_object_name, "$state")'); - - #set Children to initial states - $thermo_temp->set_receive($thermostat->get_temp()); - $thermo_setpoint_h->set_receive($thermostat->get_heat_sp()); - $thermo_setpoint_c->set_receive($thermostat->get_cool_sp()); - $thermo_fan->set_receive($thermostat->get_fan_mode()); - $thermo_mode->set_receive($thermostat->get_mode()); -} - -sub thermo_parent_event { - my ($self, $p_state) = @_; - $self = ::get_object_by_name($self); - if ($p_state eq 'temp_change'){ - $thermo_temp->set_receive($self->get_temp(), $self); - } - elsif ($p_state eq 'heat_setpoint_change'){ - $thermo_setpoint_h->set_receive($self->get_heat_sp(), $self); - } - elsif ($p_state eq 'cool_setpoint_change'){ - $thermo_setpoint_c->set_receive($self->get_cool_sp(), $self); - } - elsif ($p_state eq 'fan_mode_change'){ - $thermo_fan->set_receive($self->get_fan_mode(), $self); - } - elsif ($p_state eq 'mode_change'){ - $thermo_mode->set_receive($self->get_mode(), $self); - } - elsif ($p_state eq 'humid_change'){ - $thermo_humidity->set_receive($$self{humid}, $self); - } -} From a5d8ef3d2a9eaa61d298f7e54340598056398b9a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 19:55:25 -0700 Subject: [PATCH 59/86] Insteon_thermo_i2: Add Parent Event Routine to Base Thermostat Code parent_event is called by tie_event on state changes in the parent. The routine updates the child object with the correct state --- lib/Insteon/Thermostat.pm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 587adbd5f..f3bd5af1e 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -364,6 +364,29 @@ sub _process_message return $clear_message; } +#Used to update the state of child objects +sub parent_event { + my ($self, $p_state) = @_; + if ($p_state eq 'mode_change'){ + $$self{child_mode}->set_receive($self->get_mode()); + } + elsif ($p_state eq 'temp_change'){ + $$self{child_temp}->set_receive($self->get_temp(), $self); + } + elsif ($p_state eq 'heat_setpoint_change'){ + $$self{child_setpoint_h}->set_receive($self->get_heat_sp(), $self); + } + elsif ($p_state eq 'cool_setpoint_change'){ + $$self{child_setpoint_c}->set_receive($self->get_cool_sp(), $self); + } + elsif ($p_state eq 'fan_mode_change'){ + $$self{child_fan}->set_receive($self->get_fan_mode(), $self); + } + elsif ($p_state eq 'humid_change'){ + $$self{child_humidity}->set_receive($$self{humid}, $self); + } +} + # Overload methods we don't use, but would otherwise cause Insteon traffic. sub request_status { return 0 } From 1e934f1b7c9ca3dcfe1922a8ba22d3ade4c01f63 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 18 Apr 2013 20:05:25 -0700 Subject: [PATCH 60/86] Insteon_thermo_i2: Add Child Object to Parent Hash and Call Tie_Event The child objects are stored in the parent hash so that the parent can locate the correct child to update it. tie_events is set on child object creation. --- lib/Insteon/Thermostat.pm | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index f3bd5af1e..1a5609749 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -812,7 +812,8 @@ sub new { bless $self, $class; $$self{parent} = $parent; @{$$self{states}} = ('Off', 'Heat', 'Cool', 'Auto', 'Program'); - $self->set_receive($$self{parent}->get_mode()); + $$self{parent}{child_mode} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "mode_change"); return $self; } @@ -847,7 +848,8 @@ sub new { bless $self, $class; $$self{parent} = $parent; @{$$self{states}} = ('Auto', 'On'); - $self->set_receive($$self{parent}->get_fan_mode()); + $$self{parent}{child_fan} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "fan_mode_change"); return $self; } @@ -881,7 +883,8 @@ sub new { my $self = new Generic_Item(); bless $self, $class; $$self{parent} = $parent; - $self->set_receive($$self{parent}->get_temp()); + $$self{parent}{child_temp} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "temp_change"); return $self; } @@ -889,6 +892,7 @@ sub set_receive { my ($self, $p_state) = @_; $self->SUPER::set($p_state); } + package Insteon::Thermo_humidity; use strict; @@ -899,7 +903,8 @@ sub new { my $self = new Generic_Item(); bless $self, $class; $$self{parent} = $parent; - $self->set_receive($$self{parent}{humid}); + $$self{parent}{child_humidity} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "humid_change"); return $self; } @@ -919,7 +924,8 @@ sub new { bless $self, $class; $$self{parent} = $parent; @{$$self{states}} = ('Cooler' , 'Warmer'); - $self->set_receive($$self{parent}->get_heat_sp()); + $$self{parent}{child_setpoint_h} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "heat_setpoint_change"); return $self; } @@ -959,7 +965,8 @@ sub new { bless $self, $class; $$self{parent} = $parent; @{$$self{states}} = ('Cooler', 'Warmer'); - $self->set_receive($$self{parent}->get_cool_sp()); + $$self{parent}{child_setpoint_c} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "cool_setpoint_change"); return $self; } From 6d30f8bd33048f7246962fce87e19a543b3d0144 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 22 Apr 2013 20:45:49 -0700 Subject: [PATCH 61/86] Insteon_Thermo_i2: Final touches on Thermostat Code --- lib/Insteon/Thermostat.pm | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 1a5609749..7b05d2e2d 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -47,6 +47,14 @@ All of the states that may be set: fan_mode_change: Fan mode changed (call get_fan_mode() to get value). +Child objects which track the states of the thermostat can be created: +$thermo_temp = new Insteon::Thermo_temp($thermostat); +$thermo_fan = new Insteon::Thermo_fan($thermostat); +$thermo_mode = new Insteon::Thermo_mode($thermostat); +$thermo_humidity = new Insteon::Thermo_humidity($thermostat); +$thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); +$thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); + see code/examples/Insteon_thermostat.pl for more. =head1 BUGS @@ -68,11 +76,9 @@ Kevin Rober Keegan =head1 TODO - - Look at possible bugs when starting from factory defaults - There seemed to be an issue with the setpoints changing when changing modes until - they were set programatically. - - Test fan modes and associated state_changes - Manage aldb - should be able to adjust setpoints based on plm scene. <- may be overkill + - Add ability to link devices to the I2 Thermostat groups 1-4 + - Add ability to link other devices to the broadcast group. =head1 INHERITS @@ -516,16 +522,23 @@ sub init { sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; - #Make sure thermostat is set to broadcast changes - ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; - my $extra = "000008000000000000000000000000"; - my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); - $$self{_ext_set_get_action} = 'set'; - $self->_send_cmd($message); + if !($audit_mode){ + #Make sure thermostat is set to broadcast changes + ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; + my $extra = "000008000000000000000000000000"; + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$self{_ext_set_get_action} = 'set'; + $self->_send_cmd($message); + } # Call the main sync_links code return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } +=item C + +Requests the status of all Thermostat data points (temp, fan, mode ...) in a single +request. Only available for I2 devices. +=cut sub poll_simple{ my ($self) = @_; my $extra = "020000000000000000000000000000"; @@ -775,7 +788,10 @@ sub simple_message { $message = new Insteon::InsteonMessage('insteon_ext_send', $self, $type, $extra); return $message; } +=item C +Sets the data and time of the thermostat based on the time of the MH server. +=cut sub sync_time { my ($self) = @_; #In order to set the time, we need to know the current value of other data From 1005109bd1afb9bda622354bf078890205054674 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 22 Apr 2013 21:03:07 -0700 Subject: [PATCH 62/86] Insteon_Thermo_i2: Fix Typo --- lib/Insteon/Thermostat.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 7b05d2e2d..3fc90c8d5 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -55,6 +55,8 @@ $thermo_humidity = new Insteon::Thermo_humidity($thermostat); $thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); $thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); +where $thermostat is the parent object to track. + see code/examples/Insteon_thermostat.pl for more. =head1 BUGS @@ -522,7 +524,7 @@ sub init { sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; - if !($audit_mode){ + if (!$audit_mode){ #Make sure thermostat is set to broadcast changes ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; my $extra = "000008000000000000000000000000"; From 30547d6be6845cf01dee6a6e8bc62756c520d04f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 27 May 2013 18:56:03 -0700 Subject: [PATCH 63/86] Insteon_Thermostat: Cleanup Instructions, Make Print Log More Clear, Force Temperature to a Whole Number --- lib/Insteon/Thermostat.pm | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 3fc90c8d5..9ada5af49 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -35,7 +35,8 @@ And, you can set the temperature and mode at will... $thermostat->cool_setpoint(89); } -All of the states that may be set: +All of the states of the parent object that may be set by MH, you can use tie_event +to link specific actions to these states: temp_change: Inside temperature changed (call get_temp() to get value) heat_sp_change: Heat setpoint was changed @@ -61,11 +62,8 @@ see code/examples/Insteon_thermostat.pl for more. =head1 BUGS -Initial code for Venstar thermostats, which use Insteon engine version i1, only -provided basic features. The new Insteon 2441TH thermostats use the i2cs engine -and only allow the polling, but not setting, of the thermostat attributes using -i2 code. As such, I am unable to test or provide enhancements to certain i1 -only aspects. +This code has not been tested on older Venstar thermsotats, however it is believed +that the basic functionality should work as it did in the old code. =head1 AUTHOR @@ -74,13 +72,14 @@ Gregg Liming Brian Warren Enhanced to i2 by: -Kevin Rober Keegan +Kevin Robert Keegan =head1 TODO - - Manage aldb - should be able to adjust setpoints based on plm scene. <- may be overkill - - Add ability to link devices to the I2 Thermostat groups 1-4 - - Add ability to link other devices to the broadcast group. + - Enable Linking of the Thermostat as a Responder - The current design of MH + will not create valid links when the thermostat is the responder. To enable + this function, a reorganization of the add_link and update_link code at the + BaseObject level needs to be performed. =head1 INHERITS @@ -613,32 +612,32 @@ sub _process_message { } elsif ($msg{command} eq "status_temp" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Status Temp Message ". - "for ". $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2] Received Temp Change Message ". + "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_short_temp($msg{extra}); } elsif ($msg{command} eq "status_mode" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Status Mode Message ". - "for ". $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2] Received Mode Change Message ". + "from ". $self->get_object_name) if $main::Debug{insteon}; $self->status_mode($msg{extra}); } elsif ($msg{command} eq "status_cool" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Status Cool Message ". - "for ". $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2] Received Cool Setpoint Change Message ". + "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_cool($msg{extra}); } elsif ($msg{command} eq "status_humid" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Status Humid Message ". - "for ". $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2] Received Humidity Change Message ". + "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_humid($msg{extra}); } elsif ($msg{command} eq "status_heat" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Status Heat Message ". - "for ". $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2] Received Heat Setpoint Change Message ". + "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_heat($msg{extra}); } else { @@ -724,7 +723,8 @@ sub hex_long_temp{ my $temp_cel = (hex($hex_temp)/10); ## ATM I am going to assume farenheit b/c that is what I have # in future, can pull setting bit from thermometer - $$self{temp} = (($temp_cel*9)/5 +32); + # Extra .5 since sprintf doesn't round + $$self{temp} = sprintf("%d", (($temp_cel*9)/5 +32 +.5)); $self->set_receive('temp_change'); } From 1f42c65a70ea6a305a877fbc491c1e09b90eab51 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 27 May 2013 18:57:55 -0700 Subject: [PATCH 64/86] Insteon_Thermo: Add support for Groups 2-4, Add Status Child Groups 1-4 of the i2 devices report on/off events for cooling, heating, high humidity, and low humidity. Insteon objects can be linked to these events. e.g. Turn on a fan when the humidity is high. In addition, a new status child has been added. It provides a simple text of the current status of the HVAC system. --- lib/Insteon/Thermostat.pm | 186 +++++++++++++++++++++++++++++++++++--- 1 file changed, 175 insertions(+), 11 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 9ada5af49..553a05cca 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -9,10 +9,30 @@ Enables support for an Insteon Thermostat. =head1 SYNOPSIS In user code: + $thermostat = new Insteon_Thermostat($myPLM, '12.34.56'); + +Additional i2 specific objects: + + $thermostat_heating = new Insteon_Thermostat($myPLM, '12.34.56:02'); + $thermostat_high_humid = new Insteon_Thermostat($myPLM, '12.34.56:03'); + $thermostat_low_humid = new Insteon_Thermostat($myPLM, '12.34.56:04'); + $thermostat_broadcast = new Insteon_Thermostat($myPLM, '12.34.56:EF'); + +These devices will not have any states, but are only used for linking purposes. In items.mht: + INSTEON_THERMOSTAT, 12.34.56, thermostat, HVAC + +Additional i2 specific objects: + + INSTEON_THERMOSTAT, 12.34.56:02, thermostat_heating, HVAC + INSTEON_THERMOSTAT, 12.34.56:03, thermostat_high_humid, HVAC + INSTEON_THERMOSTAT, 12.34.56:04, thermostat_low_humid, HVAC + INSTEON_THERMOSTAT, 12.34.56:EF, thermostat_broadcast, HVAC + +These devices will not have any states, but are only used for linking purposes. Poll for temperature changes. @@ -47,16 +67,60 @@ to link specific actions to these states: (call get_mode() to get value). fan_mode_change: Fan mode changed (call get_fan_mode() to get value). - -Child objects which track the states of the thermostat can be created: -$thermo_temp = new Insteon::Thermo_temp($thermostat); -$thermo_fan = new Insteon::Thermo_fan($thermostat); -$thermo_mode = new Insteon::Thermo_mode($thermostat); -$thermo_humidity = new Insteon::Thermo_humidity($thermostat); -$thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); -$thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); - -where $thermostat is the parent object to track. + status_change: Heating, Cooling, Dehumidifying, or Humidifying change (i2 only) + (call get_status() to get status). + +I2 Broadcast messages: + +If a group EF device is defined, MH will receive broadcast changes from the +thermostat. When enabled, broadcast messages for changes in setpoint, mode, +temp, and humidity will be sent to MH. When enabled, there is no reason to +poll the thermostat, except for possibly at reboot. To enable simply define +the EF group as described above and run sync links. + +Broadcast messages are NOT sent when the heater turns on/off. Broadcast +message are also NOT sent when the humidity setpoints are exceeded. Instead, +you must define the heating, high_humdid, and _low_humid groups and link them +to MH. (The base group 01 is the cooling group and should always be linked to +MH). When linked, these groups will send on/off commands to MH when these events +occur. Alternatively, you can periodically call the poll_simple method to check +the status of these attributes. + +Linking: + +I am not sure how or if the i1 device can be linked to other devices. + +I2 devices have 5 controllers, groups 01-04 plus the broadcast group EF. At the +moment, MH only supports using the thermostat as a controller of another device. +To control another device, simply define it as a scene member of the desired +thermostat group. The groups are: + + 01 - Cooling - Will send an ON/OFF command when the A/C is turned on/off. + 02 - Heating - Will send an ON/OFF command when the heater is turned on/off. + 03 - Humid High - Will send an ON/OFF command when the humidity exceeds the + humid high setpoint. + 04 - Humid Low - Will send an ON/OFF command when the humidity falls below the + humid low setpoint. + EF - Broadcast - Other than MH, I do not know if any other device can + respond to these commands. + +Tracking Child Objects: + +For both, i1 and i2 devices, optional child objects which track the states of the +thermostat can be created in user code: + + $thermo_temp = new Insteon::Thermo_temp($thermostat); + $thermo_fan = new Insteon::Thermo_fan($thermostat); + $thermo_mode = new Insteon::Thermo_mode($thermostat); + $thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); + $thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); + $thermo_humidity = new Insteon::Thermo_humidity($thermostat); #Only available on i2 devices + $thermo_status = new Insteon::Thermo_status($thermostat); #Only available on i2 devices + +where $thermostat is the parent object to track. The state of these child objects +will be the state of the various objects. This makes the display of the various +states easier within MH. The child objects also make it easier to change the +various states on the thermostat. see code/examples/Insteon_thermostat.pl for more. @@ -392,6 +456,9 @@ sub parent_event { elsif ($p_state eq 'humid_change'){ $$self{child_humidity}->set_receive($$self{humid}, $self); } + elsif ($p_state eq 'status_change'){ + $$self{child_status}->set_receive($self->get_status(), $self); + } } # Overload methods we don't use, but would otherwise cause Insteon traffic. @@ -501,7 +568,7 @@ sub init { my ($self) = @_; $$self{message_types} = \%message_types; #Set saved state unique to i2 devices - $self->restore_data('humid'); + $self->restore_data('humid', 'cooling', 'heating', 'high_humid', 'low_humid'); # Create the broadcast dummy item # This may not belong here. Maybe this should go into read table A? @@ -521,6 +588,33 @@ sub init { $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; } +sub set { + my ($self, $p_state, $p_setby, $p_respond) = @_; + my $root = $self->get_root(); + if (!(ref $p_setby) || !($p_setby->equals($self))) { + ::print_log("[Insteon::Thermo_i2] Sorry, you cannot control the ". + "thermostat in this manner. Please read the documentation ". + "for Insteon::Thermostat for help."); + return; + } + #Update the root object state + my $link_state = &Insteon::BaseObject::derive_link_state($p_state); + if ($self->group eq '01'){ + $root->_cooling($link_state); + } + elsif ($self->group eq '02') { + $root->_heating($link_state); + } + elsif ($self->group eq '03') { + $root->_high_humid($link_state); + } + elsif ($self->group eq '04') { + $root->_low_humid($link_state); + } + #Update the status of linked devices + $self->set_linked_devices($link_state); +} + sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; if (!$audit_mode){ @@ -548,6 +642,25 @@ sub poll_simple{ $self->_send_cmd($message); } +=item C + +Returns a text string describing the current status of the thermostat. May include +a combination of "Heating; Cooling; Dehumidifying; Humidifying; or Off." Only +available for I2 devices. + +=cut +sub get_status() { + my ($self) = @_; + my $root = $self->get_root(); + my $output = ""; + $output .= "Heating, " if ($$root{heating} eq 'on'); + $output .= "Cooling, " if ($$root{cooling} eq 'on'); + $output .= "Dehumidifying, " if ($$root{high_humid} eq 'on'); + $output .= "Humidifying" if ($$root{low_humid} eq 'on'); + $output = 'Off' if ($output eq ''); + return $output; +} + sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; @@ -736,6 +849,8 @@ sub hex_short_temp{ sub hex_status{ ### Not sure about this one yet, was 80 when set to auto but no activity + ## need to call _cooling, _heating, _high_humid, and _low_humid when + ## figured out } sub hex_heat{ @@ -752,6 +867,34 @@ sub _humid { return $$self{humid}; } +sub _cooling { + my ($self,$p_state) = @_; + $$self{cooling} = $p_state; + $self->set_receive('status_change'); + return $$self{cooling}; +} + +sub _heating { + my ($self,$p_state) = @_; + $$self{heating} = $p_state; + $self->set_receive('status_change'); + return $$self{heating}; +} + +sub _high_humid { + my ($self,$p_state) = @_; + $$self{high_humid} = $p_state; + $self->set_receive('status_change'); + return $$self{high_humid}; +} + +sub _low_humid { + my ($self,$p_state) = @_; + $$self{low_humid} = $p_state; + $self->set_receive('status_change'); + return $$self{low_humid}; +} + =item C Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', @@ -1012,6 +1155,27 @@ sub set_receive { my ($self, $p_state) = @_; $self->SUPER::set($p_state); } + +package Insteon::Thermo_status; +use strict; + +@Insteon::Thermo_status::ISA = ('Generic_Item'); + +sub new { + my ($class, $parent) = @_; + my $self = new Generic_Item(); + bless $self, $class; + $$self{parent} = $parent; + $$self{parent}{child_status} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "status_change"); + return $self; +} + +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} + 1; =back From e42ae6c90e1f60c08f898738850b6caa1004316f Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 27 May 2013 19:00:02 -0700 Subject: [PATCH 65/86] Insteon_Thermostat: Add support for setting the high_humidity setpoint --- lib/Insteon/Thermostat.pm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 553a05cca..8bee556e0 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -946,6 +946,25 @@ sub sync_time { $self->poll_simple(); } +=item C + +Sets the high humidity setpoint. + +=cut +sub high_humid_setpoint { + my ($self, $value) = @_; + main::print_log("[Insteon::Thermo_i2] Setting high humid setpoint -> $value") if $main::Debug{insteon}; + if($value !~ /^\d+$/){ + main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); + return; + } + my $extra = "00000B" . sprintf("%02x", $value); + $extra .= '0' x (30 - length $extra); + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$self{_ext_set_get_action} = 'set'; + $self->_send_cmd($message); +} + package Insteon::Thermo_i2_bcast; use strict; From 849fd9de036fcb3b0b2a67d5a1a8d944195622dd Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 1 Jun 2013 15:34:38 -0700 Subject: [PATCH 66/86] Insteon_Thermoi2: Add Support for Humidity Setpoints --- lib/Insteon/Thermostat.pm | 66 +++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 8bee556e0..489ef9a96 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -718,8 +718,27 @@ sub _process_message { $$self{sync_time} = undef; $self->_send_cmd($message); } - } else { - main::print_log("[Insteon::Thermo_i2] WARN: Corrupt Extended " + } + elsif (substr($msg{extra},0,8) eq "00000101") { + $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); + #0 = 00 #14 = Cool SP + #2 = 00 #16 = Heat SP + #4 = 01 Response #18 = RF Offset + #6 = 01 Data Set 2 #20 = Energy Saving Setback + #8 = humid low #22 = External TempOffset + #10 = humid high #24 = 1 = Status Report Enabled + #12 = firmware #26 = 1 = External Power On + #28 = 1 = Int, 2=Ext Temp + main::print_log("[Insteon::Thermo_i2] Humidity setpoints for ". + $self->get_object_name . " are High: " . + $self->_high_humid(hex(substr($msg{extra}, 8, 2))) . + " Low: " . $self->_low_humid(hex(substr($msg{extra}, 10, 2))) + ) if $main::Debug{insteon}; + $clear_message = 1; + $self->_process_command_stack(%msg); + } + else { + main::print_log("[Insteon::Thermo_i2] WARN: Unknown Extended " ."Set/Get Data Received for ". $self->get_object_name) if $main::Debug{insteon}; } } @@ -881,17 +900,20 @@ sub _heating { return $$self{heating}; } + sub _high_humid { my ($self,$p_state) = @_; - $$self{high_humid} = $p_state; - $self->set_receive('status_change'); + if ($p_state ne $$self{high_humid}) { + $$self{high_humid} = $p_state; + } return $$self{high_humid}; } sub _low_humid { my ($self,$p_state) = @_; - $$self{low_humid} = $p_state; - $self->set_receive('status_change'); + if ($p_state ne $$self{low_humid}) { + $$self{low_humid} = $p_state; + } return $$self{low_humid}; } @@ -965,6 +987,38 @@ sub high_humid_setpoint { $self->_send_cmd($message); } +=item C + +Sets the low humidity setpoint. + +=cut +sub low_humid_setpoint { + my ($self, $value) = @_; + main::print_log("[Insteon::Thermo_i2] Setting low humid setpoint -> $value") if $main::Debug{insteon}; + if($value !~ /^\d+$/){ + main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); + return; + } + my $extra = "00000C" . sprintf("%02x", $value); + $extra .= '0' x (30 - length $extra); + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$self{_ext_set_get_action} = 'set'; + $self->_send_cmd($message); +} + +=item C + +Retreives and prints the current humidity high and low setpoints. Only available for I2 devices. +=cut +sub get_humid_setpoints{ + my ($self) = @_; + my $extra = "00000001"; + $extra .= '0' x (30 - length $extra); + my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + $$self{_ext_set_get_action} = 'get'; + $self->_send_cmd($message); +} + package Insteon::Thermo_i2_bcast; use strict; From 54b0beb6a69aed48c34c65af34d9f63551edbd7e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 1 Jun 2013 15:45:43 -0700 Subject: [PATCH 67/86] Insteon_Thermoi2: Elevate Broadcast Item to a Full Fledged Object There is a possibility that a user may define a link to the EF group. If a broadcast object is defined, sync_links will automatically enable the broadcast setting. --- lib/Insteon/Thermostat.pm | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 489ef9a96..647fb1f35 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -569,23 +569,6 @@ sub init { $$self{message_types} = \%message_types; #Set saved state unique to i2 devices $self->restore_data('humid', 'cooling', 'heating', 'high_humid', 'low_humid'); - - # Create the broadcast dummy item - # This may not belong here. Maybe this should go into read table A? - # Otherwise, users cannot define insteon scenes containing this device. - # While rare a thermostat may be set to provide broadcast updates to - # another device - my $dev_id = $self->device_id(); - $dev_id =~ /(\w\w)(\w\w)(\w\w)/; - $dev_id = "$1.$2.$3"; - $$self{bcast_item} = new Insteon::Thermo_i2_bcast("$dev_id".':EF'); - - # Add bcast object to list of Insteon objects - Insteon::add($$self{bcast_item}); - - # Register bcast object with MH - &main::register_object_by_name('$' . $self->get_object_name ."{bcast_item}",$$self{bcast_item}); - $$self{bcast_item}->{object_name} = '$' . $self->get_object_name ."{bcast_item}"; } sub set { @@ -617,7 +600,9 @@ sub set { sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; - if (!$audit_mode){ + my $dev_id = $self->device_id(); + my $bcast_obj = Insteon::get_object($self->device_id(), 'EF'); + if (!$audit_mode && ref $bcast_obj){ #Make sure thermostat is set to broadcast changes ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; my $extra = "000008000000000000000000000000"; @@ -1019,22 +1004,6 @@ sub get_humid_setpoints{ $self->_send_cmd($message); } -package Insteon::Thermo_i2_bcast; -use strict; - -@Insteon::Thermo_i2_bcast::ISA = ('Insteon::BaseDevice', 'Insteon::DeviceController'); - -###This is basically a dummy object, it is designed to allow a link from group -###EF to be added as part of sync links. Group EF is the broadcast group used -###by the 2441th thermostat to announce changes. - -sub new { - my ($class, $p_deviceid) = @_; - my $self = new Insteon::BaseDevice($p_deviceid); - bless $self, $class; - return $self; -} - package Insteon::Thermo_mode; use strict; From 9aced81c21d2c30803e8c5da920c1b99af9ff2dc Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Sat, 1 Jun 2013 18:07:46 -0700 Subject: [PATCH 68/86] Insteon_Thermoi2: Only send Broadcast Request Once --- lib/Insteon/Thermostat.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 647fb1f35..4992f0954 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -602,7 +602,7 @@ sub sync_links{ my ($self, $audit_mode, $callback, $failure_callback) = @_; my $dev_id = $self->device_id(); my $bcast_obj = Insteon::get_object($self->device_id(), 'EF'); - if (!$audit_mode && ref $bcast_obj){ + if (!$audit_mode && ref $bcast_obj && $self->is_root){ #Make sure thermostat is set to broadcast changes ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; my $extra = "000008000000000000000000000000"; From e654e93c7c167d5f4efe0fc626683452ca3a4151 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 7 Jun 2013 17:45:25 -0700 Subject: [PATCH 69/86] Insteon_Thermo: Catch Status Request Responses Catch and process status request responses before processing unique _process_message commands. --- lib/Insteon/Thermostat.pm | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 4992f0954..251ceb2ff 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -406,7 +406,14 @@ sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; - if ($msg{command} eq "thermostat_setpoint_cool" && $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 "thermostat_setpoint_cool" && $msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); main::print_log("[Insteon::Thermostat] Received ACK of cool setpoint ". "for ". $self->get_object_name) if $main::Debug{insteon}; @@ -649,7 +656,14 @@ sub get_status() { 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::Thermo_i2] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; From ce0b2a85f44c12d9c10438e4247ef47be11e591e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 27 Aug 2013 17:33:00 -0700 Subject: [PATCH 70/86] Insteon_Thermo: Convert i2 Entries to i2CS Some of the older thermostat devices including the 2441v venstar device were i2 devices but do not support the newer thermostat code. As such the distinction between the old and new devices is not i1 vs i2 but rather i2CS or not i2CS. --- lib/Insteon.pm | 6 ++-- lib/Insteon/Thermostat.pm | 72 +++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 166d5e61a..4cd68c02f 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -565,11 +565,11 @@ sub check_thermo_versions foreach my $thermo_device (@thermo_devices) { if ($thermo_device->isa('Insteon::Thermostat') && - $thermo_device->_aldb->aldb_version() eq "I2"){ + $thermo_device->_aldb->aldb_version() eq "I2CS"){ main::print_log("[Insteon] DEBUG4 Setting thermostat " - . $thermo_device->get_object_name() . " to i2") + . $thermo_device->get_object_name() . " to i2CS") if ($main::Debug{insteon} >= 4); - bless $thermo_device, 'Insteon::Thermo_i2'; + bless $thermo_device, 'Insteon::Thermo_i2CS'; $thermo_device->init(); } elsif ($thermo_device->isa('Insteon::Thermostat') diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 251ceb2ff..5ed8c940c 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -12,7 +12,7 @@ In user code: $thermostat = new Insteon_Thermostat($myPLM, '12.34.56'); -Additional i2 specific objects: +Additional i2CS specific objects: $thermostat_heating = new Insteon_Thermostat($myPLM, '12.34.56:02'); $thermostat_high_humid = new Insteon_Thermostat($myPLM, '12.34.56:03'); @@ -25,7 +25,7 @@ In items.mht: INSTEON_THERMOSTAT, 12.34.56, thermostat, HVAC -Additional i2 specific objects: +Additional i2CS specific objects: INSTEON_THERMOSTAT, 12.34.56:02, thermostat_heating, HVAC INSTEON_THERMOSTAT, 12.34.56:03, thermostat_high_humid, HVAC @@ -67,10 +67,10 @@ to link specific actions to these states: (call get_mode() to get value). fan_mode_change: Fan mode changed (call get_fan_mode() to get value). - status_change: Heating, Cooling, Dehumidifying, or Humidifying change (i2 only) + status_change: Heating, Cooling, Dehumidifying, or Humidifying change (i2CS only) (call get_status() to get status). -I2 Broadcast messages: +I2CS Broadcast messages: If a group EF device is defined, MH will receive broadcast changes from the thermostat. When enabled, broadcast messages for changes in setpoint, mode, @@ -90,7 +90,7 @@ Linking: I am not sure how or if the i1 device can be linked to other devices. -I2 devices have 5 controllers, groups 01-04 plus the broadcast group EF. At the +I2CS devices have 5 controllers, groups 01-04 plus the broadcast group EF. At the moment, MH only supports using the thermostat as a controller of another device. To control another device, simply define it as a scene member of the desired thermostat group. The groups are: @@ -106,7 +106,7 @@ thermostat group. The groups are: Tracking Child Objects: -For both, i1 and i2 devices, optional child objects which track the states of the +For both, i1 and i2CS devices, optional child objects which track the states of the thermostat can be created in user code: $thermo_temp = new Insteon::Thermo_temp($thermostat); @@ -114,8 +114,8 @@ thermostat can be created in user code: $thermo_mode = new Insteon::Thermo_mode($thermostat); $thermo_setpoint_h = new Insteon::Thermo_setpoint_h($thermostat); $thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); - $thermo_humidity = new Insteon::Thermo_humidity($thermostat); #Only available on i2 devices - $thermo_status = new Insteon::Thermo_status($thermostat); #Only available on i2 devices + $thermo_humidity = new Insteon::Thermo_humidity($thermostat); #Only available on i2CS devices + $thermo_status = new Insteon::Thermo_status($thermostat); #Only available on i2CS devices where $thermostat is the parent object to track. The state of these child objects will be the state of the various objects. This makes the display of the various @@ -135,7 +135,7 @@ Initial Code by: Gregg Liming Brian Warren -Enhanced to i2 by: +Enhanced to i2CS by: Kevin Robert Keegan =head1 TODO @@ -349,7 +349,7 @@ sub _mode() { =item C -Returns the last mode returned by C I2 devices will report auto for both auto and program_auto. +Returns the last mode returned by C I2CS devices will report auto for both auto and program_auto. =cut sub get_mode() { my ($self) = @_; @@ -556,10 +556,10 @@ sub simple_message { return $message; } -package Insteon::Thermo_i2; +package Insteon::Thermo_i2CS; use strict; -@Insteon::Thermo_i2::ISA = ('Insteon::Thermostat'); +@Insteon::Thermo_i2CS::ISA = ('Insteon::Thermostat'); our %message_types = ( %Insteon::Thermostat::message_types, @@ -574,7 +574,7 @@ our %message_types = ( sub init { my ($self) = @_; $$self{message_types} = \%message_types; - #Set saved state unique to i2 devices + #Set saved state unique to i2CS devices $self->restore_data('humid', 'cooling', 'heating', 'high_humid', 'low_humid'); } @@ -582,7 +582,7 @@ sub set { my ($self, $p_state, $p_setby, $p_respond) = @_; my $root = $self->get_root(); if (!(ref $p_setby) || !($p_setby->equals($self))) { - ::print_log("[Insteon::Thermo_i2] Sorry, you cannot control the ". + ::print_log("[Insteon::Thermo_i2CS] Sorry, you cannot control the ". "thermostat in this manner. Please read the documentation ". "for Insteon::Thermostat for help."); return; @@ -611,7 +611,7 @@ sub sync_links{ my $bcast_obj = Insteon::get_object($self->device_id(), 'EF'); if (!$audit_mode && ref $bcast_obj && $self->is_root){ #Make sure thermostat is set to broadcast changes - ::print_log("[Insteon::Thermo_i2] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; + ::print_log("[Insteon::Thermo_i2CS] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; my $extra = "000008000000000000000000000000"; my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); $$self{_ext_set_get_action} = 'set'; @@ -624,7 +624,7 @@ sub sync_links{ =item C Requests the status of all Thermostat data points (temp, fan, mode ...) in a single -request. Only available for I2 devices. +request. Only available for I2CS devices. =cut sub poll_simple{ my ($self) = @_; @@ -638,7 +638,7 @@ sub poll_simple{ Returns a text string describing the current status of the thermostat. May include a combination of "Heating; Cooling; Dehumidifying; Humidifying; or Off." Only -available for I2 devices. +available for I2CS devices. =cut sub get_status() { @@ -666,9 +666,9 @@ sub _process_message { 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::Thermo_i2] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2CS] Extended Set/Get ACK Received for " . $self->get_object_name) if $main::Debug{insteon}; if ($$self{_ext_set_get_action} eq 'set'){ - main::print_log("[Insteon::Thermo_i2] Clearing active message") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2CS] Clearing active message") if $main::Debug{insteon}; $clear_message = 1; $$self{_ext_set_get_action} = undef; $self->_process_command_stack(%msg); @@ -677,7 +677,7 @@ sub _process_message { elsif ($msg{command} eq "extended_set_get" && $msg{is_extended}) { if (substr($msg{extra},0,4) eq "0201") { $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Extended Set/Get Data ". + main::print_log("[Insteon::Thermo_i2CS] Extended Set/Get Data ". "Received for ". $self->get_object_name) if $main::Debug{insteon}; #0 = 2 #14 = Cool SP #2 = 1 #16 = humidity @@ -728,7 +728,7 @@ sub _process_message { #10 = humid high #24 = 1 = Status Report Enabled #12 = firmware #26 = 1 = External Power On #28 = 1 = Int, 2=Ext Temp - main::print_log("[Insteon::Thermo_i2] Humidity setpoints for ". + main::print_log("[Insteon::Thermo_i2CS] Humidity setpoints for ". $self->get_object_name . " are High: " . $self->_high_humid(hex(substr($msg{extra}, 8, 2))) . " Low: " . $self->_low_humid(hex(substr($msg{extra}, 10, 2))) @@ -737,37 +737,37 @@ sub _process_message { $self->_process_command_stack(%msg); } else { - main::print_log("[Insteon::Thermo_i2] WARN: Unknown Extended " + main::print_log("[Insteon::Thermo_i2CS] WARN: Unknown Extended " ."Set/Get Data Received for ". $self->get_object_name) if $main::Debug{insteon}; } } elsif ($msg{command} eq "status_temp" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Temp Change Message ". + main::print_log("[Insteon::Thermo_i2CS] Received Temp Change Message ". "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_short_temp($msg{extra}); } elsif ($msg{command} eq "status_mode" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Mode Change Message ". + main::print_log("[Insteon::Thermo_i2CS] Received Mode Change Message ". "from ". $self->get_object_name) if $main::Debug{insteon}; $self->status_mode($msg{extra}); } elsif ($msg{command} eq "status_cool" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Cool Setpoint Change Message ". + main::print_log("[Insteon::Thermo_i2CS] Received Cool Setpoint Change Message ". "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_cool($msg{extra}); } elsif ($msg{command} eq "status_humid" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Humidity Change Message ". + main::print_log("[Insteon::Thermo_i2CS] Received Humidity Change Message ". "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_humid($msg{extra}); } elsif ($msg{command} eq "status_heat" && !$msg{is_ack}){ $self->default_hop_count($msg{maxhops}-$msg{hopsleft}); - main::print_log("[Insteon::Thermo_i2] Received Heat Setpoint Change Message ". + main::print_log("[Insteon::Thermo_i2CS] Received Heat Setpoint Change Message ". "from ". $self->get_object_name) if $main::Debug{insteon}; $self->hex_heat($msg{extra}); } @@ -782,7 +782,7 @@ sub _is_info_request { my $is_info_request; if ($cmd eq 'thermostat_control' && $$self{_control_action} eq "mode") { my $val = $msg{extra}; - main::print_log("[Insteon::Thermo_i2] Processing is_info_request for $cmd with value: $val") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2CS] Processing is_info_request for $cmd with value: $val") if $main::Debug{insteon}; if ($val eq '09') { $self->_mode('Off'); } elsif ($val eq '04') { @@ -797,7 +797,7 @@ sub _is_info_request { $$self{_control_action} = undef; $is_info_request = 1; } - else #This was not a thermo_i2 info_request + else #This was not a thermo_i2CS info_request { #Check if this was a generic info_request $is_info_request = $self->SUPER::_is_info_request($cmd, $ack_setby, %msg); @@ -974,7 +974,7 @@ Sets the high humidity setpoint. =cut sub high_humid_setpoint { my ($self, $value) = @_; - main::print_log("[Insteon::Thermo_i2] Setting high humid setpoint -> $value") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2CS] Setting high humid setpoint -> $value") if $main::Debug{insteon}; if($value !~ /^\d+$/){ main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); return; @@ -993,7 +993,7 @@ Sets the low humidity setpoint. =cut sub low_humid_setpoint { my ($self, $value) = @_; - main::print_log("[Insteon::Thermo_i2] Setting low humid setpoint -> $value") if $main::Debug{insteon}; + main::print_log("[Insteon::Thermo_i2CS] Setting low humid setpoint -> $value") if $main::Debug{insteon}; if($value !~ /^\d+$/){ main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); return; @@ -1007,7 +1007,7 @@ sub low_humid_setpoint { =item C -Retreives and prints the current humidity high and low setpoints. Only available for I2 devices. +Retreives and prints the current humidity high and low setpoints. Only available for I2CS devices. =cut sub get_humid_setpoints{ my ($self) = @_; @@ -1043,7 +1043,7 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received set mode request to " + ::print_log("[Insteon::Thermo_i2CS] Received set mode request to " . $p_state . " for device " . $self->get_object_name); $$self{parent}->mode($p_state); } @@ -1079,7 +1079,7 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received set fan to " + ::print_log("[Insteon::Thermo_i2CS] Received set fan to " . $p_state . " for device " . $self->get_object_name); $$self{parent}->fan($p_state); } @@ -1155,7 +1155,7 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received request to set heat setpoint " + ::print_log("[Insteon::Thermo_i2CS] Received request to set heat setpoint " . $p_state . " for device " . $self->get_object_name); if (lc($p_state) eq 'cooler'){ $$self{parent}->heat_setpoint($$self{parent}->get_heat_sp - 1); @@ -1196,7 +1196,7 @@ sub set { } } if ($found_state){ - ::print_log("[Insteon::Thermo_i2] Received request to set cool setpoint " + ::print_log("[Insteon::Thermo_i2CS] Received request to set cool setpoint " . $p_state . " for device " . $self->get_object_name); if (lc($p_state) eq 'cooler'){ $$self{parent}->cool_setpoint($$self{parent}->get_cool_sp - 1); From cd001a4e2c6849ba1445b1f3b902601413bb2b17 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 27 Aug 2013 17:38:00 -0700 Subject: [PATCH 71/86] Insteon_Thermo: Check Engine Version of Root Device not ALDB Version The ALDB can only be i1 or i2, need to be checking the engine version instead. --- lib/Insteon.pm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 4cd68c02f..c97b2cdee 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -558,24 +558,23 @@ sub check_all_aldb_versions sub check_thermo_versions { - #main::print_log("[Insteon] DEBUG4 Checking thermostat versions") if ($main::Debug{insteon} >= 4); + main::print_log("[Insteon] DEBUG4 Initializing thermostat versions") if ($main::Debug{insteon} >= 4); my @thermo_devices = (); push @thermo_devices, Insteon::find_members("Insteon::Thermostat"); foreach my $thermo_device (@thermo_devices) { if ($thermo_device->isa('Insteon::Thermostat') && - $thermo_device->_aldb->aldb_version() eq "I2CS"){ + $thermo_device->get_root()->engine_version eq "I2CS"){ main::print_log("[Insteon] DEBUG4 Setting thermostat " . $thermo_device->get_object_name() . " to i2CS") if ($main::Debug{insteon} >= 4); bless $thermo_device, 'Insteon::Thermo_i2CS'; $thermo_device->init(); } - elsif ($thermo_device->isa('Insteon::Thermostat') - && $thermo_device->_aldb->aldb_version() eq "I1"){ + else { main::print_log("[Insteon] DEBUG4 Setting thermostat " - . $thermo_device->get_object_name() . " to i2") + . $thermo_device->get_object_name() . " to i1") if ($main::Debug{insteon} >= 4); bless $thermo_device, 'Insteon::Thermo_i1'; } From c3e335028906e00817be310cf7368f11bd6b4517 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 8 Oct 2013 18:44:04 -0700 Subject: [PATCH 72/86] Insteon_Thermo_i2: Decode Status Flag Only Cooling and Heating statuses appear to be supported. Bits 4 and 6 are also always enabled, I don't currently know what these are. Sadly, humidifying and dehumidifying do not appear to be supported. --- lib/Insteon/Thermostat.pm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 5ed8c940c..7d5707c46 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -866,9 +866,19 @@ sub hex_short_temp{ } sub hex_status{ - ### Not sure about this one yet, was 80 when set to auto but no activity - ## need to call _cooling, _heating, _high_humid, and _low_humid when - ## figured out + my ($self, $hex_status) = @_; + # Bit Value Bit Value + # 0 Cooling 4 1?? + # 1 Heating 5 0?? + # 2 0?? 6 1?? + # 3 0?? 7 0?? + # Sadly, dehumidifying and humidifying do not appear to be reported here + my ($pre_cooling, $pre_heating) = ($$self{cooling}, $$self{heating}); + $$self{cooling} = ($hex_status & 0x01) ? 'on' : 'off'; + $$self{heating} = ($hex_status & 0x02) ? 'on' : 'off'; + if (($pre_cooling ne $$self{cooling}) || ($pre_heating ne $$self{heating})){ + $self->set_receive('status_change'); + } } sub hex_heat{ From 101143b7435b274677d49b4fa66847f4fd144ab7 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 8 Oct 2013 18:45:28 -0700 Subject: [PATCH 73/86] Insteon_Thermo_i2: Update status on Humidifying or Dehumdifying --- lib/Insteon/Thermostat.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 7d5707c46..cb2e1d1ec 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -914,6 +914,7 @@ sub _high_humid { my ($self,$p_state) = @_; if ($p_state ne $$self{high_humid}) { $$self{high_humid} = $p_state; + $self->set_receive('status_change'); } return $$self{high_humid}; } @@ -922,6 +923,7 @@ sub _low_humid { my ($self,$p_state) = @_; if ($p_state ne $$self{low_humid}) { $$self{low_humid} = $p_state; + $self->set_receive('status_change'); } return $$self{low_humid}; } From 608d81cc188d43bf1d3f2b0e21f5aad0b1cf8d88 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 8 Oct 2013 19:50:57 -0700 Subject: [PATCH 74/86] Inston_Thermo_i2: Convert Poll_Status to an Internal Sub, Replace with Request_Status This more closely matches what the user would expect --- lib/Insteon/Thermostat.pm | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index cb2e1d1ec..8f863bd5b 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -83,7 +83,7 @@ message are also NOT sent when the humidity setpoints are exceeded. Instead, you must define the heating, high_humdid, and _low_humid groups and link them to MH. (The base group 01 is the cooling group and should always be linked to MH). When linked, these groups will send on/off commands to MH when these events -occur. Alternatively, you can periodically call the poll_simple method to check +occur. Alternatively, you can periodically call request_status() to check the status of these attributes. Linking: @@ -621,16 +621,21 @@ sub sync_links{ return $self->SUPER::sync_links($audit_mode, $callback, $failure_callback); } -=item C +=item C<_poll_simple()> Requests the status of all Thermostat data points (temp, fan, mode ...) in a single -request. Only available for I2CS devices. +request. Called by C, you likely don't need to call this directly +Only available for I2CS devices. + =cut -sub poll_simple{ - my ($self) = @_; + +sub _poll_simple{ + my ($self, $success_callback, $failure_callback) = @_; my $extra = "020000000000000000000000000000"; my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); $$message{add_crc16} = 1; + $message->failure_callback($failure_callback); + $message->success_callback($success_callback); $self->_send_cmd($message); } @@ -976,7 +981,7 @@ sub sync_time { #points such as mode and what not becuase we can't just set the time without #setting these variables too. $$self{sync_time} = 1; - $self->poll_simple(); + $self->_poll_simple(); } =item C From 3de1e9d86982c55cbc9ea76526604adeb645021e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 8 Oct 2013 19:52:24 -0700 Subject: [PATCH 75/86] Insteon_Thermo_i2: Add Request_status Routine --- lib/Insteon/Thermostat.pm | 68 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 8f863bd5b..4c50a2f37 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -658,6 +658,57 @@ sub get_status() { return $output; } +=item C + +Prints the currently known status to the log as a text string. + +=cut +sub print_status() { + my ($self) = @_; + my $root = $self->get_root(); + my $output = "[Insteon:Thermo_i2CaS] The status of " . $root->get_object_name . " is:\n"; + $output .= "Mode: "; + $output .= $root->get_mode(); + $output .= "; Status: "; + $output .= "Heating, " if ($$root{heating} eq 'on'); + $output .= "Cooling, " if ($$root{cooling} eq 'on'); + $output .= "Dehumidifying, " if ($$root{high_humid} eq 'on'); + $output .= "Humidifying" if ($$root{low_humid} eq 'on'); + $output .= 'Off' if ($output eq ''); + $output .= "; Temp: "; + $output .= $root->get_temp(); + $output .= "; Humid: "; + $output .= $root->get_humid(); + $output .= "; Heat SP: "; + $output .= $root->get_heat_sp(); + $output .= "; Cool SP: "; + $output .= $root->get_cool_sp(); + $output .= "; High Humid SP: "; + $output .= $root->_high_humid(); + $output .= "; Low Humid SP: "; + $output .= $root->_low_humid(); + ::print_log($output); +} + +=item C + +Returns the current humidity at the thermostat. +=cut +sub get_humid() { + my ($self) = @_; + return $$self{'humid'}; +} + +sub request_status { + my ($self) = @_; + $self = $self->get_root(); + my $self_name = $self->get_object_name; + my $failure_callback = "::print_log('[Insteon:Thermo_i2CS] ERROR: Failed to get status for $self_name.');"; + my $print_callback = $self_name . "->print_status"; + my $humid_callback = $self_name . "->_poll_humid_setpoints(\'$print_callback\', \"$failure_callback\")"; + $self->_poll_simple($humid_callback, $failure_callback); +} + sub _process_message { my ($self,$p_setby,%msg) = @_; my $clear_message = 0; @@ -733,11 +784,8 @@ sub _process_message { #10 = humid high #24 = 1 = Status Report Enabled #12 = firmware #26 = 1 = External Power On #28 = 1 = Int, 2=Ext Temp - main::print_log("[Insteon::Thermo_i2CS] Humidity setpoints for ". - $self->get_object_name . " are High: " . - $self->_high_humid(hex(substr($msg{extra}, 8, 2))) . - " Low: " . $self->_low_humid(hex(substr($msg{extra}, 10, 2))) - ) if $main::Debug{insteon}; + $self->_high_humid(hex(substr($msg{extra}, 8, 2))); + $self->_low_humid(hex(substr($msg{extra}, 10, 2))); $clear_message = 1; $self->_process_command_stack(%msg); } @@ -1022,16 +1070,20 @@ sub low_humid_setpoint { $self->_send_cmd($message); } -=item C +=item C<_poll_humid_setpoints()> Retreives and prints the current humidity high and low setpoints. Only available for I2CS devices. + =cut -sub get_humid_setpoints{ - my ($self) = @_; + +sub _poll_humid_setpoints{ + my ($self, $success_callback, $failure_callback) = @_; my $extra = "00000001"; $extra .= '0' x (30 - length $extra); my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); $$self{_ext_set_get_action} = 'get'; + $message->failure_callback($failure_callback); + $message->success_callback($success_callback); $self->_send_cmd($message); } From a970d64da40c8da0f015ffb779e857effe021bfe Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 76/86] Insteon_Thermo_i2: Fix Typos in Comments --- lib/Insteon/Thermostat.pm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 4c50a2f37..61ab1dc21 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -80,7 +80,7 @@ the EF group as described above and run sync links. Broadcast messages are NOT sent when the heater turns on/off. Broadcast message are also NOT sent when the humidity setpoints are exceeded. Instead, -you must define the heating, high_humdid, and _low_humid groups and link them +you must define the heating, high_humid, and low_humid groups and link them to MH. (The base group 01 is the cooling group and should always be linked to MH). When linked, these groups will send on/off commands to MH when these events occur. Alternatively, you can periodically call request_status() to check @@ -116,17 +116,20 @@ thermostat can be created in user code: $thermo_setpoint_c = new Insteon::Thermo_setpoint_c($thermostat); $thermo_humidity = new Insteon::Thermo_humidity($thermostat); #Only available on i2CS devices $thermo_status = new Insteon::Thermo_status($thermostat); #Only available on i2CS devices + $thermo_humidity_setpoint_h = new Insteon::Thermo_setpoint_humid_h($thermostat); #Only available on i2CS devices + $thermo_humidity_setpoint_l = new Insteon::Thermo_setpoint_humid_l($thermostat); #Only available on i2CS devices + where $thermostat is the parent object to track. The state of these child objects -will be the state of the various objects. This makes the display of the various -states easier within MH. The child objects also make it easier to change the -various states on the thermostat. +will be the state of the various attributes of the thermostat. This makes the +display of the various states easier within MH. The child objects also make it +easier to change the various states on the thermostat. see code/examples/Insteon_thermostat.pl for more. =head1 BUGS -This code has not been tested on older Venstar thermsotats, however it is believed +This code has not been tested on older Venstar thermostats, however it is believed that the basic functionality should work as it did in the old code. =head1 AUTHOR From 3fa31ea29413d6046ff9f055f0573c7758690cb1 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 77/86] Insteon_thermo_i2: Add Humidity Setpoint Child Objects --- lib/Insteon/Thermostat.pm | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 61ab1dc21..58167fa9d 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -469,6 +469,12 @@ sub parent_event { elsif ($p_state eq 'status_change'){ $$self{child_status}->set_receive($self->get_status(), $self); } + elsif ($p_state eq 'low_humid_setpoint_change'){ + $$self{child_setpoint_humid_l}->set_receive($self->get_low_humid_sp(), $self); + } + elsif ($p_state eq 'high_humid_setpoint_change'){ + $$self{child_setpoint_humid_h}->set_receive($self->get_high_humid_sp(), $self); + } } # Overload methods we don't use, but would otherwise cause Insteon traffic. @@ -1304,6 +1310,88 @@ sub set_receive { $self->SUPER::set($p_state); } +package Insteon::Thermo_setpoint_humid_h; +use strict; + +@Insteon::Thermo_setpoint_humid_h::ISA = ('Generic_Item'); + +sub new { + my ($class, $parent) = @_; + my $self = new Generic_Item(); + bless $self, $class; + $$self{parent} = $parent; + @{$$self{states}} = ('Lower' , 'Higher'); + $$self{parent}{child_setpoint_humid_h} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "high_humid_setpoint_change"); + return $self; +} + +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2CS] Received request to set high humidity setpoint to " + . $p_state . " for device " . $self->get_object_name); + if (lc($p_state) eq 'lower'){ + $$self{parent}->high_humid_setpoint($$self{parent}->get_high_humid_sp - 1); + } + elsif (lc($p_state) eq 'higher'){ + $$self{parent}->high_humid_setpoint($$self{parent}->get_high_humid_sp + 1); + } + } +} + +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} + +package Insteon::Thermo_setpoint_humid_l; +use strict; + +@Insteon::Thermo_setpoint_humid_l::ISA = ('Generic_Item'); + +sub new { + my ($class, $parent) = @_; + my $self = new Generic_Item(); + bless $self, $class; + $$self{parent} = $parent; + @{$$self{states}} = ('Lower', 'Higher'); + $$self{parent}{child_setpoint_humid_l} = $self; + $$self{parent} -> tie_event ('$object->parent_event("$state")', "low_humid_setpoint_change"); + return $self; +} + +sub set { + my ($self, $p_state, $p_setby, $p_response) = @_; + my $found_state = 0; + foreach my $test_state (@{$$self{states}}){ + if (lc($test_state) eq lc($p_state)){ + $found_state = 1; + } + } + if ($found_state){ + ::print_log("[Insteon::Thermo_i2CS] Received request to set low humidity setpoint to " + . $p_state . " for device " . $self->get_object_name); + if (lc($p_state) eq 'lower'){ + $$self{parent}->low_humid_setpoint($$self{parent}->get_low_humid_sp - 1); + } + elsif (lc($p_state) eq 'higher'){ + $$self{parent}->low_humid_setpoint($$self{parent}->get_low_humid_sp + 1); + } + } +} + +sub set_receive { + my ($self, $p_state) = @_; + $self->SUPER::set($p_state); +} + 1; =back From ba1831fc3c58c9b924e01a5d1fd507436b4a1396 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 78/86] Insteon_Thermo_i2: Change Name of (De)Humidifying Functions to Match Nomenclature Heating, Cooling, ... should be humidifying and the like --- lib/Insteon/Thermostat.pm | 47 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 58167fa9d..34d7200b3 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -584,7 +584,7 @@ sub init { my ($self) = @_; $$self{message_types} = \%message_types; #Set saved state unique to i2CS devices - $self->restore_data('humid', 'cooling', 'heating', 'high_humid', 'low_humid'); + $self->restore_data('humid', 'cooling', 'heating', 'humidifying', 'dehumidifying', 'high_humid_sp', 'low_humid_sp'); } sub set { @@ -605,10 +605,10 @@ sub set { $root->_heating($link_state); } elsif ($self->group eq '03') { - $root->_high_humid($link_state); + $root->_dehumidifying($link_state); } elsif ($self->group eq '04') { - $root->_low_humid($link_state); + $root->_humidifying($link_state); } #Update the status of linked devices $self->set_linked_devices($link_state); @@ -661,8 +661,8 @@ sub get_status() { my $output = ""; $output .= "Heating, " if ($$root{heating} eq 'on'); $output .= "Cooling, " if ($$root{cooling} eq 'on'); - $output .= "Dehumidifying, " if ($$root{high_humid} eq 'on'); - $output .= "Humidifying" if ($$root{low_humid} eq 'on'); + $output .= "Dehumidifying, " if ($$root{dehumidifying} eq 'on'); + $output .= "Humidifying" if ($$root{humidifying} eq 'on'); $output = 'Off' if ($output eq ''); return $output; } @@ -679,11 +679,13 @@ sub print_status() { $output .= "Mode: "; $output .= $root->get_mode(); $output .= "; Status: "; - $output .= "Heating, " if ($$root{heating} eq 'on'); - $output .= "Cooling, " if ($$root{cooling} eq 'on'); - $output .= "Dehumidifying, " if ($$root{high_humid} eq 'on'); - $output .= "Humidifying" if ($$root{low_humid} eq 'on'); - $output .= 'Off' if ($output eq ''); + my $output_status = ''; + $output_status .= "Heating, " if ($$root{heating} eq 'on'); + $output_status .= "Cooling, " if ($$root{cooling} eq 'on'); + $output_status .= "Dehumidifying, " if ($$root{dehumidifying} eq 'on'); + $output_status .= "Humidifying" if ($$root{humidifying} eq 'on'); + $output_status .= 'Off' if ($output_status eq ''); + $output .= $output_status; $output .= "; Temp: "; $output .= $root->get_temp(); $output .= "; Humid: "; @@ -972,22 +974,31 @@ sub _heating { } -sub _high_humid { +sub _dehumidifying { my ($self,$p_state) = @_; - if ($p_state ne $$self{high_humid}) { - $$self{high_humid} = $p_state; + if ($p_state ne $$self{dehumidifying}) { + $$self{dehumidifying} = $p_state; $self->set_receive('status_change'); } - return $$self{high_humid}; + return $$self{dehumidifying}; } -sub _low_humid { +sub _humidifying { my ($self,$p_state) = @_; - if ($p_state ne $$self{low_humid}) { - $$self{low_humid} = $p_state; + if ($p_state ne $$self{humidifying}) { + $$self{humidifying} = $p_state; $self->set_receive('status_change'); } - return $$self{low_humid}; + return $$self{humidifying}; +} + +sub _high_humid_sp { + my ($self,$p_state) = @_; + if ($p_state ne $$self{high_humid_sp}) { + $$self{high_humid_sp} = $p_state; + $self->set_receive('high_humid_setpoint_change'); + } + return $$self{high_humid_sp}; } =item C From a7d18a21fbe8acf5deb79c9a1a24499efe0a7518 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 79/86] Insteon_Thermo_i2: Use Simple_Message Whenever Possible --- lib/Insteon/Thermostat.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 34d7200b3..4f6457d55 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -621,8 +621,8 @@ sub sync_links{ if (!$audit_mode && ref $bcast_obj && $self->is_root){ #Make sure thermostat is set to broadcast changes ::print_log("[Insteon::Thermo_i2CS] (sync_links) Enabling thermostat broadcast setting.") unless $audit_mode; - my $extra = "000008000000000000000000000000"; - my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + my $extra = "000008"; + my $message = $self->simple_message('extended_set_get', $extra); $$self{_ext_set_get_action} = 'set'; $self->_send_cmd($message); } @@ -640,8 +640,8 @@ Only available for I2CS devices. sub _poll_simple{ my ($self, $success_callback, $failure_callback) = @_; - my $extra = "020000000000000000000000000000"; - my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); + my $extra = "02"; + my $message = $self->simple_message('extended_set_get', $extra); $$message{add_crc16} = 1; $message->failure_callback($failure_callback); $message->success_callback($success_callback); From 3aa224a5c7d7e1fe3de3e7f7aaa9c53acb2838e9 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 80/86] Insteon_Thermo_i2: Fix Typo in Debug Log Entry --- lib/Insteon/Thermostat.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 4f6457d55..56ab41bd6 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -675,7 +675,7 @@ Prints the currently known status to the log as a text string. sub print_status() { my ($self) = @_; my $root = $self->get_root(); - my $output = "[Insteon:Thermo_i2CaS] The status of " . $root->get_object_name . " is:\n"; + my $output = "[Insteon:Thermo_i2CS] The status of " . $root->get_object_name . " is:\n"; $output .= "Mode: "; $output .= $root->get_mode(); $output .= "; Status: "; From 0680d3914b8b80fa555afe5f0755808a69a57917 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 81/86] Insteon_Thermo_i2: Create Get_Humid_Setpoint Functions --- lib/Insteon/Thermostat.pm | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 56ab41bd6..5c150f71f 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -695,9 +695,9 @@ sub print_status() { $output .= "; Cool SP: "; $output .= $root->get_cool_sp(); $output .= "; High Humid SP: "; - $output .= $root->_high_humid(); + $output .= $root->get_high_humid_sp(); $output .= "; Low Humid SP: "; - $output .= $root->_low_humid(); + $output .= $root->get_low_humid_sp(); ::print_log($output); } @@ -1001,6 +1001,34 @@ sub _high_humid_sp { return $$self{high_humid_sp}; } +sub _low_humid_sp { + my ($self,$p_state) = @_; + if ($p_state ne $$self{low_humid_sp}) { + $$self{low_humid_sp} = $p_state; + $self->set_receive('low_humid_setpoint_change'); + } + return $$self{low_humid_sp}; +} + +=item C + +Returns the current high humidity setpoint. +=cut +sub get_high_humid_sp { + my ($self) = @_; + return $$self{high_humid_sp}; +} + +=item C + +Returns the current low humidity setpoint. +=cut +sub get_low_humid_sp { + my ($self) = @_; + return $$self{low_humid_sp}; +} + + =item C Sets system mode to argument: 'off', 'heat', 'cool', 'auto', 'program_heat', From 0e48368743813ae3d6be165264656d9eaa3f9224 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 82/86] Insteon_Thermo_i2: Manually Set (De)Humidifying States on Request Status There does not appear to be any request which can be made to the device to get the status of the Humidifying or Dehumidifying group (perhaps a direct request to each device->group would work) But in order to make sure that the two groups are in the proper state after a call to request_status we just manually set them inside the code --- lib/Insteon/Thermostat.pm | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 5c150f71f..fdaf83549 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -795,8 +795,23 @@ sub _process_message { #10 = humid high #24 = 1 = Status Report Enabled #12 = firmware #26 = 1 = External Power On #28 = 1 = Int, 2=Ext Temp - $self->_high_humid(hex(substr($msg{extra}, 8, 2))); - $self->_low_humid(hex(substr($msg{extra}, 10, 2))); + $self->_high_humid_sp(hex(substr($msg{extra}, 8, 2))); + $self->_low_humid_sp(hex(substr($msg{extra}, 10, 2))); + + #Humidifying and Dehumidifying are only reported by the + #thermostat as scene-commands. When a user calls + #request_status, we manually check the values and update + #as appropriate + if ($self->get_high_humid_sp > $self->get_humid){ + $self->_dehumidifying('off'); + } else { + $self->_dehumidifying('on'); + } + if ($self->get_low_humid_sp < $self->get_humid){ + $self->_humidifying('off'); + } else { + $self->_humidifying('on'); + } $clear_message = 1; $self->_process_command_stack(%msg); } From 95597cbe160ef4683eb8442f9889dfdecaf369b5 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 83/86] Insteon_Thermo_i2: Set Humidity Setpoint Pending States on Extended Message ACK --- lib/Insteon/Thermostat.pm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index fdaf83549..296ad1ad0 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -740,6 +740,24 @@ sub _process_message { $$self{_ext_set_get_action} = undef; $self->_process_command_stack(%msg); } + elsif ($$self{_ext_set_get_action} eq 'set_high_humid'){ + main::print_log("[Insteon::Thermostat] Received ACK of high humid setpoint ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->_high_humid_sp($$self{_high_humid_pending}); + $clear_message = 1; + $$self{_ext_set_get_action} = undef; + $$self{_high_humid_pending} = undef; + $self->_process_command_stack(%msg); + } + elsif ($$self{_ext_set_get_action} eq 'set_low_humid'){ + main::print_log("[Insteon::Thermostat] Received ACK of low humid setpoint ". + "for ". $self->get_object_name) if $main::Debug{insteon}; + $self->_low_humid_sp($$self{_low_humid_pending}); + $clear_message = 1; + $$self{_ext_set_get_action} = undef; + $$self{_low_humid_pending} = undef; + $self->_process_command_stack(%msg); + } } elsif ($msg{command} eq "extended_set_get" && $msg{is_extended}) { if (substr($msg{extra},0,4) eq "0201") { From 47aeab1e398033d358d19af9ee8528133edaded8 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 17:37:00 -0700 Subject: [PATCH 84/86] Insteon_Thermo_i2: Humidity Setpoints must be in range of [1-99], Set Pending Flag --- lib/Insteon/Thermostat.pm | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 296ad1ad0..8c9109817 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -1122,13 +1122,18 @@ sub high_humid_setpoint { my ($self, $value) = @_; main::print_log("[Insteon::Thermo_i2CS] Setting high humid setpoint -> $value") if $main::Debug{insteon}; if($value !~ /^\d+$/){ - main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); + main::print_log("[Insteon::Thermo_i2CS] ERROR: Setpoint $value not numeric"); return; } + if($value > 99 || $value < 1){ + main::print_log("[Insteon::Thermo_i2CS] ERROR: Setpoint must be between 1-99, not $value"); + return; + } my $extra = "00000B" . sprintf("%02x", $value); $extra .= '0' x (30 - length $extra); my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); - $$self{_ext_set_get_action} = 'set'; + $$self{_ext_set_get_action} = 'set_high_humid'; + $$self{_high_humid_pending} = $value; $self->_send_cmd($message); } @@ -1141,13 +1146,18 @@ sub low_humid_setpoint { my ($self, $value) = @_; main::print_log("[Insteon::Thermo_i2CS] Setting low humid setpoint -> $value") if $main::Debug{insteon}; if($value !~ /^\d+$/){ - main::print_log("[Insteon::Thermostat] ERROR: Setpoint $value not numeric"); + main::print_log("[Insteon::Thermo_i2CS] ERROR: Setpoint $value not numeric"); return; } + if($value > 99 || $value < 1){ + main::print_log("[Insteon::Thermo_i2CS] ERROR: Setpoint must be between 1-99, not $value"); + return; + } my $extra = "00000C" . sprintf("%02x", $value); $extra .= '0' x (30 - length $extra); my $message = new Insteon::InsteonMessage('insteon_ext_send', $self, 'extended_set_get', $extra); - $$self{_ext_set_get_action} = 'set'; + $$self{_ext_set_get_action} = 'set_low_humid'; + $$self{_low_humid_pending} = $value; $self->_send_cmd($message); } From 0098ae6d48219175bbdb51dddc8c58ccbbc2a88a Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 9 Oct 2013 18:47:40 -0700 Subject: [PATCH 85/86] Insteon_Thermo_i2: Remove Extra Space --- lib/Insteon_PLM.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Insteon_PLM.pm b/lib/Insteon_PLM.pm index 70688b02b..dd6315f74 100644 --- a/lib/Insteon_PLM.pm +++ b/lib/Insteon_PLM.pm @@ -402,7 +402,6 @@ sub _send_cmd { $self->_set_timeout('command', $cmd_timeout); # a commmand needs to be PLM ack'd w/i 3 seconds or it gets dropped } } - my $is_extended = ($message->can('command_type') && $message->command_type eq "insteon_ext_send") ? 1 : 0; if (length($command) != (Insteon::MessageDecoder::insteon_cmd_len(substr($command,0,4), 0, $is_extended)*2)){ &::print_log( "[Insteon_PLM]: ERROR!! Command sent to PLM " . lc($command) From ae81eaa1e4e382df78833ca919244851c0f2bf1e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 10 Oct 2013 18:53:38 -0700 Subject: [PATCH 86/86] Insteon_Thermostat: Add Voice Command for Sync Time Generate Voice Commands needed to be called after the thermo version is set --- lib/Insteon.pm | 2 +- lib/Insteon/Thermostat.pm | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Insteon.pm b/lib/Insteon.pm index 67c3d5610..9038ff491 100755 --- a/lib/Insteon.pm +++ b/lib/Insteon.pm @@ -1193,8 +1193,8 @@ sub _active_interface &main::Reload_post_add_hook(\&Insteon::BaseInterface::poll_all, 1); $init_complete = 0; &main::MainLoop_pre_add_hook(\&Insteon::init, 1); - &main::Reload_post_add_hook(\&Insteon::generate_voice_commands, 1); &main::Reload_post_add_hook(\&Insteon::check_thermo_versions, 1); + &main::Reload_post_add_hook(\&Insteon::generate_voice_commands, 1); } $$self{active_interface} = $interface if $interface; return $$self{active_interface}; diff --git a/lib/Insteon/Thermostat.pm b/lib/Insteon/Thermostat.pm index 8c9109817..f224d0484 100755 --- a/lib/Insteon/Thermostat.pm +++ b/lib/Insteon/Thermostat.pm @@ -1178,6 +1178,30 @@ sub _poll_humid_setpoints{ $self->_send_cmd($message); } +=item C + +Returns a hash of voice commands where the key is the voice command name and the +value is the perl code to run when the voice command name is called. + +Higher classes which inherit this object may add to this list of voice commands by +redefining this routine while inheriting this routine using the SUPER function. + +This routine is called by L to generate the +necessary voice commands. + +=cut + +sub get_voice_cmds +{ + my ($self) = @_; + my $object_name = $self->get_object_name; + my %voice_cmds = ( + %{$self->SUPER::get_voice_cmds}, + 'sync time' => "$object_name->sync_time()" + ); + return \%voice_cmds; +} + package Insteon::Thermo_mode; use strict;