From f868c298a4a8c270b6042af45238f218637d2d6d Mon Sep 17 00:00:00 2001 From: H Plato Date: Sun, 18 Sep 2016 20:07:27 -0600 Subject: [PATCH 01/14] Steve's merged in changes, plus some additions for 4.08. Humidity workaround still todo --- lib/Venstar_Colortouch.pm | 142 +++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 62 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 60c2e7c2d..b0e80a1e2 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -21,10 +21,15 @@ use Data::Dumper; # v1.1 - added in schedule and humidity control. # v1.2 - added communication tracker object & timeout control # v1.3 - added check for timer defined +# v1.4 - support for API v5. It seems like v5 +# "hum_setpoint": is the current humidity and +# "dehum_setpoint": is the humidity setpoint +# "hum" doesn't return anything anymore. # Notes: # - Best to use firmware at least 3.14 released Nov 2014. This fixes issues with both # schedule and humidity/dehumidify control. +# - 4.08 is API5, seems to have better wifi, but humidity control is messed up. #todo # - temp setpoint bounds checking @@ -55,6 +60,7 @@ sub new { my $self = {}; bless $self, $class; $self->{data} = undef; + $self->{api_ver} = 0; $self->{child_object} = undef; $self->{config}->{cache_time} = 30; #TODO fix cache timeouts $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} @@ -68,7 +74,7 @@ sub new { $self->{updating} = 0; $self->{data}->{retry} = 0; $self->{host} = $host; - $self->{debug} = 0; + $self->{debug} = 5; $self->{loglevel} = 1; $self->{status} = ""; $self->{timeout} = 15; #300; @@ -106,11 +112,11 @@ sub _init { my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api'); if ($isSuccessResponse1) { - - if ( ( $stat->{api_ver} > 3 ) and ( $stat->{type} eq "residential" ) ) { + $self->{api_ver} = $stat->{api_ver}; + if ( ( $self->{api_ver} > 3 ) and ( $stat->{type} eq "residential" ) ) { main::print_log( - "[Venstar Colortouch] Residental Venstar ColorTouch found with api level $stat->{api_ver}" + "[Venstar Colortouch] Residental Venstar ColorTouch found with api level $self->{api_ver}" ); if ( $self->poll() ) { main::print_log( "[Venstar Colortouch:" @@ -305,7 +311,8 @@ sub _push_JSON_data { if ( $type eq 'settings' ) { #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 - + # with v4.08 firmware, hum and dehum setpoints need to be at least 4 % points different + my ( $newunits, $newaway, $newsched, $newhumsp, $newdehumsp ); my ($units) = $params =~ /tempunits=(\d+)/; my ($away) = $params =~ /away=(\d+)/; @@ -314,7 +321,7 @@ sub _push_JSON_data { my ($humsp) = $params =~ /hum_setpoint=(\d+)/; my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/ ; #need to add in dehumidifier stuff at some point - + my $humidity_change = 0; my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; @@ -324,10 +331,10 @@ sub _push_JSON_data { $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); $away = $caway if ( not defined $away ); - $sched = $csched if ( not defined $sched ); + $sched = $csched if ( not defined $sched ); $hum = $chum if ( not defined $hum ); $humsp = $chumsp if ( not defined $humsp ); - $dehumsp = $cdehumsp if ( not defined $dehumsp ); + $dehumsp = $cdehumsp if ( not defined $dehumsp ); if ( $cunits ne $units ) { main::print_log( "[Venstar Colortouch:" @@ -362,8 +369,11 @@ sub _push_JSON_data { if ( $chumsp ne $humsp ) { print - "[Venstar Colortouch] Changing Humidity Setpoint from $chumsp to $humsp\n"; + "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Humidity Setpoint from $chumsp to $humsp\n"; $newhumsp = $humsp; + $humidity_change = 1; } else { $newhumsp = $chumsp; @@ -371,17 +381,25 @@ sub _push_JSON_data { if ( $cdehumsp ne $dehumsp ) { print - "[Venstar Colortouch] Changing Dehumidity Setpoint from $cdehumsp to $dehumsp\n"; + "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing (De)humidity Setpoint from $cdehumsp to $dehumsp\n"; $newdehumsp = $dehumsp; } else { $newdehumsp = $cdehumsp; } - $cmd = - "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; - main::print_log( "Sending Settings command $cmd to " . $self->{host} ) - if $self->{debug}; + # v4.08 needs 6 % point delta on humidity and dehumidity. + # if humidify is more then dehumidify, then set dehumidify to 6 + humidity + $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); + if ($self->{api_ver} >=5) { + $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched"; #&dehum_setpoint=$newhumsp&hum_setpoint=" . ($newhumsp + 1); + $cmd = "hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp" if ($humidity_change); + } else { + $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; + } + main::print_log( "Sending Settings command [$cmd] to " . $self->{host} ) if $self->{debug}; } elsif ( $type eq 'control' ) { @@ -485,8 +503,7 @@ sub _push_JSON_data { $cmd = "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; - main::print_log( "Sending Control command $cmd to " . $self->{host} ) - ; # if $self->{debug}; + main::print_log( "Sending Control command $cmd to " . $self->{host} ) if $self->{debug}; } else { @@ -653,7 +670,7 @@ sub print_info { . "] Device " . $self->{data}->{name} . " is " . $type - . " Thermostat" ); + . " Thermostat with API level " . $self->{api_ver} ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -700,7 +717,7 @@ sub print_info { . $self->{data}->{name} . "] Current Humidity:" . $self->{data}->{info}->{hum} - . "%" ); + . "%" ) if ($self->{api_ver} < 5); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -723,18 +740,20 @@ sub print_info { . "$unit\t" . $self->{data}->{info}->{cooltempmax} . "$unit" ); + my $hum_value = $self->{data}->{info}->{hum_setpoint}; + $hum_value = $self->{data}->{info}->{dehum_setpoint} if ($self->{api_ver} >= 5); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidity:\t" - . $self->{data}->{info}->{hum_setpoint} + . $hum_value . "%" ) - unless ( $self->{data}->{info}->{hum_setpoint} == 99 ); + unless ( $hum_value == 99 ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Dehumidity:" . $self->{data}->{info}->{dehum_setpoint} . "%" ) - unless ( $self->{data}->{info}->{dehum_setpoint} == 99 ); + unless (( $self->{data}->{info}->{dehum_setpoint} == 99 ) or ($self->{api_ver} >= 5)); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -839,7 +858,7 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{fan} = $self->{data}->{info}->{fan}; if ( defined $self->{child_object}->{fanstate} ) { - main::print_log "Child object found. Updating..." + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Fan Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{fanstate} ->set_mode( $fan[ $self->{data}->{info}->{fan} ] ); @@ -856,7 +875,7 @@ sub process_data { $self->{previous}->{info}->{fanstate} = $self->{data}->{info}->{fanstate}; if ( defined $self->{child_object}->{fanstate} ) { - main::print_log "Child object found. Updating..." + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Fanstate Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{fanstate} ->set( $fanstate[ $self->{data}->{info}->{fanstate} ], 'poll' ); @@ -898,7 +917,7 @@ sub process_data { $self->{previous}->{info}->{schedule} = $self->{data}->{info}->{schedule}; if ( defined $self->{child_object}->{sched} ) { - main::print_log "Child object found. Updating..." + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Schedule Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{sched} ->set( $sched[ $self->{data}->{info}->{schedule} ], 'poll' ); @@ -937,7 +956,7 @@ sub process_data { $self->{previous}->{info}->{spacetemp} = $self->{data}->{info}->{spacetemp}; if ( defined $self->{child_object}->{temp} ) { - main::print_log "Child object found. Updating..." + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Temperature Sensor Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{temp} ->set( $self->{data}->{info}->{spacetemp}, 'poll' ); @@ -1010,44 +1029,35 @@ sub process_data { $self->{data}->{info}->{cooltempmax}; } - if ( $self->{previous}->{info}->{dehum_setpoint} != - $self->{data}->{info}->{dehum_setpoint} ) - { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Thermostat dehumidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}" - ) if ( $self->{loglevel} ); - $self->{previous}->{info}->{dehum_setpoint} = - $self->{data}->{info}->{dehum_setpoint}; - if ( defined $self->{child_object}->{dehum_sp} ) { - main::print_log "Child object found. Updating..." - if ( $self->{loglevel} ); - $self->{child_object}->{dehum_sp} - ->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); + if ( $self->{previous}->{info}->{dehum_setpoint} != $self->{data}->{info}->{dehum_setpoint} ) { + if ($self->{api_ver} >=5) { #v5, dehum_setpoint is now the humidity setpoint? + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}") if ( $self->{loglevel} ); + $self->{previous}->{info}->{dehum_setpoint} = $self->{data}->{info}->{dehum_setpoint}; + if ( defined $self->{child_object}->{hum_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidify Child object found. Updating..." if ( $self->{loglevel} ); + $self->{child_object}->{hum_sp}->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); + } + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat dehumidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}") if ( $self->{loglevel} ); + $self->{previous}->{info}->{dehum_setpoint} = $self->{data}->{info}->{dehum_setpoint}; + if ( defined $self->{child_object}->{dehum_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Dehumidify Child object found. Updating..." if ( $self->{loglevel} ); + $self->{child_object}->{dehum_sp}->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); + } } } - if ( $self->{previous}->{info}->{hum_setpoint} != - $self->{data}->{info}->{hum_setpoint} ) - { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{hum_setpoint} to $self->{data}->{info}->{hum_setpoint}" - ) if ( $self->{loglevel} ); - $self->{previous}->{info}->{hum_setpoint} = - $self->{data}->{info}->{hum_setpoint}; - if ( defined $self->{child_object}->{hum_sp} ) { - main::print_log "Child object found. Updating..." - if ( $self->{loglevel} ); - $self->{child_object}->{hum_sp} - ->set( $self->{data}->{info}->{hum_setpoint}, 'poll' ); - } + if ( $self->{previous}->{info}->{hum_setpoint} != $self->{data}->{info}->{hum_setpoint} ) { + if ($self->{api_ver} < 5) { #v5, hum_setpoint is now the humidity sensor? + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{hum_setpoint} to $self->{data}->{info}->{hum_setpoint}") if ( $self->{loglevel} ); + $self->{previous}->{info}->{hum_setpoint} = $self->{data}->{info}->{hum_setpoint}; + if ( defined $self->{child_object}->{hum_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidify Child object found. Updating..." if ( $self->{loglevel} ); + $self->{child_object}->{hum_sp} ->set( $self->{data}->{info}->{hum_setpoint}, 'poll' ); + } + } } - #if ($self->{previous}->{info}->{hum} != $self->{data}->{info}->{hum}) { - # main::print_log("[Venstar Colortouch:". $self->{data}->{name} . "] Thermostat humidity changed from $self->{previous}->{info}->{hum} to $self->{data}->{info}->{hum}") if ($self->{loglevel}); - # $self->{previous}->{info}->{hum} = $self->{data}->{info}->{hum}; - #} if ( $self->{previous}->{info}->{setpointdelta} != $self->{data}->{info}->{setpointdelta} ) @@ -1070,7 +1080,7 @@ sub process_data { $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; if ( defined $self->{child_object}->{hum} ) { - main::print_log "Child object found. Updating..." + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidity Sensor Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{hum} ->set( $self->{data}->{sensors}->{sensors}[0]->{hum}, 'poll' ); @@ -1108,6 +1118,12 @@ sub print_runtimes { #------------ # User access methods +sub get_apiver { + my ($self) = @_; + + return ($self->{api_ver}); +} + sub get_mode { my ($self) = @_; my @mode; @@ -1181,11 +1197,14 @@ sub get_sp_cool { sub get_sp_hum { my ($self) = @_; - return ( $self->{data}->{info}->{hum_setpoint} ); + my $value = $self->{data}->{info}->{hum_setpoint}; + $value = $self->{data}->{info}->{dehum_setpoint} if ($self->{api_ver} >= 5); + return ( $value ); } sub get_sp_dehum { my ($self) = @_; + main::print_log("[Venstar_Colortouch]: WARNING, api v5 humidity settings are questionable.") if ($self->{api_ver} >= 5); return ( $self->{data}->{info}->{dehum_setpoint} ); } @@ -1462,6 +1481,7 @@ sub set_mode { sub set_away { + #I don't use this. Shouldn't just be this simple though. #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); } @@ -1506,8 +1526,6 @@ sub set_fan { sub set_units { - #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); - } sub set { From c413d4cb843bbb24ec305499fd9f6afd8d62320c Mon Sep 17 00:00:00 2001 From: Pmatis Date: Mon, 19 Sep 2016 10:08:06 -0400 Subject: [PATCH 02/14] Typo correction for examples --- lib/Venstar_Colortouch.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index b0e80a1e2..6aebcbe1e 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -15,7 +15,7 @@ use Data::Dumper; # $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); # $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); # $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); -# $stat_upper_sched = new Venstar_Colortouch_Sched($stat_upper); +# $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); # $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); # v1.1 - added in schedule and humidity control. From 81f903529cf2131c93dc9344c984c0f9f6047ca3 Mon Sep 17 00:00:00 2001 From: Pmatis Date: Mon, 19 Sep 2016 10:18:33 -0400 Subject: [PATCH 03/14] Add mode, heat, cool setpoint child objects --- lib/Venstar_Colortouch.pm | 129 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 6aebcbe1e..dbfaf6fc7 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -11,7 +11,10 @@ use Data::Dumper; # Venstar::Colortouch # $stat_upper = new Venstar_Colortouch('192.168.0.100'); # +# $stat_upper_mode = new Venstar_Colortouch_Mode($stat_upper); # $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); +# $stat_upper_heat_sp = new Venstar_Colortouch_Heat_sp($stat_upper); +# $stat_upper_cool_sp = new Venstar_Colortouch_Cool_sp($stat_upper); # $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); # $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); # $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); @@ -888,6 +891,12 @@ sub process_data { . "] Mode changed from $mode[$self->{previous}->{info}->{mode}] to $mode[$self->{data}->{info}->{mode}]" ) if ( $self->{loglevel} ); $self->{previous}->{info}->{mode} = $self->{data}->{info}->{mode}; + if ( defined $self->{child_object}->{mode} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Mode Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{mode} + ->set( $mode[$self->{data}->{info}->{mode}] ); + } } if ( $self->{previous}->{info}->{state} != $self->{data}->{info}->{state} ) @@ -972,6 +981,12 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{heattemp} = $self->{data}->{info}->{heattemp}; + if ( defined $self->{child_object}->{heat_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Heat Setpoint Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{heat_sp} + ->set( $self->{data}->{info}->{heattemp}, 'poll' ); + } } if ( $self->{previous}->{info}->{heattempmin} != @@ -1005,6 +1020,12 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{cooltemp} = $self->{data}->{info}->{cooltemp}; + if ( defined $self->{child_object}->{cool_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Cooling Setpoint Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{cool_sp} + ->set( $self->{data}->{info}->{cooltemp}, 'poll' ); + } } if ( $self->{previous}->{info}->{cooltempmin} != @@ -1742,4 +1763,112 @@ sub set { } } +package Venstar_Colortouch_Mode; + +@Venstar_Colortouch_Mode::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + + $object->register( $self, 'mode' ); + $self->set( $object->get_mode, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( lc $p_state eq "cooling" ) or ( lc $p_state eq "heating" ) or ( lc $p_state eq "cool" ) or ( lc $p_state eq "heat" ) or ( lc $p_state eq "auto" ) or ( lc $p_state eq "off" ) ) { + $$self{master_object}->set_mode($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Mode] Error. Unknown set state $p_state" + ); + } + } +} + +package Venstar_Colortouch_Heat_sp; + +@Venstar_Colortouch_Heat_sp::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + + $object->register( $self, 'heat_sp' ); + $self->set( $object->get_sp_heat, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { + $$self{master_object}->set_heat_sp($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Heat_SP] Error. Unknown set state $p_state" + ); + } + } +} + +package Venstar_Colortouch_Cool_sp; + +@Venstar_Colortouch_Cool_sp::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + + $object->register( $self, 'cool_sp' ); + $self->set( $object->get_sp_cool, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { + $$self{master_object}->set_cool_sp($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state" + ); + } + } +} + 1; From abffc4ac4814fdeacf98bfc94197a70aa1b8fda7 Mon Sep 17 00:00:00 2001 From: Pmatis Date: Mon, 19 Sep 2016 10:20:03 -0400 Subject: [PATCH 04/14] Neaten examples --- lib/Venstar_Colortouch.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index dbfaf6fc7..fa41ba403 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -9,16 +9,16 @@ use JSON::XS; use Data::Dumper; # Venstar::Colortouch -# $stat_upper = new Venstar_Colortouch('192.168.0.100'); +# $stat_upper = new Venstar_Colortouch('192.168.0.100'); # # $stat_upper_mode = new Venstar_Colortouch_Mode($stat_upper); -# $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); +# $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); # $stat_upper_heat_sp = new Venstar_Colortouch_Heat_sp($stat_upper); # $stat_upper_cool_sp = new Venstar_Colortouch_Cool_sp($stat_upper); -# $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); -# $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); -# $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); -# $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); +# $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); +# $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); +# $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); +# $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); # $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); # v1.1 - added in schedule and humidity control. From 914d40074b44b45549021daa1e2e889ca2a3c9b4 Mon Sep 17 00:00:00 2001 From: Pmatis Date: Mon, 19 Sep 2016 10:24:05 -0400 Subject: [PATCH 05/14] Add debug control --- lib/Venstar_Colortouch.pm | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index fa41ba403..6b88ca1ba 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -59,7 +59,7 @@ $rest{control} = "/control"; $rest{settings} = "/settings"; sub new { - my ( $class, $host, $poll ) = @_; + my ( $class, $host, $poll, $debug ) = @_; my $self = {}; bless $self, $class; $self->{data} = undef; @@ -77,7 +77,10 @@ sub new { $self->{updating} = 0; $self->{data}->{retry} = 0; $self->{host} = $host; - $self->{debug} = 5; + $self->{debug} = 0; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 + if ( $self->{debug} < 0 ); $self->{loglevel} = 1; $self->{status} = ""; $self->{timeout} = 15; #300; @@ -1310,6 +1313,11 @@ sub get_mode_humid { } +sub get_debug { + my ($self) = @_; + return $self->{debug}; +} + #------------ # User control methods #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 @@ -1549,6 +1557,14 @@ sub set_units { } + +sub set_debug { + my ( $self, $debug ) = @_; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 + if ( $self->{debug} < 0 ); +} + sub set { my ( $self, $p_state, $p_setby ) = @_; From 604618955dddcadda396d91c533325932ad42080 Mon Sep 17 00:00:00 2001 From: Pmatis Date: Mon, 19 Sep 2016 10:32:10 -0400 Subject: [PATCH 06/14] Add support for commercial versions andthe values that only commercial versions have --- lib/Venstar_Colortouch.pm | 326 +++++++++++++++++++++++++++++++++++--- 1 file changed, 308 insertions(+), 18 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 6b88ca1ba..91d81ff2b 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -58,6 +58,7 @@ $rest{alerts} = "query/alerts"; $rest{control} = "/control"; $rest{settings} = "/settings"; + sub new { my ( $class, $host, $poll, $debug ) = @_; my $self = {}; @@ -119,10 +120,13 @@ sub _init { if ($isSuccessResponse1) { $self->{api_ver} = $stat->{api_ver}; - if ( ( $self->{api_ver} > 3 ) and ( $stat->{type} eq "residential" ) ) { + + if ( ( $stat->{api_ver} > 3 ) and ( $stat->{type} eq "residential" or $stat->{type} eq "commercial" ) ) { + + $self->{type} = $stat->{type}; main::print_log( - "[Venstar Colortouch] Residental Venstar ColorTouch found with api level $self->{api_ver}" + "[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}" ); if ( $self->poll() ) { main::print_log( "[Venstar Colortouch:" @@ -140,8 +144,8 @@ sub _init { $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; ## set states based on available mode - $self->print_info(); $self->set( $state[ $self->{data}->{info}->{state} ], 'poll' ); + $self->print_info(); } else { @@ -282,7 +286,7 @@ sub _get_JSON_data { . $self->{data}->{name} . "] Warning, not fetching data due to operation in progress" ); return ('0'); - } + } sub _push_JSON_data { @@ -318,17 +322,44 @@ sub _push_JSON_data { #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 # with v4.08 firmware, hum and dehum setpoints need to be at least 4 % points different - - my ( $newunits, $newaway, $newsched, $newhumsp, $newdehumsp ); + + my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; + my %info = %$thedata; + my %cinfo = %$thedata; + my @changearr; + + while ($params =~ /(\w+)=(\d+)/g) { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] _push_JSON_data match: $1 = $2\n"; + $info{$1} = $2; + if ($info{$1} ne $cinfo{$1}) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing $1 from $cinfo{$1} to $info{$1}" ); + push(@changearr, "$1=$info{$1}"); + } + } + + $cmd = join ('&', @changearr); + main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd + ; # if $self->{debug}; + + my ( $newunits, $newaway, $newholiday, $newsched, $newhumsp, $newdehumsp ); my ($units) = $params =~ /tempunits=(\d+)/; my ($away) = $params =~ /away=(\d+)/; + my ($holiday) = $params =~ /holiday=(\d+)/; + my ($override) = $params =~ /override=(\d+)/; + my ($overridetime) = $params =~ /overridetime=(\d+)/; + my ($forceunocc) = $params =~ /forceunocc=(\d+)/; my ($sched) = $params =~ /schedule=(\d+)/; my $hum; my ($humsp) = $params =~ /hum_setpoint=(\d+)/; my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/ ; #need to add in dehumidifier stuff at some point my $humidity_change = 0; - my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, + + my ( $isSuccessResponse, $cunits, $caway, $choliday, $coverride, $coverridetime, $cforceunocc, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; @@ -337,10 +368,14 @@ sub _push_JSON_data { $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); $away = $caway if ( not defined $away ); - $sched = $csched if ( not defined $sched ); + $holiday = $choliday if ( not defined $holiday ); + $override = $coverride if ( not defined $override ); + $overridetime = $coverridetime if ( not defined $overridetime ); + $forceunocc = $cforceunocc if ( not defined $forceunocc ); + $sched = $csched if ( not defined $sched ); $hum = $chum if ( not defined $hum ); $humsp = $chumsp if ( not defined $humsp ); - $dehumsp = $cdehumsp if ( not defined $dehumsp ); + $dehumsp = $cdehumsp if ( not defined $dehumsp ); if ( $cunits ne $units ) { main::print_log( "[Venstar Colortouch:" @@ -362,6 +397,47 @@ sub _push_JSON_data { $newaway = $caway; } + if ( $choliday ne $holiday ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $choliday to $holiday" ); + $newholiday = $holiday; + } + else { + $newholiday = $choliday; + } + + if ( $caway ne $away ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $caway to $away" ); + $newaway = $away; + } + else { + $newaway = $caway; + } + + if ( $caway ne $away ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $caway to $away" ); + $newaway = $away; + } + else { + $newaway = $caway; + } + + if ( $caway ne $away ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $caway to $away" ); + $newaway = $away; + } + else { + $newaway = $caway; + } + + if ( $csched ne $sched ) { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -452,7 +528,7 @@ sub _push_JSON_data { if ( $cfan ne $fan ) { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} - . "] Changing fan from $fanstate[$cfan] to $fanstate[$fan]" ); + . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); $newfan = $fan; } else { @@ -601,9 +677,9 @@ sub _get_control_params { sub _get_setting_params { my ($self) = @_; my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info'); - return ( $isSuccessResponse, $info->{tempunits}, $info->{away}, - $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, - $info->{dehum_setpoint} ); + return ( $isSuccessResponse, $info); #->{tempunits}, $info->{away}, +# $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, +# $info->{dehum_setpoint} ); } sub stop_timer { @@ -900,6 +976,8 @@ sub process_data { $self->{child_object}->{mode} ->set( $mode[$self->{data}->{info}->{mode}] ); } + + } if ( $self->{previous}->{info}->{state} != $self->{data}->{info}->{state} ) @@ -947,17 +1025,59 @@ sub process_data { $self->{data}->{info}->{schedulepart}; } - if ( $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) { + if ( $self->{type} eq "residential" and $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) { my $away = "home mode"; $away = "away mode" if ( $self->{data}->{info}->{away} ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} - . "] Thermostat occupency changed to" + . "] Thermostat occupancy changed to" . $away ) if ( $self->{loglevel} ); $self->{previous}->{info}->{away} = $self->{data}->{info}->{away}; } + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{holiday} != $self->{data}->{info}->{holiday} ) { + my $holiday = "observing holiday"; + $holiday = "no holiday" if ( $self->{data}->{info}->{holiday} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat holiday changed to" + . $holiday ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{holiday} = $self->{data}->{info}->{holiday}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{override} != $self->{data}->{info}->{override} ) { + my $override = "off"; + $override = "on" if ( $self->{data}->{info}->{override} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat override changed to" + . $override ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{override} = $self->{data}->{info}->{override}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{overridetime} != $self->{data}->{info}->{overridetime} ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat overridetime changed to" + . $self->{data}->{info}->{overridetime} ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{overridetime} = $self->{data}->{info}->{overridetime}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{forceunocc} != $self->{data}->{info}->{forceunocc} ) { + my $forceunocc = "off"; + $forceunocc = "on" if ( $self->{data}->{info}->{forceunocc} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat forceunocc changed to" + . $forceunocc ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{forceunocc} = $self->{data}->{info}->{forceunocc}; + } + if ( $self->{previous}->{info}->{spacetemp} != $self->{data}->{info}->{spacetemp} ) { @@ -1082,6 +1202,10 @@ sub process_data { } } + #if ($self->{previous}->{info}->{hum} != $self->{data}->{info}->{hum}) { + # main::print_log("[Venstar Colortouch:". $self->{data}->{name} . "] Thermostat humidity changed from $self->{previous}->{info}->{hum} to $self->{data}->{info}->{hum}") if ($self->{loglevel}); + # $self->{previous}->{info}->{hum} = $self->{data}->{info}->{hum}; + #} if ( $self->{previous}->{info}->{setpointdelta} != $self->{data}->{info}->{setpointdelta} ) @@ -1509,10 +1633,175 @@ sub set_mode { } sub set_away { + my ( $self, $value ) = @_; + return unless $self->{type} eq "residential"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown away mode $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'control', "away=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set away to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} - #I don't use this. Shouldn't just be this simple though. - #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); +sub set_holiday { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown holiday $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "holiday=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set holiday to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} +sub set_override { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown override $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "override=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set override to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} + +sub set_overridetime { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "overridetime=$value" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set overridetime to $value" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} + +sub set_forceunocc { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown forceunocc mode $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "forceunocc=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set forceunocc to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } } sub set_fan { @@ -1555,8 +1844,9 @@ sub set_fan { sub set_units { -} + #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); +} sub set_debug { my ( $self, $debug ) = @_; From 30e303a3003cb92a38f931d03330631b974d84b9 Mon Sep 17 00:00:00 2001 From: hplato Date: Sun, 25 Sep 2016 15:03:28 -0600 Subject: [PATCH 07/14] Added in workaround for firmware 4.08 humidity bug --- lib/Venstar_Colortouch.pm | 194 +++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 91d81ff2b..289182992 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -1,4 +1,5 @@ package Venstar_Colortouch; +# v1.4.1 use strict; use warnings; @@ -19,7 +20,7 @@ use Data::Dumper; # $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); # $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); # $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); -# $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); +# $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); # v1.1 - added in schedule and humidity control. # v1.2 - added communication tracker object & timeout control @@ -28,6 +29,7 @@ use Data::Dumper; # "hum_setpoint": is the current humidity and # "dehum_setpoint": is the humidity setpoint # "hum" doesn't return anything anymore. +# v1.4.1 - API v5, working schedule, humidity setpoints # Notes: # - Best to use firmware at least 3.14 released Nov 2014. This fixes issues with both @@ -40,8 +42,8 @@ use Data::Dumper; # - figure out timezone w/ DST # - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints # - add decimals for setpoints -# # make the data poll non-blocking, turn off timer -# +# - make the data poll non-blocking, turn off timer + # State can only be set by stat. Set mode will change the mode. @Venstar_Colortouch::ISA = ('Generic_Item'); @@ -286,13 +288,13 @@ sub _get_JSON_data { . $self->{data}->{name} . "] Warning, not fetching data due to operation in progress" ); return ('0'); - + } } sub _push_JSON_data { my ( $self, $type, $params ) = @_; - my ( @fan, @fanstate, @modename, @statename, @schedule, @home ); + my ( @fan, @fanstate, @modename, @statename, @schedule, @home, @schedulestat ); $fan[0] = "auto"; $fan[1] = "on"; $fanstate[0] = "off"; @@ -313,6 +315,10 @@ sub _push_JSON_data { $schedule[2] = "evening (occupied3)"; $schedule[3] = "night (occupied4)"; $schedule[255] = "inactive"; + $schedulestat[0] = "off"; + $schedulestat[1] = "on"; + +#4.08, schedulepart is now the schedule type. schedule 0 is off, my $cmd; @@ -320,30 +326,29 @@ sub _push_JSON_data { $self->stop_timer; #stop timer to prevent a clash of updates if ( $type eq 'settings' ) { - #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 - # with v4.08 firmware, hum and dehum setpoints need to be at least 4 % points different - - my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; - my %info = %$thedata; - my %cinfo = %$thedata; - my @changearr; - - while ($params =~ /(\w+)=(\d+)/g) { - print "[Venstar Colortouch:" - . $self->{data}->{name} - . "] _push_JSON_data match: $1 = $2\n"; - $info{$1} = $2; - if ($info{$1} ne $cinfo{$1}) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing $1 from $cinfo{$1} to $info{$1}" ); - push(@changearr, "$1=$info{$1}"); - } - } + #for testing purposes, curl is: + #curl --data "tempunits=1&away=0&schedule=0&hum_setpoint=30&dehum_setpoint=35" http://ip/settings - $cmd = join ('&', @changearr); - main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd - ; # if $self->{debug}; +# my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; +# my %info = %$thedata; +# my %cinfo = %$thedata; +# my @changearr; +# +# while ($params =~ /(\w+)=(\d+)/g) { +# print "[Venstar Colortouch:" +# . $self->{data}->{name} +# . "] _push_JSON_data match: $1 = $2\n"; +# $info{$1} = $2; +# if ($info{$1} ne $cinfo{$1}) { +# main::print_log( "[Venstar Colortouch:" +# . $self->{data}->{name} +# . "] Changing $1 from $cinfo{$1} to $info{$1}" ); +# push(@changearr, "$1=$info{$1}"); +# } +# } + +# $cmd = join ('&', @changearr); +# main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd; # if $self->{debug}; my ( $newunits, $newaway, $newholiday, $newsched, $newhumsp, $newdehumsp ); my ($units) = $params =~ /tempunits=(\d+)/; @@ -359,10 +364,9 @@ sub _push_JSON_data { ; #need to add in dehumidifier stuff at some point my $humidity_change = 0; - my ( $isSuccessResponse, $cunits, $caway, $choliday, $coverride, $coverridetime, $cforceunocc, $csched, $chum, $chumsp, - $cdehumsp ) - = $self->_get_setting_params; - + my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; +#check why is this called twice? + my ($choliday,$coverride,$coverridetime,$cforceunocc); $units = $cunits if ( not defined $units ); $units = 1 if ( ( $units eq "C" ) or ( $units eq "c" ) ); $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); @@ -374,8 +378,21 @@ sub _push_JSON_data { $forceunocc = $cforceunocc if ( not defined $forceunocc ); $sched = $csched if ( not defined $sched ); $hum = $chum if ( not defined $hum ); - $humsp = $chumsp if ( not defined $humsp ); - $dehumsp = $cdehumsp if ( not defined $dehumsp ); + #v4.08, dehum_sp is humidify, and hum_sp is dehumidify. + if ($self->{api_ver} >=5) { + $humsp = $cdehumsp if ( not defined $humsp ); + } else { + $humsp = $chumsp if ( not defined $humsp ); + } + if ($self->{api_ver} >=5) { + $dehumsp = $chumsp if ( not defined $dehumsp ); + } else { + $dehumsp = $cdehumsp if ( not defined $dehumsp ); + } +print "venstar db: params = $params\n"; +print "units=$units, away=$away, sched=$sched, hum=$hum, humsp=$humsp, dehumsp=$dehumsp\n"; +print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; + if ( $cunits ne $units ) { main::print_log( "[Venstar Colortouch:" @@ -417,70 +434,57 @@ sub _push_JSON_data { $newaway = $caway; } - if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $caway to $away" ); - $newaway = $away; - } - else { - $newaway = $caway; - } - - if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $caway to $away" ); - $newaway = $away; - } - else { - $newaway = $caway; - } - - if ( $csched ne $sched ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]" - ); + if ($self->{api_ver} >=5) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedulestat[$csched] to $schedulestat[$sched]"); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]"); + } $newsched = $sched; } else { $newsched = $csched; } - - if ( $chumsp ne $humsp ) { - print - "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Humidity Setpoint from $chumsp to $humsp\n"; - $newhumsp = $humsp; - $humidity_change = 1; - } - else { - $newhumsp = $chumsp; - } - - if ( $cdehumsp ne $dehumsp ) { - print - "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing (De)humidity Setpoint from $cdehumsp to $dehumsp\n"; - $newdehumsp = $dehumsp; - } - else { - $newdehumsp = $cdehumsp; - } - - # v4.08 needs 6 % point delta on humidity and dehumidity. - # if humidify is more then dehumidify, then set dehumidify to 6 + humidity - $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); + if ($self->{api_ver} >=5) { + if ( $cdehumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $cdehumsp to $humsp\n" ); + $newhumsp = $humsp; + $humidity_change = 1; + } else { + $newhumsp = $cdehumsp; + } + + if ( $chumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] *Changing Dehumidity Setpoint from $chumsp to $dehumsp\n" ); + $newdehumsp = $dehumsp; + } else { + $newdehumsp = $chumsp; + } + } else { + if ( $chumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $chumsp to $humsp\n" ); + $newhumsp = $humsp; + $humidity_change = 1; + } else { + $newhumsp = $chumsp; + } + + if ( $cdehumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Dehumidity Setpoint from $cdehumsp to $dehumsp\n" ); + $newdehumsp = $dehumsp; + } else { + $newdehumsp = $cdehumsp; + } + } + +## to set v4.08, humidity to 32%, this is needed "tempunits=1&away=0&schedule=0&hum_setpoint=32&dehum_setpoint=38" +## so humidity setpoint hasn't changed? if ($self->{api_ver} >=5) { - $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched"; #&dehum_setpoint=$newhumsp&hum_setpoint=" . ($newhumsp + 1); - $cmd = "hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp" if ($humidity_change); - } else { - $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; - } + # v4.08 has changed humidification settings + # - need 6 % point delta on humidity and dehumidity. + $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); + } + $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; main::print_log( "Sending Settings command [$cmd] to " . $self->{host} ) if $self->{debug}; } @@ -677,9 +681,9 @@ sub _get_control_params { sub _get_setting_params { my ($self) = @_; my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info'); - return ( $isSuccessResponse, $info); #->{tempunits}, $info->{away}, -# $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, -# $info->{dehum_setpoint} ); + return ( $isSuccessResponse, $info->{tempunits}, $info->{away}, + $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, + $info->{dehum_setpoint} ); } sub stop_timer { @@ -2177,4 +2181,4 @@ sub set { } } -1; +1; \ No newline at end of file From cc40b8d7b33ec882c4e0e86de90daec941c4d60a Mon Sep 17 00:00:00 2001 From: hplato Date: Mon, 26 Sep 2016 20:14:09 -0600 Subject: [PATCH 08/14] v2.0 - Initial background support Added ability to background web requests to reduce pauses. --- lib/Venstar_Colortouch.pm | 332 ++++++++++++++++++++++---------------- 1 file changed, 189 insertions(+), 143 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 289182992..37172d999 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -1,5 +1,5 @@ package Venstar_Colortouch; -# v1.4.1 +# v2.0 use strict; use warnings; @@ -30,6 +30,7 @@ use Data::Dumper; # "dehum_setpoint": is the humidity setpoint # "hum" doesn't return anything anymore. # v1.4.1 - API v5, working schedule, humidity setpoints +# v2.0 - Background process # Notes: # - Best to use firmware at least 3.14 released Nov 2014. This fixes issues with both @@ -65,31 +66,36 @@ sub new { my ( $class, $host, $poll, $debug ) = @_; my $self = {}; bless $self, $class; - $self->{data} = undef; - $self->{api_ver} = 0; - $self->{child_object} = undef; - $self->{config}->{cache_time} = 30; #TODO fix cache timeouts - $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} - if defined $::config_params{venstar_config_cache_time}; - $self->{config}->{tz} = - $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes + $self->{data} = undef; + $self->{api_ver} = 0; + $self->{child_object} = undef; + $self->{config}->{cache_time} = 30; #TODO fix cache timeouts + $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} if defined $::config_params{venstar_config_cache_time}; + $self->{config}->{tz} =$::config_params{time_zone}; #TODO Need to figure out DST for print runtimes $self->{config}->{poll_seconds} = 60; $self->{config}->{poll_seconds} = $poll if ($poll); - $self->{config}->{poll_seconds} = 1 - if ( $self->{config}->{poll_seconds} < 1 ); - $self->{updating} = 0; - $self->{data}->{retry} = 0; - $self->{host} = $host; - $self->{debug} = 0; - $self->{debug} = $debug if ($debug); - $self->{debug} = 0 - if ( $self->{debug} < 0 ); - $self->{loglevel} = 1; - $self->{status} = ""; - $self->{timeout} = 15; #300; - + $self->{config}->{poll_seconds} = 1 if ( $self->{config}->{poll_seconds} < 1 ); + $self->{updating} = 0; + $self->{data}->{retry} = 0; + $self->{host} = $host; + $self->{debug} = 5; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 if ( $self->{debug} < 0 ); + $self->{loglevel} = 1; + $self->{status} = ""; + $self->{timeout} = 15; #300; + $self->{background} = 1; + $self->{max_poll_queue} = 3; + if ($self->{background}) { + @{$self->{command_queue}} = (); + $self->{poll_data_file} = "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; + $self->{poll_process} = new Process_Item; + $self->{poll_process}->set_output($self->{poll_data_file}); + &::MainLoop_post_add_hook( \&Venstar_Colortouch::process_check, 0, $self ); + } + $self->{timer} = new Timer; $self->_init; - $self->{timer} = new Timer; $self->start_timer; return $self; } @@ -107,7 +113,7 @@ sub get_data { #main::print_log("[Venstar Colortouch] get_data initiated"); $self->poll; - $self->process_data; + $self->process_data unless ($self->{background}); #for background tasks, data will be processed when process completed. } sub _init { @@ -118,7 +124,7 @@ sub _init { $state[2] = "cooling"; $state[3] = "lockout"; $state[4] = "error"; - my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api'); + my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api',"direct"); if ($isSuccessResponse1) { $self->{api_ver} = $stat->{api_ver}; @@ -127,63 +133,60 @@ sub _init { $self->{type} = $stat->{type}; - main::print_log( - "[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}" - ); - if ( $self->poll() ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Data Successfully Retrieved" ); + main::print_log("[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}"); + if ( $self->poll("direct") ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Data Successfully Retrieved" ); $self->{active} = 1; $self->{previous}->{tempunits} = $self->{data}->{tempunits}; $self->{previous}->{name} = $self->{data}->{name}; foreach my $key1 ( keys $self->{data}->{info} ) { - $self->{previous}->{info}->{$key1} = - $self->{data}->{info}->{$key1}; + $self->{previous}->{info}->{$key1} = $self->{data}->{info}->{$key1}; } - $self->{previous}->{sensors}->{sensors}[0]->{temp} = - $self->{data}->{sensors}->{sensors}[0]->{temp}; - $self->{previous}->{sensors}->{sensors}[0]->{hum} = - $self->{data}->{sensors}->{sensors}[0]->{hum}; + $self->{previous}->{sensors}->{sensors}[0]->{temp} = $self->{data}->{sensors}->{sensors}[0]->{temp}; + $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; ## set states based on available mode - $self->set( $state[ $self->{data}->{info}->{state} ], 'poll' ); +### Strange, if this set is here, then the timer is not defined. + #print "db: set " . $state[ $self->{data}->{info}->{state} ] . "=" . $self->{data}->{info}->{state} . "\n"; + #$self->set( $state[ $self->{data}->{info}->{state} ], 'poll' ); $self->print_info(); } else { - main::print_log( - "[Venstar Colortouch] Problem retrieving initial data"); + main::print_log("[Venstar Colortouch] Problem retrieving initial data"); $self->{active} = 0; return ('1'); } } else { - main::print_log( - "[Venstar Colortouch] Unknown device " . $self->{host} ); + main::print_log("[Venstar Colortouch] Unknown device " . $self->{host} ); $self->{active} = 0; return ('1'); } } else { - main::print_log( "[Venstar Colortouch] Error. Unable to connect to " - . $self->{host} ); + main::print_log( "[Venstar Colortouch] Error. Unable to connect to " . $self->{host} ); $self->{active} = 0; return ('1'); } } sub poll { - my ($self) = @_; - - main::print_log("[Venstar Colortouch] Polling initiated") - if ( $self->{debug} ); - - my ( $isSuccessResponse1, $info ) = $self->_get_JSON_data('info'); - my ( $isSuccessResponse2, $sensors ) = $self->_get_JSON_data('sensors'); + my ($self,$method) = @_; + $method = "" unless (defined $method); + if (($self->{background}) and (lc $method ne "direct")) { + main::print_log("[Venstar Colortouch] Background Polling initiated") if ( $self->{debug} ); + } else { + main::print_log("[Venstar Colortouch] Direct Polling initiated") if ( $self->{debug} ); + } + #spawn off info. + #might had to add into the main loop to check if the object's data has changed. + #$self->{process_pid}->{{$self->{process}->pid} = "poll_info"; + my ( $isSuccessResponse1, $info ) = $self->_get_JSON_data('info',$method); + my ( $isSuccessResponse2, $sensors ) = $self->_get_JSON_data('sensors',$method); - if ( $isSuccessResponse1 and $isSuccessResponse2 ) { + if (( $isSuccessResponse1 and $isSuccessResponse2 ) and ((!$self->{background}) or (lc $method eq "direct"))) { $self->{data}->{tempunits} = $info->{tempunits}; $self->{data}->{name} = $info->{name}; $self->{data}->{info} = $info; @@ -191,103 +194,139 @@ sub poll { $self->{data}->{timestamp} = time; $self->{data}->{retry} = 0; - #if (defined $self->{child_object}->{comm}) { - # if ($self->{child_object}->{comm}->state() ne "online") { - # main::print_log "[Venstar Colortouch:". $self->{data}->{name} . "]] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to online..." if ($self->{loglevel}); - # $self->{child_object}->{comm}->set("online",'poll'); - # } - #} return ('1'); - - #} else { - # main::print_log("[Venstar Colortouch:". $self->{data}->{name} . "] Problem retrieving poll data from " . $self->{host}); - # $self->{data}->{retry}++; - # if (defined $self->{child_object}->{comm}) { - # if ($self->{child_object}->{comm}->state() ne "offline") { - # main::print_log "[Venstar Colortouch:". $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ($self->{loglevel}); - # $self->{child_object}->{comm}->set("offline",'poll'); - # } - # } - # return ('0'); } } +sub process_check { + my ($self) = @_; + + return unless (defined $self->{poll_process}); + if ($self->{poll_process}->done_now()) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background, " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); + + my $file_data = &main::file_read($self->{poll_data_file}); + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + my ($json_data) = $file_data =~ /({.*})/; + my $data; + eval { $data = JSON::XS->new->decode( $json_data ); }; + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + } else { + if (keys $data) { +# if ($self->{poll_process_pid}->{$self->{poll_process}->pid()} eq "info") { +# need to check for valid data + if ($self->{poll_process_mode} eq "info") { + $self->{data}->{tempunits} = $data->{tempunits}; + $self->{data}->{name} = $data->{name}; + $self->{data}->{info} = $data; + $self->{data}->{timestamp} = time; + } elsif ($self->{poll_process_mode} eq "sensors") { + $self->{data}->{sensors} = $data; + $self->{data}->{timestamp} = time; + } + $self->process_data(); + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + } + } + if (scalar @{$self->{command_queue}}) { + my $cmd_string = shift @{$self->{command_queue}}; + my ($mode,$cmd) = split/\|/,$cmd_string; + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); + + } + } +} + #------------------------------------------------------------------------------------ sub _get_JSON_data { - my ( $self, $mode ) = @_; - - unless ( $self->{updating} ) { - - $self->{updating} = 1; - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); - - my $host = $self->{host}; - - my $request = HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); - $request->content_type("application/x-www-form-urlencoded"); - - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" - if $self->{debug}; + my ( $self, $mode, $method ) = @_; - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; - $self->{updating} = 0; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, failed to get data. Response code $responseCode" - ); - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to offline..." - if ( $self->{loglevel} ); - $self->{status} = "offline"; - $self->{child_object}->{comm}->set( "offline", 'poll' ); - } - } - return ('0'); - } - else { - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "offline" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to online..." - if ( $self->{loglevel} ); - $self->{status} = "online"; - $self->{child_object}->{comm}->set( "online", 'poll' ); - } - } - } - my $response; - eval { $response = JSON::XS->new->decode( $responseObj->content ); }; + if (($self->{background}) and (lc $method ne "direct")) { + + my $cmd = 'get_url "http://' . $self->{host} . "/$rest{$mode}" . '"'; + if ($self->{poll_process}->done()) { + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{poll_process}->pid() . " command $mode, $cmd") if ($self->{debug}); + + } else { + if (scalar @{$self->{command_queue}} < $self->{max_poll_queue}) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{command_queue}} . ". Queing command $mode, $cmd") if ($self->{debug}); + push @{$self->{command_queue}}, "$mode|$cmd"; +#TODO: queue shouldn't grow for polling. Since a poll is 2 queries, the queue should only be 3 items. Otherwise it will grow every poll +# if there are device issues. + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_poll_queue} . ". Command discarded."); + } + } + } else { + unless ( $self->{updating} ) { + + $self->{updating} = 1; + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); + + my $host = $self->{host}; + + my $request = HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); + $request->content_type("application/x-www-form-urlencoded"); + + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + my $isSuccessResponse = $responseCode < 400; + $self->{updating} = 0; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to get data. Response code $responseCode"); +print "Venstar. status=" . $self->{status}; +if (defined $self->{child_object}->{comm}) { + print " Tracker defined\n"; +} else { + print " Tracker UNDEFINED\n"; +} + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "online" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + return ('0'); + } else { + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "offline" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to online..." if ( $self->{loglevel} ); + $self->{status} = "online"; + $self->{child_object}->{comm}->set( "online", 'poll' ); + } + } + } + my $response; + eval { $response = JSON::XS->new->decode( $responseObj->content ); }; - # catch crashes: - if ($@) { - print "[Venstar Colortouch:" - . $self->{data}->{name} - . "] ERROR! JSON parser crashed! $@\n"; - return ('0'); - } - else { - return ( $isSuccessResponse, $response ); - } - } - else { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, not fetching data due to operation in progress" ); - return ('0'); + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON parser crashed! $@\n"; + return ('0'); + } else { + return ( $isSuccessResponse, $response ); + } + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, not fetching data due to operation in progress" ); + return ('0'); + } } } @@ -365,7 +404,6 @@ sub _push_JSON_data { my $humidity_change = 0; my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; -#check why is this called twice? my ($choliday,$coverride,$coverridetime,$cforceunocc); $units = $cunits if ( not defined $units ); $units = 1 if ( ( $units eq "C" ) or ( $units eq "c" ) ); @@ -617,6 +655,12 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to push data. Response code $responseCode" ); +print "Venstar. status=" . $self->{status}; +if (defined $self->{child_object}->{comm}) { + print " Tracker defined\n"; +} else { + print " Tracker UNDEFINED\n"; +} if ( defined $self->{child_object}->{comm} ) { if ( $self->{status} eq "online" ) { main::print_log "[Venstar Colortouch:" @@ -701,7 +745,7 @@ sub stop_timer { sub start_timer { my ($self) = @_; - + print "db: timer has started\n"; if ( defined $self->{timer} ) { $self->{timer}->set( $self->{config}->{poll_seconds}, sub { &Venstar_Colortouch::_poll_check($self) }, -1 ); @@ -1863,9 +1907,11 @@ sub set { my ( $self, $p_state, $p_setby ) = @_; if ( $p_setby eq 'poll' ) { + print "db: in super set\n"; $self->SUPER::set($p_state); } else { + print "db: in mode set\n"; $self->set_mode($p_state); } } From 8bb3cf053aee38cfe9ed6781449cacf76a19c416 Mon Sep 17 00:00:00 2001 From: hplato Date: Tue, 27 Sep 2016 16:22:26 -0600 Subject: [PATCH 09/14] Working active commands. --- lib/Venstar_Colortouch.pm | 235 +++++++++++++++++++++++++++----------- 1 file changed, 166 insertions(+), 69 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 37172d999..202770380 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -9,7 +9,7 @@ use HTTP::Request::Common qw(POST); use JSON::XS; use Data::Dumper; -# Venstar::Colortouch +# Venstar::Colortouch Objects # $stat_upper = new Venstar_Colortouch('192.168.0.100'); # # $stat_upper_mode = new Venstar_Colortouch_Mode($stat_upper); @@ -22,6 +22,7 @@ use Data::Dumper; # $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); # $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); +# Version History # v1.1 - added in schedule and humidity control. # v1.2 - added communication tracker object & timeout control # v1.3 - added check for timer defined @@ -32,20 +33,26 @@ use Data::Dumper; # v1.4.1 - API v5, working schedule, humidity setpoints # v2.0 - Background process -# Notes: +# Notes +# - State can only be set by stat. Set mode will change the mode. # - Best to use firmware at least 3.14 released Nov 2014. This fixes issues with both # schedule and humidity/dehumidify control. -# - 4.08 is API5, seems to have better wifi, but humidity control is messed up. +# - 4.08 brings API5, and a workaround to a humidity bug. -#todo -# - temp setpoint bounds checking +# Issues +#2 # - log runtimes. Maybe into a dbm file? log_runtimes method with destination. # - figure out timezone w/ DST +#1 +# - temp setpoint bounds checking # - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints +# - allow for a humidity value of 0 # - add decimals for setpoints -# - make the data poll non-blocking, turn off timer - -# State can only be set by stat. Set mode will change the mode. +# - add in communication tracker for background requests +# - verify command sets work both with existing poll data, and have a poll data gap. +# - verify that network issues don't escalate CPU usage (by _push_json_data being continually called) +# - add in the commercial stat fields +# - incorporate Steve's approach for more efficient detection of changed values. @Venstar_Colortouch::ISA = ('Generic_Item'); @@ -58,8 +65,8 @@ $rest{api} = ""; $rest{sensors} = "query/sensors"; $rest{runtimes} = "query/runtimes"; $rest{alerts} = "query/alerts"; -$rest{control} = "/control"; -$rest{settings} = "/settings"; +$rest{control} = "control"; +$rest{settings} = "settings"; sub new { @@ -69,10 +76,10 @@ sub new { $self->{data} = undef; $self->{api_ver} = 0; $self->{child_object} = undef; - $self->{config}->{cache_time} = 30; #TODO fix cache timeouts + $self->{config}->{cache_time} = 30; #is this still necessary? $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} if defined $::config_params{venstar_config_cache_time}; - $self->{config}->{tz} =$::config_params{time_zone}; #TODO Need to figure out DST for print runtimes - $self->{config}->{poll_seconds} = 60; + $self->{config}->{tz} = $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes + $self->{config}->{poll_seconds} = 10; $self->{config}->{poll_seconds} = $poll if ($poll); $self->{config}->{poll_seconds} = 1 if ( $self->{config}->{poll_seconds} < 1 ); $self->{updating} = 0; @@ -85,13 +92,21 @@ sub new { $self->{status} = ""; $self->{timeout} = 15; #300; $self->{background} = 1; - $self->{max_poll_queue} = 3; + $self->{poll_data_timestamp} = 0; + $self->{max_poll_queue} = 3; + $self->{max_cmd_queue} = 5; + $self->{cmd_process_retry_limit}= 6; if ($self->{background}) { - @{$self->{command_queue}} = (); + @{$self->{poll_queue}} = (); $self->{poll_data_file} = "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; unlink "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; $self->{poll_process} = new Process_Item; $self->{poll_process}->set_output($self->{poll_data_file}); + @{$self->{cmd_queue}} = (); + $self->{cmd_data_file} = "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; + $self->{cmd_process} = new Process_Item; + $self->{cmd_process}->set_output($self->{cmd_data_file}); &::MainLoop_post_add_hook( \&Venstar_Colortouch::process_check, 0, $self ); } $self->{timer} = new Timer; @@ -204,7 +219,7 @@ sub process_check { return unless (defined $self->{poll_process}); if ($self->{poll_process}->done_now()) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background, " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background poll " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); my $file_data = &main::file_read($self->{poll_data_file}); #for some reason get_url adds garbage to the output. Clean out the characters before and after the json @@ -216,8 +231,7 @@ sub process_check { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; } else { if (keys $data) { -# if ($self->{poll_process_pid}->{$self->{poll_process}->pid()} eq "info") { -# need to check for valid data + $self->{poll_data_timestamp} = time; if ($self->{poll_process_mode} eq "info") { $self->{data}->{tempunits} = $data->{tempunits}; $self->{data}->{name} = $data->{name}; @@ -232,17 +246,57 @@ sub process_check { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; } } - if (scalar @{$self->{command_queue}}) { - my $cmd_string = shift @{$self->{command_queue}}; + if (scalar @{$self->{poll_queue}}) { + my $cmd_string = shift @{$self->{poll_queue}}; my ($mode,$cmd) = split/\|/,$cmd_string; $self->{poll_process}->set($cmd); $self->{poll_process}->start(); $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; $self->{poll_process_mode} = $mode; - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Poll Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); + } + } + return unless (defined $self->{cmd_process}); + if ($self->{cmd_process}->done_now()) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background Command " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); + + my $file_data = &main::file_read($self->{cmd_data_file}); + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + my ($json_data) = $file_data =~ /({.*})/; + my $data; + eval { $data = JSON::XS->new->decode( $json_data ); }; + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + } else { + if (keys $data) { + if ($data->{success} eq "true") { + shift @{$self->{cmd_queue}}; #remove the command from queue since it was successful + $self->{cmd_process_retry} = 0; + $self->poll; + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING Issued command was unsuccessful (success=" . $data->{success} .") , retrying..."; + if ($self->{cmd_process_retry} > $self->{cmd_process_retry_limit}) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR Issued command max retries reached. Abandoning command attempt..."; + shift @{$self->{cmd_queue}}; + $self->{cmd_process_retry} = 0; + } else { + $self->{cmd_process_retry}++; + } + } + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + } + } + if (scalar @{$self->{cmd_queue}}) { + my $cmd = @{$self->{cmd_queue}}[0]; #grab the first command, but don't take it off. + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{poll_process}->pid() . " cmd=$cmd") if ($self->{debug}); } } + } #------------------------------------------------------------------------------------ @@ -260,9 +314,9 @@ sub _get_JSON_data { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{poll_process}->pid() . " command $mode, $cmd") if ($self->{debug}); } else { - if (scalar @{$self->{command_queue}} < $self->{max_poll_queue}) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{command_queue}} . ". Queing command $mode, $cmd") if ($self->{debug}); - push @{$self->{command_queue}}, "$mode|$cmd"; + if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{poll_queue}} . ". Queing command $mode, $cmd") if ($self->{debug}); + push @{$self->{poll_queue}}, "$mode|$cmd"; #TODO: queue shouldn't grow for polling. Since a poll is 2 queries, the queue should only be 3 items. Otherwise it will grow every poll # if there are device issues. } else { @@ -331,7 +385,7 @@ if (defined $self->{child_object}->{comm}) { } sub _push_JSON_data { - my ( $self, $type, $params ) = @_; + my ( $self, $type, $params, $method ) = @_; my ( @fan, @fanstate, @modename, @statename, @schedule, @home, @schedulestat ); $fan[0] = "auto"; @@ -360,9 +414,26 @@ sub _push_JSON_data { #4.08, schedulepart is now the schedule type. schedule 0 is off, my $cmd; + $method = "" unless (defined $method); + +#For background tasks, we want up to date data, ie returned within the last poll period. +#recursively calling the same subroutine might be a memory or performance hog, but need to effectively +#'suspend' the data push until we get valid data. + + if (($self->{background}) and (lc $method ne "direct")) { + if ($self->{poll_data_timestamp} + $self->{config}->{poll_seconds} < &main::tickcount()) { + $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more + if ($self->{poll_data_timestamp} + 300 > &main::tickcount()) { #give up after 5 minutes of trying + $self->_push_json_data($type,$params); + } else { + return ('1'); + } + } + } + #print "VCT DB: $params\n"; - $self->stop_timer; #stop timer to prevent a clash of updates + $self->stop_timer unless ($self->{background}); #stop timer to prevent a clash of updates if ( $type eq 'settings' ) { #for testing purposes, curl is: @@ -399,12 +470,23 @@ sub _push_JSON_data { my ($sched) = $params =~ /schedule=(\d+)/; my $hum; my ($humsp) = $params =~ /hum_setpoint=(\d+)/; - my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/ - ; #need to add in dehumidifier stuff at some point + my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/; #need to add in dehumidifier stuff at some point my $humidity_change = 0; - my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; + my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ); my ($choliday,$coverride,$coverridetime,$cforceunocc); + + + if (($self->{background}) and (lc $method ne "direct")) { + $cunits = $self->{data}->{info}->{tempunits}; + $caway = $self->{data}->{info}->{away}; + $csched = $self->{data}->{info}->{schedule}; + $chumsp = $self->{data}->{info}->{hum_setpoint}; + $cdehumsp = $self->{data}->{info}->{dehum_setpoint}; + } else { + ($isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; + #($choliday,$coverride,$coverridetime,$cforceunocc); + } $units = $cunits if ( not defined $units ); $units = 1 if ( ( $units eq "C" ) or ( $units eq "c" ) ); $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); @@ -433,9 +515,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, if ( $cunits ne $units ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Units from $cunits to $units" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Units from $cunits to $units" ); $newunits = $units; } else { @@ -443,9 +523,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $home[$caway] to $home[$away]" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $home[$caway] to $home[$away]" ); $newaway = $away; } else { @@ -453,9 +531,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $choliday ne $holiday ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $choliday to $holiday" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $choliday to $holiday" ); $newholiday = $holiday; } else { @@ -463,9 +539,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $caway to $away" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $caway to $away" ); $newaway = $away; } else { @@ -538,12 +612,22 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, my ($heattemp) = $params =~ /heattemp=(\d+)/; #need decimals my ($cooltemp) = $params =~ /cooltemp=(\d+)/; - my ( - $isSuccessResponse, $cmode, $cfan, - $cheattemp, $ccooltemp, $setpointdelta, - $minheat, $maxheat, $mincool, - $maxcool - ) = $self->_get_control_params; + my ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool); + + if (($self->{background}) and (lc $method ne "direct")) { + $cmode = $self->{data}->{info}->{mode}; + $cfan = $self->{data}->{info}->{fan}; + $cheattemp = $self->{data}->{info}->{heattemp}; + $ccooltemp = $self->{data}->{info}->{cooltemp}; + $setpointdelta = $self->{data}->{info}->{setpointdelta}; + $minheat = $self->{data}->{info}->{heattempmin}; + $maxheat = $self->{data}->{info}->{heattempmax}; + $mincool = $self->{data}->{info}->{cooltempmin}; + $maxcool = $self->{data}->{info}->{cooltempmax}; + + } else { + ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool) = $self->_get_control_params; + } $mode = $cmode if ( not defined $mode ); $fan = $cfan if ( not defined $fan ); @@ -636,25 +720,42 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, return ( '1', 'error' ); } - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); + if (($self->{background}) and (lc $method ne "direct")) { + + my $cmd = 'get_url -post "' .$cmd . '" "http://' . $self->{host} . "/$rest{$type}" . '"'; + push @{$self->{cmd_queue}}, "$cmd"; + if ($self->{cmd_process}->done()) { + $self->{cmd_process}->set($cmd); + $self->{cmd_process}->start(); + $self->{cmd_process_retry} = 0; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{cmd_process}->pid() . " command $cmd") if ($self->{debug}); + + } else { + if (scalar @{$self->{cmd_queue}} < $self->{max_cmd_queue}) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{cmd_queue}} . ". Queing command $cmd") if ($self->{debug}); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_cmd_queue} . ". Command discarded."); + } + } + } else { - my $host = $self->{host}; + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); - my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); - $request->content_type("application/x-www-form-urlencoded"); - $request->content($cmd) if $cmd; + my $host = $self->{host}; - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" if $self->{debug}; + my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); + $request->content_type("application/x-www-form-urlencoded"); + $request->content($cmd) if $cmd; - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, failed to push data. Response code $responseCode" ); + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + my $isSuccessResponse = $responseCode < 400; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to push data. Response code $responseCode" ); print "Venstar. status=" . $self->{status}; if (defined $self->{child_object}->{comm}) { print " Tracker defined\n"; @@ -663,12 +764,7 @@ if (defined $self->{child_object}->{comm}) { } if ( defined $self->{child_object}->{comm} ) { if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to offline..." - if ( $self->{loglevel} ); + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); $self->{status} = "offline"; $self->{child_object}->{comm}->set( "offline", 'poll' ); } @@ -697,6 +793,7 @@ if (defined $self->{child_object}->{comm}) { $self->poll if ( $response eq "success" ); $self->start_timer; return ( $isSuccessResponse, $response ); + } } sub register { @@ -724,7 +821,7 @@ sub _get_control_params { sub _get_setting_params { my ($self) = @_; - my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info'); + my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info',"direct"); return ( $isSuccessResponse, $info->{tempunits}, $info->{away}, $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, $info->{dehum_setpoint} ); From eead01054a00c61d906f78c207e6ec7c000ed2e2 Mon Sep 17 00:00:00 2001 From: hplato Date: Tue, 27 Sep 2016 20:42:26 -0600 Subject: [PATCH 10/14] Fixed timeout setting --- bin/get_url | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/get_url b/bin/get_url index 549d83f63..1efcd0fb3 100755 --- a/bin/get_url +++ b/bin/get_url @@ -103,7 +103,7 @@ sub use_ua { $ua->proxy( [ 'http', 'ftp' ] => $config_parms{proxy} ) if $config_parms{proxy}; - $ua->timeout( [120] ); # Time out after 60 seconds + $ua->timeout(60); # Time out after 60 seconds $ua->env_proxy(); $ua->agent( $config_parms{get_url_ua} ) if $config_parms{get_url_ua}; From 42a658ac38eedea112d75e21f2084df3e9a56536 Mon Sep 17 00:00:00 2001 From: hplato Date: Tue, 27 Sep 2016 20:43:01 -0600 Subject: [PATCH 11/14] Fixed some cosmetic errors with v2.0 --- lib/Venstar_Colortouch.pm | 61 ++++++++++++--------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 202770380..9beeee4fa 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -216,7 +216,7 @@ sub poll { sub process_check { my ($self) = @_; - +#Need to catch error 500's 403's and update communication tracker and success:false messages to write to log return unless (defined $self->{poll_process}); if ($self->{poll_process}->done_now()) { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background poll " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); @@ -421,7 +421,7 @@ sub _push_JSON_data { #'suspend' the data push until we get valid data. if (($self->{background}) and (lc $method ne "direct")) { - if ($self->{poll_data_timestamp} + $self->{config}->{poll_seconds} < &main::tickcount()) { + if ($self->{poll_data_timestamp} + $self->{config}->{poll_seconds} < &main::get_tickcount()) { $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more if ($self->{poll_data_timestamp} + 300 > &main::tickcount()) { #give up after 5 minutes of trying $self->_push_json_data($type,$params); @@ -509,9 +509,9 @@ sub _push_JSON_data { } else { $dehumsp = $cdehumsp if ( not defined $dehumsp ); } -print "venstar db: params = $params\n"; -print "units=$units, away=$away, sched=$sched, hum=$hum, humsp=$humsp, dehumsp=$dehumsp\n"; -print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; +#print "venstar db: params = $params\n"; +#print "units=$units, away=$away, sched=$sched, humsp=$humsp, dehumsp=$dehumsp\n"; +#print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; if ( $cunits ne $units ) { @@ -530,7 +530,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, $newaway = $caway; } - if ( $choliday ne $holiday ) { + if ( ($self->{type} eq "commercial") and ($choliday ne $holiday) ) { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $choliday to $holiday" ); $newholiday = $holiday; } @@ -634,17 +634,9 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, $heattemp = $cheattemp if ( !$heattemp ); $cooltemp = $ccooltemp if ( !$cooltemp ); - main::print_log( - "data1=$isSuccessResponse,$cmode,$cfan,$cheattemp,$ccooltemp,$setpointdelta" - ) if $self->{debug}; #TODO pass object to get debug - main::print_log("data2=$mode,$fan,$heattemp,$cooltemp") - if $self->{debug}; #TODO debug if ( $cmode ne $mode ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing mode from $modename[$cmode] to $modename[$mode]" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing mode from $modename[$cmode] to $modename[$mode]"); $newmode = $mode; } else { @@ -652,9 +644,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $cfan ne $fan ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); $newfan = $fan; } else { @@ -662,9 +652,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $heattemp ne $cheattemp ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing heat setpoint from $cheattemp to $heattemp" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing heat setpoint from $cheattemp to $heattemp" ); $newheatsp = $heattemp; } else { @@ -672,9 +660,7 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( $cooltemp ne $ccooltemp ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); $newcoolsp = $cooltemp; } else { @@ -682,35 +668,23 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if ( ( $newcoolsp > $maxcool ) or ( $newcoolsp < $mincool ) ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool"); $newcoolsp = $ccooltemp; } if ( ( $newheatsp > $maxheat ) or ( $newheatsp < $minheat ) ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat"); $newheatsp = $cheattemp; } if ( ( $newheatsp - $newcoolsp ) > $setpointdelta ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta" - ); - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Not setting setpoints" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta"); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Not setting setpoints" ); $newcoolsp = $ccooltemp; $newheatsp = $cheattemp; } - $cmd = - "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; + $cmd = "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; main::print_log( "Sending Control command $cmd to " . $self->{host} ) if $self->{debug}; } @@ -721,7 +695,8 @@ print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, } if (($self->{background}) and (lc $method ne "direct")) { - + $isSuccessResponse = 1; #set these to successful, since the process_data will indicate if a setting was unsuccessful. + $response = "success"; my $cmd = 'get_url -post "' .$cmd . '" "http://' . $self->{host} . "/$rest{$type}" . '"'; push @{$self->{cmd_queue}}, "$cmd"; if ($self->{cmd_process}->done()) { @@ -792,8 +767,8 @@ if (defined $self->{child_object}->{comm}) { print "response=$response\n" if $self->{debug}; $self->poll if ( $response eq "success" ); $self->start_timer; - return ( $isSuccessResponse, $response ); } + return ( $isSuccessResponse, $response ); } sub register { From 13f9535f4edd56649d67488324a58ad0de2d0c33 Mon Sep 17 00:00:00 2001 From: hplato Date: Fri, 7 Oct 2016 16:34:13 -0600 Subject: [PATCH 12/14] v2.0.3 - tracker, setpoints, decimals fixed active commands, verified setpoints --- lib/Venstar_Colortouch.pm | 125 +++++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 30 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 9beeee4fa..e0c0dc52e 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -1,5 +1,5 @@ package Venstar_Colortouch; -# v2.0 +# v2.0.3 use strict; use warnings; @@ -14,7 +14,7 @@ use Data::Dumper; # # $stat_upper_mode = new Venstar_Colortouch_Mode($stat_upper); # $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); -# $stat_upper_heat_sp = new Venstar_Colortouch_Heat_sp($stat_upper); +# $stat_upper_heat_sp = new Venstar_Colortouch_Heat_sp($stat_upper,"F"); #F for Fahrenheit # $stat_upper_cool_sp = new Venstar_Colortouch_Cool_sp($stat_upper); # $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); # $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); @@ -44,10 +44,8 @@ use Data::Dumper; # - log runtimes. Maybe into a dbm file? log_runtimes method with destination. # - figure out timezone w/ DST #1 -# - temp setpoint bounds checking -# - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints -# - allow for a humidity value of 0 -# - add decimals for setpoints +# - TEST temp setpoint bounds checking +# - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints? # - add in communication tracker for background requests # - verify command sets work both with existing poll data, and have a poll data gap. # - verify that network issues don't escalate CPU usage (by _push_json_data being continually called) @@ -85,7 +83,7 @@ sub new { $self->{updating} = 0; $self->{data}->{retry} = 0; $self->{host} = $host; - $self->{debug} = 5; + $self->{debug} = 0; $self->{debug} = $debug if ($debug); $self->{debug} = 0 if ( $self->{debug} < 0 ); $self->{loglevel} = 1; @@ -219,31 +217,40 @@ sub process_check { #Need to catch error 500's 403's and update communication tracker and success:false messages to write to log return unless (defined $self->{poll_process}); if ($self->{poll_process}->done_now()) { + my $com_status = "online"; main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background poll " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); my $file_data = &main::file_read($self->{poll_data_file}); #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + print "debug: file_data=$file_data\n" if ($self->{debug}); my ($json_data) = $file_data =~ /({.*})/; + print "debug: json_data=$json_data\n" if ($self->{debug}); + unless ($file_data) { + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by poll"); + return; + } my $data; eval { $data = JSON::XS->new->decode( $json_data ); }; # catch crashes: if ($@) { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + $com_status = "offline"; } else { if (keys $data) { - $self->{poll_data_timestamp} = time; + $self->{poll_data_timestamp} = &main::get_tickcount(); if ($self->{poll_process_mode} eq "info") { $self->{data}->{tempunits} = $data->{tempunits}; $self->{data}->{name} = $data->{name}; $self->{data}->{info} = $data; - $self->{data}->{timestamp} = time; + $self->{data}->{timestamp} = &main::get_tickcount(); } elsif ($self->{poll_process_mode} eq "sensors") { $self->{data}->{sensors} = $data; - $self->{data}->{timestamp} = time; + $self->{data}->{timestamp} = &main::get_tickcount(); } $self->process_data(); } else { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; } } if (scalar @{$self->{poll_queue}}) { @@ -255,13 +262,25 @@ sub process_check { $self->{poll_process_mode} = $mode; main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Poll Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); - } + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } } return unless (defined $self->{cmd_process}); if ($self->{cmd_process}->done_now()) { + my $com_status = "online"; main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background Command " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); my $file_data = &main::file_read($self->{cmd_data_file}); + unless ($file_data) { + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by command"); + return; + } #for some reason get_url adds garbage to the output. Clean out the characters before and after the json my ($json_data) = $file_data =~ /({.*})/; my $data; @@ -269,6 +288,8 @@ sub process_check { # catch crashes: if ($@) { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + $com_status = "offline"; + } else { if (keys $data) { if ($data->{success} eq "true") { @@ -280,13 +301,15 @@ sub process_check { if ($self->{cmd_process_retry} > $self->{cmd_process_retry_limit}) { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR Issued command max retries reached. Abandoning command attempt..."; shift @{$self->{cmd_queue}}; - $self->{cmd_process_retry} = 0; + $self->{cmd_process_retry} = 0; + $com_status = "offline"; } else { $self->{cmd_process_retry}++; } } } else { print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; } } if (scalar @{$self->{cmd_queue}}) { @@ -294,7 +317,14 @@ sub process_check { $self->{poll_process}->set($cmd); $self->{poll_process}->start(); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{poll_process}->pid() . " cmd=$cmd") if ($self->{debug}); - } + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } } } @@ -321,6 +351,13 @@ sub _get_JSON_data { # if there are device issues. } else { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_poll_queue} . ". Command discarded."); + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne "offline" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } } } } else { @@ -419,13 +456,15 @@ sub _push_JSON_data { #For background tasks, we want up to date data, ie returned within the last poll period. #recursively calling the same subroutine might be a memory or performance hog, but need to effectively #'suspend' the data push until we get valid data. - + print "db: poll_timestamp=" . $self->{poll_data_timestamp} . " poll_seconds = " . $self->{config}->{poll_seconds} . " get_tickcount=" . &main::get_tickcount() . "\n" if ($self->{debug}); if (($self->{background}) and (lc $method ne "direct")) { - if ($self->{poll_data_timestamp} + $self->{config}->{poll_seconds} < &main::get_tickcount()) { + if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()) { $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more - if ($self->{poll_data_timestamp} + 300 > &main::tickcount()) { #give up after 5 minutes of trying + if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()) { #give up after 5 minutes of trying + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING: retrying command attempt due to stale poll data!" ); $self->_push_json_data($type,$params); } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR: Abandoning command attempt due to stale poll data!" ); return ('1'); } } @@ -609,8 +648,8 @@ sub _push_JSON_data { my ( $newmode, $newfan, $newcoolsp, $newheatsp ); my ($mode) = $params =~ /mode=(\d+)/; my ($fan) = $params =~ /fan=(\d+)/; - my ($heattemp) = $params =~ /heattemp=(\d+)/; #need decimals - my ($cooltemp) = $params =~ /cooltemp=(\d+)/; + my ($heattemp) = $params =~ /heattemp=(\d+\.?\d?)/; #need decimals + my ($cooltemp) = $params =~ /cooltemp=(\d+\.?\d?)/; my ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool); @@ -693,7 +732,8 @@ sub _push_JSON_data { main::print_log("Unknown mode!"); return ( '1', 'error' ); } - + my $isSuccessResponse; + my $response; if (($self->{background}) and (lc $method ne "direct")) { $isSuccessResponse = 1; #set these to successful, since the process_data will indicate if a setting was unsuccessful. $response = "success"; @@ -728,7 +768,7 @@ sub _push_JSON_data { my $responseCode = $responseObj->code; print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; + $isSuccessResponse = $responseCode < 400; if ( !$isSuccessResponse ) { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to push data. Response code $responseCode" ); print "Venstar. status=" . $self->{status}; @@ -758,12 +798,11 @@ if (defined $self->{child_object}->{comm}) { $self->{child_object}->{comm}->set( "online", 'poll' ); } } - } - #my $response = JSON::XS->new->decode ($responseObj->content); + ($response) = $responseObj->content =~ /\{\"(.*)\":/; + } #print Dumper $response if $self->{debug}; - my ($response) = $responseObj->content =~ /\{\"(.*)\":/; print "response=$response\n" if $self->{debug}; $self->poll if ( $response eq "success" ); $self->start_timer; @@ -2105,6 +2144,8 @@ sub new { bless $self, $class; $$self{master_object} = $object; + push( @{ $$self{states} }, '0','5','10','15','20','25','30','35','40' ); + $object->register( $self, 'hum_sp' ); $self->set( $object->get_sp_hum, 'poll' ); @@ -2232,12 +2273,21 @@ package Venstar_Colortouch_Heat_sp; @Venstar_Colortouch_Heat_sp::ISA = ('Generic_Item'); sub new { - my ( $class, $object ) = @_; + my ( $class, $object, $scale ) = @_; my $self = {}; bless $self, $class; $$self{master_object} = $object; + if (lc $scale eq "F") { + push( @{ $$self{states} }, '65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); + $self->{lower_limit} = 65; + $self->{upper_limit} = 80; + } else { + push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; + } $object->register( $self, 'heat_sp' ); $self->set( $object->get_sp_heat, 'poll' ); @@ -2253,7 +2303,11 @@ sub set { } else { if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { - $$self{master_object}->set_heat_sp($p_state); + if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { + $$self{master_object}->set_heat_sp($p_state); + } else { + main::print_log("[Venstar Colortouch Heat_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); + } } else { main::print_log( @@ -2268,12 +2322,21 @@ package Venstar_Colortouch_Cool_sp; @Venstar_Colortouch_Cool_sp::ISA = ('Generic_Item'); sub new { - my ( $class, $object ) = @_; + my ( $class, $object, $scale ) = @_; my $self = {}; bless $self, $class; $$self{master_object} = $object; + if (lc $scale eq "F") { + push( @{ $$self{states} }, '58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); + $self->{lower_limit} = 58; + $self->{upper_limit} = 80; + } else { + push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; + } $object->register( $self, 'cool_sp' ); $self->set( $object->get_sp_cool, 'poll' ); @@ -2289,12 +2352,14 @@ sub set { } else { if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { - $$self{master_object}->set_cool_sp($p_state); + if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { + $$self{master_object}->set_cool_sp($p_state); + } else { + main::print_log("[Venstar Colortouch Cool_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); + } } else { - main::print_log( - "[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state" - ); + main::print_log("[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state"); } } } From 1a2dafc4c1d52e2fac59b53d04e37d5a50cfcb80 Mon Sep 17 00:00:00 2001 From: H Plato Date: Tue, 18 Oct 2016 20:34:43 -0600 Subject: [PATCH 13/14] v2.0.11 - working hybrid model --- lib/Venstar_Colortouch.pm | 1258 ++++++++++++++++++++++++++++--------- 1 file changed, 961 insertions(+), 297 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index b0e80a1e2..0a4795712 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -1,4 +1,6 @@ package Venstar_Colortouch; +# v2.0.11 +# background > 2 for polling use strict; use warnings; @@ -8,16 +10,20 @@ use HTTP::Request::Common qw(POST); use JSON::XS; use Data::Dumper; -# Venstar::Colortouch -# $stat_upper = new Venstar_Colortouch('192.168.0.100'); +# Venstar::Colortouch Objects +# $stat_upper = new Venstar_Colortouch('192.168.0.100'); # -# $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); -# $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); -# $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); -# $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); -# $stat_upper_sched = new Venstar_Colortouch_Sched($stat_upper); -# $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); - +# $stat_upper_mode = new Venstar_Colortouch_Mode($stat_upper); +# $stat_upper_temp = new Venstar_Colortouch_Temp($stat_upper); +# $stat_upper_heat_sp = new Venstar_Colortouch_Heat_sp($stat_upper,"F"); #F for Fahrenheit +# $stat_upper_cool_sp = new Venstar_Colortouch_Cool_sp($stat_upper); +# $stat_upper_fan = new Venstar_Colortouch_Fan($stat_upper); +# $stat_upper_hum = new Venstar_Colortouch_Humidity($stat_upper); +# $stat_upper_hum_sp = new Venstar_Colortouch_Humidity_sp($stat_upper); +# $stat_upper_sched = new Venstar_Colortouch_Schedule($stat_upper); +# $stat_upper_comm = new Venstar_Colortouch_Comm($stat_upper); + +# Version History # v1.1 - added in schedule and humidity control. # v1.2 - added communication tracker object & timeout control # v1.3 - added check for timer defined @@ -25,21 +31,27 @@ use Data::Dumper; # "hum_setpoint": is the current humidity and # "dehum_setpoint": is the humidity setpoint # "hum" doesn't return anything anymore. +# v1.4.1 - API v5, working schedule, humidity setpoints +# v2.0 - Background process -# Notes: +# Notes +# - State can only be set by stat. Set mode will change the mode. # - Best to use firmware at least 3.14 released Nov 2014. This fixes issues with both # schedule and humidity/dehumidify control. -# - 4.08 is API5, seems to have better wifi, but humidity control is messed up. +# - 4.08 brings API5, and a workaround to a humidity bug. -#todo -# - temp setpoint bounds checking +# Issues +#2 # - log runtimes. Maybe into a dbm file? log_runtimes method with destination. # - figure out timezone w/ DST -# - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints -# - add decimals for setpoints -# # make the data poll non-blocking, turn off timer -# -# State can only be set by stat. Set mode will change the mode. +#1 +# - TEST temp setpoint bounds checking +# - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints? +# - add in communication tracker for background requests +# - verify command sets work both with existing poll data, and have a poll data gap. +# - verify that network issues don't escalate CPU usage (by _push_json_data being continually called) +# - add in the commercial stat fields +# - incorporate Steve's approach for more efficient detection of changed values. @Venstar_Colortouch::ISA = ('Generic_Item'); @@ -52,35 +64,52 @@ $rest{api} = ""; $rest{sensors} = "query/sensors"; $rest{runtimes} = "query/runtimes"; $rest{alerts} = "query/alerts"; -$rest{control} = "/control"; -$rest{settings} = "/settings"; +$rest{control} = "control"; +$rest{settings} = "settings"; + sub new { - my ( $class, $host, $poll ) = @_; + my ( $class, $host, $poll, $debug ) = @_; my $self = {}; bless $self, $class; - $self->{data} = undef; - $self->{api_ver} = 0; - $self->{child_object} = undef; - $self->{config}->{cache_time} = 30; #TODO fix cache timeouts - $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} - if defined $::config_params{venstar_config_cache_time}; - $self->{config}->{tz} = - $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes - $self->{config}->{poll_seconds} = 60; + $self->{data} = undef; + $self->{api_ver} = 0; + $self->{child_object} = undef; + $self->{config}->{cache_time} = 30; #is this still necessary? + $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} if defined $::config_params{venstar_config_cache_time}; + $self->{config}->{tz} = $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes + $self->{config}->{poll_seconds} = 10; $self->{config}->{poll_seconds} = $poll if ($poll); - $self->{config}->{poll_seconds} = 1 - if ( $self->{config}->{poll_seconds} < 1 ); - $self->{updating} = 0; - $self->{data}->{retry} = 0; - $self->{host} = $host; - $self->{debug} = 5; - $self->{loglevel} = 1; - $self->{status} = ""; - $self->{timeout} = 15; #300; - + $self->{config}->{poll_seconds} = 1 if ( $self->{config}->{poll_seconds} < 1 ); + $self->{updating} = 0; + $self->{data}->{retry} = 0; + $self->{host} = $host; + $self->{debug} = 5; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 if ( $self->{debug} < 0 ); + $self->{loglevel} = 1; + $self->{status} = ""; + $self->{timeout} = 15; #300; + $self->{background} = 1; #0 for direct, 1 for set commands, 2 for poll and set commands + $self->{poll_data_timestamp} = 0; + $self->{max_poll_queue} = 3; + $self->{max_cmd_queue} = 5; + $self->{cmd_process_retry_limit}= 6; + if ($self->{background}) { + @{$self->{poll_queue}} = (); + $self->{poll_data_file} = "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; + $self->{poll_process} = new Process_Item; + $self->{poll_process}->set_output($self->{poll_data_file}); + @{$self->{cmd_queue}} = (); + $self->{cmd_data_file} = "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; + $self->{cmd_process} = new Process_Item; + $self->{cmd_process}->set_output($self->{cmd_data_file}); + &::MainLoop_post_add_hook( \&Venstar_Colortouch::process_check, 0, $self ); + } + $self->{timer} = new Timer; $self->_init; - $self->{timer} = new Timer; $self->start_timer; return $self; } @@ -88,7 +117,7 @@ sub new { sub _poll_check { my ($self) = @_; - #main::print_log("[Venstar Colortouch] _poll_check initiated"); + main::print_log("[Venstar Colortouch] _poll_check initiated"); #main::run (sub {&Venstar_Colortouch::get_data($self)}); #spawn this off to run in the background $self->get_data(); } @@ -96,9 +125,9 @@ sub _poll_check { sub get_data { my ($self) = @_; - #main::print_log("[Venstar Colortouch] get_data initiated"); + main::print_log("[Venstar Colortouch] get_data initiated"); $self->poll; - $self->process_data; + $self->process_data unless ($self->{background} > 1); #for background tasks, data will be processed when process completed. } sub _init { @@ -109,69 +138,70 @@ sub _init { $state[2] = "cooling"; $state[3] = "lockout"; $state[4] = "error"; - my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api'); + my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api',"direct"); if ($isSuccessResponse1) { $self->{api_ver} = $stat->{api_ver}; - if ( ( $self->{api_ver} > 3 ) and ( $stat->{type} eq "residential" ) ) { - main::print_log( - "[Venstar Colortouch] Residental Venstar ColorTouch found with api level $self->{api_ver}" - ); - if ( $self->poll() ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Data Successfully Retrieved" ); + if ( ( $stat->{api_ver} > 3 ) and ( $stat->{type} eq "residential" or $stat->{type} eq "commercial" ) ) { + + $self->{type} = $stat->{type}; + + main::print_log("[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}"); + if ( $self->poll("direct") ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Data Successfully Retrieved" ); $self->{active} = 1; $self->{previous}->{tempunits} = $self->{data}->{tempunits}; $self->{previous}->{name} = $self->{data}->{name}; foreach my $key1 ( keys $self->{data}->{info} ) { - $self->{previous}->{info}->{$key1} = - $self->{data}->{info}->{$key1}; + $self->{previous}->{info}->{$key1} = $self->{data}->{info}->{$key1}; } - $self->{previous}->{sensors}->{sensors}[0]->{temp} = - $self->{data}->{sensors}->{sensors}[0]->{temp}; - $self->{previous}->{sensors}->{sensors}[0]->{hum} = - $self->{data}->{sensors}->{sensors}[0]->{hum}; + $self->{previous}->{sensors}->{sensors}[0]->{temp} = $self->{data}->{sensors}->{sensors}[0]->{temp}; + $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; ## set states based on available mode +### Strange, if this set is here, then the timer is not defined. + #print "db: set " . $state[ $self->{data}->{info}->{state} ] . "=" . $self->{data}->{info}->{state} . "\n"; + #$self->set( $state[ $self->{data}->{info}->{state} ], 'poll' ); $self->print_info(); - $self->set( $state[ $self->{data}->{info}->{state} ], 'poll' ); } else { - main::print_log( - "[Venstar Colortouch] Problem retrieving initial data"); + main::print_log("[Venstar Colortouch] Problem retrieving initial data"); $self->{active} = 0; return ('1'); } } else { - main::print_log( - "[Venstar Colortouch] Unknown device " . $self->{host} ); + main::print_log("[Venstar Colortouch] Unknown device " . $self->{host} ); $self->{active} = 0; return ('1'); } } else { - main::print_log( "[Venstar Colortouch] Error. Unable to connect to " - . $self->{host} ); + main::print_log( "[Venstar Colortouch] Error. Unable to connect to " . $self->{host} ); $self->{active} = 0; return ('1'); } } sub poll { - my ($self) = @_; - - main::print_log("[Venstar Colortouch] Polling initiated") - if ( $self->{debug} ); - - my ( $isSuccessResponse1, $info ) = $self->_get_JSON_data('info'); - my ( $isSuccessResponse2, $sensors ) = $self->_get_JSON_data('sensors'); - - if ( $isSuccessResponse1 and $isSuccessResponse2 ) { + my ($self,$method) = @_; + $method = "" unless (defined $method); + if (($self->{background} > 1) and (lc $method ne "direct")) { + main::print_log("[Venstar Colortouch] Background Polling initiated") if ( $self->{debug} ); + } else { + main::print_log("[Venstar Colortouch] Direct Polling initiated") if ( $self->{debug} ); + } + #spawn off info. + #might had to add into the main loop to check if the object's data has changed. + #$self->{process_pid}->{{$self->{process}->pid} = "poll_info"; + my ( $isSuccessResponse1, $info ) = $self->_get_JSON_data('info',$method); + my ( $isSuccessResponse2, $sensors ) = $self->_get_JSON_data('sensors',$method); + + if (( $isSuccessResponse1 and $isSuccessResponse2 ) and (($self->{background} < 2) or (lc $method eq "direct"))) { + $self->{poll_data_timestamp} = &main::get_tickcount(); $self->{data}->{tempunits} = $info->{tempunits}; $self->{data}->{name} = $info->{name}; $self->{data}->{info} = $info; @@ -179,110 +209,234 @@ sub poll { $self->{data}->{timestamp} = time; $self->{data}->{retry} = 0; - #if (defined $self->{child_object}->{comm}) { - # if ($self->{child_object}->{comm}->state() ne "online") { - # main::print_log "[Venstar Colortouch:". $self->{data}->{name} . "]] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to online..." if ($self->{loglevel}); - # $self->{child_object}->{comm}->set("online",'poll'); - # } - #} return ('1'); - - #} else { - # main::print_log("[Venstar Colortouch:". $self->{data}->{name} . "] Problem retrieving poll data from " . $self->{host}); - # $self->{data}->{retry}++; - # if (defined $self->{child_object}->{comm}) { - # if ($self->{child_object}->{comm}->state() ne "offline") { - # main::print_log "[Venstar Colortouch:". $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ($self->{loglevel}); - # $self->{child_object}->{comm}->set("offline",'poll'); - # } - # } - # return ('0'); } } -#------------------------------------------------------------------------------------ -sub _get_JSON_data { - my ( $self, $mode ) = @_; - - unless ( $self->{updating} ) { - - $self->{updating} = 1; - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); - - my $host = $self->{host}; - - my $request = HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); - $request->content_type("application/x-www-form-urlencoded"); - - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" - if $self->{debug}; - - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; - $self->{updating} = 0; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, failed to get data. Response code $responseCode" - ); - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to offline..." - if ( $self->{loglevel} ); - $self->{status} = "offline"; - $self->{child_object}->{comm}->set( "offline", 'poll' ); - } - } - return ('0'); - } - else { - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "offline" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to online..." - if ( $self->{loglevel} ); - $self->{status} = "online"; - $self->{child_object}->{comm}->set( "online", 'poll' ); - } - } - } - my $response; - eval { $response = JSON::XS->new->decode( $responseObj->content ); }; - +sub process_check { + my ($self) = @_; +#Need to catch error 500's 403's and update communication tracker and success:false messages to write to log +# as a safety measure, just check that the timer's active + if ( defined $self->{timer} ) { + if ($self->{timer}->inactive()) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] INFO, timer is not active. ") if (($self->{debug}) or ($self->{loglevel} > 2)); + #$self->start_timer(); + } + } else { +# main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! timer is not defined!"); + } +# $self->start_timer if ($self->{timer}->inactive()); + return unless (defined $self->{poll_process}); + if ($self->{poll_process}->done_now()) { + my $com_status = "online"; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background poll " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); + + my $file_data = &main::file_read($self->{poll_data_file}); + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + print "debug: file_data=$file_data\n" if ($self->{debug}); + my ($json_data) = $file_data =~ /({.*})/; + print "debug: json_data=$json_data\n" if ($self->{debug}); + unless ($file_data) { + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by poll"); + return; + } + my $data; + eval { $data = JSON::XS->new->decode( $json_data ); }; # catch crashes: if ($@) { - print "[Venstar Colortouch:" - . $self->{data}->{name} - . "] ERROR! JSON parser crashed! $@\n"; - return ('0'); + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + $com_status = "offline"; + } else { + if (keys $data) { + $self->{poll_data_timestamp} = &main::get_tickcount(); + if ($self->{poll_process_mode} eq "info") { + $self->{data}->{tempunits} = $data->{tempunits}; + $self->{data}->{name} = $data->{name}; + $self->{data}->{info} = $data; + $self->{data}->{timestamp} = &main::get_tickcount(); + } elsif ($self->{poll_process_mode} eq "sensors") { + $self->{data}->{sensors} = $data; + $self->{data}->{timestamp} = &main::get_tickcount(); + } + $self->process_data(); + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; + } } - else { - return ( $isSuccessResponse, $response ); + if (scalar @{$self->{poll_queue}}) { + my $cmd_string = shift @{$self->{poll_queue}}; + my ($mode,$cmd) = split/\|/,$cmd_string; + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Poll Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); + + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } + } + return unless (defined $self->{cmd_process}); + if ($self->{cmd_process}->done_now()) { + my $com_status = "online"; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background Command " . $self->{cmd_process_name} . " process completed") if ($self->{debug}); + + my $file_data = &main::file_read($self->{cmd_data_file}); + unless ($file_data) { + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by command"); + return; + } + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + my ($json_data) = $file_data =~ /({.*})/; + my $data; + eval { $data = JSON::XS->new->decode( $json_data ); }; + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + $com_status = "offline"; + + } else { + if (keys $data) { + if ($data->{success} eq "true") { + shift @{$self->{cmd_queue}}; #remove the command from queue since it was successful + $self->{cmd_process_retry} = 0; + $self->poll; + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING Issued command was unsuccessful (success=" . $data->{success} .") , retrying..."; + if ($self->{cmd_process_retry} > $self->{cmd_process_retry_limit}) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR Issued command max retries reached. Abandoning command attempt..."; + shift @{$self->{cmd_queue}}; + $self->{cmd_process_retry} = 0; + $com_status = "offline"; + } else { + $self->{cmd_process_retry}++; + } + } + } else { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; + } } - } - else { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, not fetching data due to operation in progress" ); - return ('0'); - } + if (scalar @{$self->{cmd_queue}}) { + my $cmd = @{$self->{cmd_queue}}[0]; #grab the first command, but don't take it off. + $self->{cmd_process}->set($cmd); + $self->{cmd_process}->start(); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{cmd_process}->pid() . " cmd=$cmd") if ($self->{debug}); + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } + } + +} + +#------------------------------------------------------------------------------------ +sub _get_JSON_data { + my ( $self, $mode, $method ) = @_; + + if (($self->{background} > 1) and (lc $method ne "direct")) { + + my $cmd = 'get_url "http://' . $self->{host} . "/$rest{$mode}" . '"'; + if ($self->{poll_process}->done()) { + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{poll_process}->pid() . " command $mode, $cmd") if ($self->{debug}); + + } else { + if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{poll_queue}} . ". Queing command $mode, $cmd") if ($self->{debug}); + push @{$self->{poll_queue}}, "$mode|$cmd"; +#TODO: queue shouldn't grow for polling. Since a poll is 2 queries, the queue should only be 3 items. Otherwise it will grow every poll +# if there are device issues. + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_poll_queue} . ". Command discarded."); + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne "offline" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + } + } + } else { + unless ( $self->{updating} ) { + + $self->{updating} = 1; + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); + + my $host = $self->{host}; + + my $request = HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); + $request->content_type("application/x-www-form-urlencoded"); + + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + my $isSuccessResponse = $responseCode < 400; + $self->{updating} = 0; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to get data. Response code $responseCode"); +print "Venstar. status=" . $self->{status}; +if (defined $self->{child_object}->{comm}) { + print " Tracker defined\n"; +} else { + print " Tracker UNDEFINED\n"; +} + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "online" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + return ('0'); + } else { + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "offline" ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to online..." if ( $self->{loglevel} ); + $self->{status} = "online"; + $self->{child_object}->{comm}->set( "online", 'poll' ); + } + } + } + my $response; + eval { $response = JSON::XS->new->decode( $responseObj->content ); }; + + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON parser crashed! $@\n"; + return ('0'); + } else { + return ( $isSuccessResponse, $response ); + } + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, not fetching data due to operation in progress" ); + return ('0'); + } + } } sub _push_JSON_data { - my ( $self, $type, $params ) = @_; + my ( $self, $type, $params, $method ) = @_; - my ( @fan, @fanstate, @modename, @statename, @schedule, @home ); + my ( @fan, @fanstate, @modename, @statename, @schedule, @home, @schedulestat ); $fan[0] = "auto"; $fan[1] = "on"; $fanstate[0] = "off"; @@ -303,43 +457,119 @@ sub _push_JSON_data { $schedule[2] = "evening (occupied3)"; $schedule[3] = "night (occupied4)"; $schedule[255] = "inactive"; + $schedulestat[0] = "off"; + $schedulestat[1] = "on"; + +#4.08, schedulepart is now the schedule type. schedule 0 is off, my $cmd; + $method = "" unless (defined $method); + +#For background tasks, we want up to date data, ie returned within the last poll period. +#recursively calling the same subroutine might be a memory or performance hog, but need to effectively +#'suspend' the data push until we get valid data. + print "venstar, command: poll_timestamp=" . $self->{poll_data_timestamp} . " poll_seconds = " . $self->{config}->{poll_seconds} . " get_tickcount=" . &main::get_tickcount() . "\n";# if ($self->{debug}); + print "venstar, command: issuing poll due to old data\n" if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()); + print "venstar, command: retrying command\n" if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()); + + if (($self->{background} > 1) and (lc $method ne "direct")) { + if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()) { + $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more + if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()) { #give up after 5 minutes of trying + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING: retrying command attempt due to stale poll data!" ); + &Venstar_Colortouch::_push_json_data($self,$type,$params); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR: Abandoning command attempt due to stale poll data!" ); + return ('1'); + } + } + } + #print "VCT DB: $params\n"; - $self->stop_timer; #stop timer to prevent a clash of updates + $self->stop_timer unless ($self->{background} > 1); #stop timer to prevent a clash of updates if ( $type eq 'settings' ) { - #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 - # with v4.08 firmware, hum and dehum setpoints need to be at least 4 % points different - - my ( $newunits, $newaway, $newsched, $newhumsp, $newdehumsp ); + #for testing purposes, curl is: + #curl --data "tempunits=1&away=0&schedule=0&hum_setpoint=30&dehum_setpoint=35" http://ip/settings + +# my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; +# my %info = %$thedata; +# my %cinfo = %$thedata; +# my @changearr; +# +# while ($params =~ /(\w+)=(\d+)/g) { +# print "[Venstar Colortouch:" +# . $self->{data}->{name} +# . "] _push_JSON_data match: $1 = $2\n"; +# $info{$1} = $2; +# if ($info{$1} ne $cinfo{$1}) { +# main::print_log( "[Venstar Colortouch:" +# . $self->{data}->{name} +# . "] Changing $1 from $cinfo{$1} to $info{$1}" ); +# push(@changearr, "$1=$info{$1}"); +# } +# } + +# $cmd = join ('&', @changearr); +# main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd; # if $self->{debug}; + + my ( $newunits, $newaway, $newholiday, $newsched, $newhumsp, $newdehumsp ); my ($units) = $params =~ /tempunits=(\d+)/; my ($away) = $params =~ /away=(\d+)/; + my ($holiday) = $params =~ /holiday=(\d+)/; + my ($override) = $params =~ /override=(\d+)/; + my ($overridetime) = $params =~ /overridetime=(\d+)/; + my ($forceunocc) = $params =~ /forceunocc=(\d+)/; my ($sched) = $params =~ /schedule=(\d+)/; my $hum; my ($humsp) = $params =~ /hum_setpoint=(\d+)/; - my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/ - ; #need to add in dehumidifier stuff at some point + my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/; #need to add in dehumidifier stuff at some point my $humidity_change = 0; - my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, - $cdehumsp ) - = $self->_get_setting_params; + my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ); + my ($choliday,$coverride,$coverridetime,$cforceunocc); + + + if (($self->{background} > 1) and (lc $method ne "direct")) { + $cunits = $self->{data}->{info}->{tempunits}; + $caway = $self->{data}->{info}->{away}; + $csched = $self->{data}->{info}->{schedule}; + $chumsp = $self->{data}->{info}->{hum_setpoint}; + $cdehumsp = $self->{data}->{info}->{dehum_setpoint}; + } else { + ($isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; + #($choliday,$coverride,$coverridetime,$cforceunocc); + } $units = $cunits if ( not defined $units ); $units = 1 if ( ( $units eq "C" ) or ( $units eq "c" ) ); $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); $away = $caway if ( not defined $away ); - $sched = $csched if ( not defined $sched ); + $holiday = $choliday if ( not defined $holiday ); + $override = $coverride if ( not defined $override ); + $overridetime = $coverridetime if ( not defined $overridetime ); + $forceunocc = $cforceunocc if ( not defined $forceunocc ); + $sched = $csched if ( not defined $sched ); $hum = $chum if ( not defined $hum ); - $humsp = $chumsp if ( not defined $humsp ); - $dehumsp = $cdehumsp if ( not defined $dehumsp ); + #v4.08, dehum_sp is humidify, and hum_sp is dehumidify. + if ($self->{api_ver} >=5) { + $humsp = $cdehumsp if ( not defined $humsp ); + } else { + $humsp = $chumsp if ( not defined $humsp ); + } + if ($self->{api_ver} >=5) { + $dehumsp = $chumsp if ( not defined $dehumsp ); + } else { + $dehumsp = $cdehumsp if ( not defined $dehumsp ); + } +#print "venstar db: params = $params\n"; +#print "units=$units, away=$away, sched=$sched, humsp=$humsp, dehumsp=$dehumsp\n"; +#print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; + if ( $cunits ne $units ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Units from $cunits to $units" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Units from $cunits to $units" ); $newunits = $units; } else { @@ -347,58 +577,80 @@ sub _push_JSON_data { } if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Away from $home[$caway] to $home[$away]" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $home[$caway] to $home[$away]" ); $newaway = $away; } else { $newaway = $caway; } - if ( $csched ne $sched ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]" - ); - $newsched = $sched; + if ( ($self->{type} eq "commercial") and ($choliday ne $holiday) ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $choliday to $holiday" ); + $newholiday = $holiday; } else { - $newsched = $csched; + $newholiday = $choliday; } - if ( $chumsp ne $humsp ) { - print - "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing Humidity Setpoint from $chumsp to $humsp\n"; - $newhumsp = $humsp; - $humidity_change = 1; + if ( $caway ne $away ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $caway to $away" ); + $newaway = $away; } else { - $newhumsp = $chumsp; + $newaway = $caway; } - if ( $cdehumsp ne $dehumsp ) { - print - "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing (De)humidity Setpoint from $cdehumsp to $dehumsp\n"; - $newdehumsp = $dehumsp; + if ( $csched ne $sched ) { + if ($self->{api_ver} >=5) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedulestat[$csched] to $schedulestat[$sched]"); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]"); + } + $newsched = $sched; } else { - $newdehumsp = $cdehumsp; + $newsched = $csched; + } + if ($self->{api_ver} >=5) { + if ( $cdehumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $cdehumsp to $humsp\n" ); + $newhumsp = $humsp; + $humidity_change = 1; + } else { + $newhumsp = $cdehumsp; + } + + if ( $chumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] *Changing Dehumidity Setpoint from $chumsp to $dehumsp\n" ); + $newdehumsp = $dehumsp; + } else { + $newdehumsp = $chumsp; + } + } else { + if ( $chumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $chumsp to $humsp\n" ); + $newhumsp = $humsp; + $humidity_change = 1; + } else { + $newhumsp = $chumsp; + } + + if ( $cdehumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Dehumidity Setpoint from $cdehumsp to $dehumsp\n" ); + $newdehumsp = $dehumsp; + } else { + $newdehumsp = $cdehumsp; + } } - # v4.08 needs 6 % point delta on humidity and dehumidity. - # if humidify is more then dehumidify, then set dehumidify to 6 + humidity - $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); +## to set v4.08, humidity to 32%, this is needed "tempunits=1&away=0&schedule=0&hum_setpoint=32&dehum_setpoint=38" +## so humidity setpoint hasn't changed? if ($self->{api_ver} >=5) { - $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched"; #&dehum_setpoint=$newhumsp&hum_setpoint=" . ($newhumsp + 1); - $cmd = "hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp" if ($humidity_change); - } else { - $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; - } + # v4.08 has changed humidification settings + # - need 6 % point delta on humidity and dehumidity. + $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); + } + $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; main::print_log( "Sending Settings command [$cmd] to " . $self->{host} ) if $self->{debug}; } @@ -411,32 +663,34 @@ sub _push_JSON_data { my ( $newmode, $newfan, $newcoolsp, $newheatsp ); my ($mode) = $params =~ /mode=(\d+)/; my ($fan) = $params =~ /fan=(\d+)/; - my ($heattemp) = $params =~ /heattemp=(\d+)/; #need decimals - my ($cooltemp) = $params =~ /cooltemp=(\d+)/; - - my ( - $isSuccessResponse, $cmode, $cfan, - $cheattemp, $ccooltemp, $setpointdelta, - $minheat, $maxheat, $mincool, - $maxcool - ) = $self->_get_control_params; + my ($heattemp) = $params =~ /heattemp=(\d+\.?\d?)/; #need decimals + my ($cooltemp) = $params =~ /cooltemp=(\d+\.?\d?)/; + + my ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool); + + if (($self->{background} > 1) and (lc $method ne "direct")) { + $cmode = $self->{data}->{info}->{mode}; + $cfan = $self->{data}->{info}->{fan}; + $cheattemp = $self->{data}->{info}->{heattemp}; + $ccooltemp = $self->{data}->{info}->{cooltemp}; + $setpointdelta = $self->{data}->{info}->{setpointdelta}; + $minheat = $self->{data}->{info}->{heattempmin}; + $maxheat = $self->{data}->{info}->{heattempmax}; + $mincool = $self->{data}->{info}->{cooltempmin}; + $maxcool = $self->{data}->{info}->{cooltempmax}; + + } else { + ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool) = $self->_get_control_params; + } $mode = $cmode if ( not defined $mode ); $fan = $cfan if ( not defined $fan ); $heattemp = $cheattemp if ( !$heattemp ); $cooltemp = $ccooltemp if ( !$cooltemp ); - main::print_log( - "data1=$isSuccessResponse,$cmode,$cfan,$cheattemp,$ccooltemp,$setpointdelta" - ) if $self->{debug}; #TODO pass object to get debug - main::print_log("data2=$mode,$fan,$heattemp,$cooltemp") - if $self->{debug}; #TODO debug if ( $cmode ne $mode ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing mode from $modename[$cmode] to $modename[$mode]" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing mode from $modename[$cmode] to $modename[$mode]"); $newmode = $mode; } else { @@ -444,9 +698,7 @@ sub _push_JSON_data { } if ( $cfan ne $fan ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing fan from $fanstate[$cfan] to $fanstate[$fan]" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); $newfan = $fan; } else { @@ -454,9 +706,7 @@ sub _push_JSON_data { } if ( $heattemp ne $cheattemp ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing heat setpoint from $cheattemp to $heattemp" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing heat setpoint from $cheattemp to $heattemp" ); $newheatsp = $heattemp; } else { @@ -464,9 +714,7 @@ sub _push_JSON_data { } if ( $cooltemp ne $ccooltemp ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); $newcoolsp = $cooltemp; } else { @@ -474,35 +722,23 @@ sub _push_JSON_data { } if ( ( $newcoolsp > $maxcool ) or ( $newcoolsp < $mincool ) ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool"); $newcoolsp = $ccooltemp; } if ( ( $newheatsp > $maxheat ) or ( $newheatsp < $minheat ) ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat" - ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat"); $newheatsp = $cheattemp; } if ( ( $newheatsp - $newcoolsp ) > $setpointdelta ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta" - ); - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Not setting setpoints" ); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta"); + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Not setting setpoints" ); $newcoolsp = $ccooltemp; $newheatsp = $cheattemp; } - $cmd = - "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; + $cmd = "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; main::print_log( "Sending Control command $cmd to " . $self->{host} ) if $self->{debug}; } @@ -511,34 +747,55 @@ sub _push_JSON_data { main::print_log("Unknown mode!"); return ( '1', 'error' ); } + my $isSuccessResponse; + my $response; + if (($self->{background}) and (lc $method ne "direct")) { + $isSuccessResponse = 1; #set these to successful, since the process_data will indicate if a setting was unsuccessful. + $response = "success"; + my $cmd = 'get_url -post "' .$cmd . '" "http://' . $self->{host} . "/$rest{$type}" . '"'; + push @{$self->{cmd_queue}}, "$cmd"; + if ($self->{cmd_process}->done()) { + $self->{cmd_process}->set($cmd); + $self->{cmd_process_name} = $cmd; + $self->{cmd_process}->start(); + $self->{cmd_process_retry} = 0; + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{cmd_process}->pid() . " command $cmd") if ($self->{debug}); - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); - - my $host = $self->{host}; - - my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); - $request->content_type("application/x-www-form-urlencoded"); - $request->content($cmd) if $cmd; - - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" if $self->{debug}; - - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Warning, failed to push data. Response code $responseCode" ); + } else { + if (scalar @{$self->{cmd_queue}} < $self->{max_cmd_queue}) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{cmd_queue}} . ". Queing command $cmd") if ($self->{debug}); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_cmd_queue} . ". Command discarded."); + } + } + } else { + + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); + + my $host = $self->{host}; + + my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); + $request->content_type("application/x-www-form-urlencoded"); + $request->content($cmd) if $cmd; + + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + $isSuccessResponse = $responseCode < 400; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to push data. Response code $responseCode" ); +print "Venstar. status=" . $self->{status}; +if (defined $self->{child_object}->{comm}) { + print " Tracker defined\n"; +} else { + print " Tracker UNDEFINED\n"; +} if ( defined $self->{child_object}->{comm} ) { if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" - . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to offline..." - if ( $self->{loglevel} ); + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); $self->{status} = "offline"; $self->{child_object}->{comm}->set( "offline", 'poll' ); } @@ -557,15 +814,20 @@ sub _push_JSON_data { $self->{child_object}->{comm}->set( "online", 'poll' ); } } - } - #my $response = JSON::XS->new->decode ($responseObj->content); - + ($response) = $responseObj->content =~ /\{\"(.*)\":/; + } + } #print Dumper $response if $self->{debug}; - my ($response) = $responseObj->content =~ /\{\"(.*)\":/; print "response=$response\n" if $self->{debug}; - $self->poll if ( $response eq "success" ); - $self->start_timer; + +# remove this poll since the stat pauses after being set? +# if ( $response eq "success" ) { +# $self->poll(); +# $self->process_data() unless ($self->{background} >1); +# } + $self->start_timer unless ($self->{background} > 1); + return ( $isSuccessResponse, $response ); } @@ -594,7 +856,7 @@ sub _get_control_params { sub _get_setting_params { my ($self) = @_; - my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info'); + my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info',"direct"); return ( $isSuccessResponse, $info->{tempunits}, $info->{away}, $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, $info->{dehum_setpoint} ); @@ -615,7 +877,6 @@ sub stop_timer { sub start_timer { my ($self) = @_; - if ( defined $self->{timer} ) { $self->{timer}->set( $self->{config}->{poll_seconds}, sub { &Venstar_Colortouch::_poll_check($self) }, -1 ); @@ -627,6 +888,19 @@ sub start_timer { } } +sub background_enable { + my ($self,$level) = @_; + $level = 1 unless (defined $level); + $self->{background} = $level; + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode enabled (level " . $level . ")"); +} + +sub background_disable { + my ($self) = @_; + $self->{background} = 0; + main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode disabled. Now in direct mode."); +} + sub print_info { my ($self) = @_; @@ -672,6 +946,16 @@ sub print_info { . $type . " Thermostat with API level " . $self->{api_ver} ); + if ($self->{background}) { + if ($self->{background} > 1) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode enabled"); + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Hybrid mode enabled (Background commands, direct polling)"); + } + } else { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Direct mode enabled"); + } + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Fan mode is set to " @@ -792,6 +1076,8 @@ sub process_data { # set state of self for state # for any registered child selfs, update their state if + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Processing Data...") if ($self->{debug}); + my ( @fan, @fanstate, @mode, @state, @schedule ); $fan[0] = "auto"; $fan[1] = "on"; @@ -888,6 +1174,13 @@ sub process_data { . "] Mode changed from $mode[$self->{previous}->{info}->{mode}] to $mode[$self->{data}->{info}->{mode}]" ) if ( $self->{loglevel} ); $self->{previous}->{info}->{mode} = $self->{data}->{info}->{mode}; + if ( defined $self->{child_object}->{mode} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Mode Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{mode} + ->set( $mode[$self->{data}->{info}->{mode}] ); + } + } if ( $self->{previous}->{info}->{state} != $self->{data}->{info}->{state} ) @@ -935,17 +1228,59 @@ sub process_data { $self->{data}->{info}->{schedulepart}; } - if ( $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) { + if ( $self->{type} eq "residential" and $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) { my $away = "home mode"; $away = "away mode" if ( $self->{data}->{info}->{away} ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} - . "] Thermostat occupency changed to" + . "] Thermostat occupancy changed to" . $away ) if ( $self->{loglevel} ); $self->{previous}->{info}->{away} = $self->{data}->{info}->{away}; } + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{holiday} != $self->{data}->{info}->{holiday} ) { + my $holiday = "observing holiday"; + $holiday = "no holiday" if ( $self->{data}->{info}->{holiday} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat holiday changed to" + . $holiday ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{holiday} = $self->{data}->{info}->{holiday}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{override} != $self->{data}->{info}->{override} ) { + my $override = "off"; + $override = "on" if ( $self->{data}->{info}->{override} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat override changed to" + . $override ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{override} = $self->{data}->{info}->{override}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{overridetime} != $self->{data}->{info}->{overridetime} ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat overridetime changed to" + . $self->{data}->{info}->{overridetime} ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{overridetime} = $self->{data}->{info}->{overridetime}; + } + + if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{forceunocc} != $self->{data}->{info}->{forceunocc} ) { + my $forceunocc = "off"; + $forceunocc = "on" if ( $self->{data}->{info}->{forceunocc} ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat forceunocc changed to" + . $forceunocc ) + if ( $self->{loglevel} ); + $self->{previous}->{info}->{forceunocc} = $self->{data}->{info}->{forceunocc}; + } + if ( $self->{previous}->{info}->{spacetemp} != $self->{data}->{info}->{spacetemp} ) { @@ -972,6 +1307,12 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{heattemp} = $self->{data}->{info}->{heattemp}; + if ( defined $self->{child_object}->{heat_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Heat Setpoint Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{heat_sp} + ->set( $self->{data}->{info}->{heattemp}, 'poll' ); + } } if ( $self->{previous}->{info}->{heattempmin} != @@ -1005,6 +1346,12 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{cooltemp} = $self->{data}->{info}->{cooltemp}; + if ( defined $self->{child_object}->{cool_sp} ) { + main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Cooling Setpoint Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{cool_sp} + ->set( $self->{data}->{info}->{cooltemp}, 'poll' ); + } } if ( $self->{previous}->{info}->{cooltempmin} != @@ -1058,6 +1405,10 @@ sub process_data { } } + #if ($self->{previous}->{info}->{hum} != $self->{data}->{info}->{hum}) { + # main::print_log("[Venstar Colortouch:". $self->{data}->{name} . "] Thermostat humidity changed from $self->{previous}->{info}->{hum} to $self->{data}->{info}->{hum}") if ($self->{loglevel}); + # $self->{previous}->{info}->{hum} = $self->{data}->{info}->{hum}; + #} if ( $self->{previous}->{info}->{setpointdelta} != $self->{data}->{info}->{setpointdelta} ) @@ -1289,6 +1640,11 @@ sub get_mode_humid { } +sub get_debug { + my ($self) = @_; + return $self->{debug}; +} + #------------ # User control methods #tempunits=0&away=0&schedule=0&hum_setpoint=0&dehum_setpoint=0 @@ -1480,10 +1836,175 @@ sub set_mode { } sub set_away { + my ( $self, $value ) = @_; + return unless $self->{type} eq "residential"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown away mode $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'control', "away=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set away to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} - #I don't use this. Shouldn't just be this simple though. - #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); +sub set_holiday { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown holiday $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "holiday=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set holiday to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} +sub set_override { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown override $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "override=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set override to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} + +sub set_overridetime { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "overridetime=$value" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set overridetime to $value" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } +} + +sub set_forceunocc { + my ( $self, $value ) = @_; + return unless $self->{type} eq "commercial"; + my $num; + if ( lc $value eq "off" ) { + $num = 0; + } + elsif ( lc $value eq lc "on" ) { + $num = 1; + } + else { + main::print_log( "Venstar Colortouch:" + . $self->{data}->{name} + . "] Error, unknown forceunocc mode $value" ); + return ('0'); + } + my ( $isSuccessResponse, $status ) = + $self->_push_JSON_data( 'settings', "forceunocc=$num" ); + if ($isSuccessResponse) { + if ( $status eq "success" ) { + return (1); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not set forceunocc to $num" ); + return (0); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error. Could not send data to Thermostat" ); + return (0); + } } sub set_fan { @@ -1526,6 +2047,15 @@ sub set_fan { sub set_units { + #($isSuccessResponse3,$status) = push_JSON_data($host,'settings','away=0&schedule=1'); + +} + +sub set_debug { + my ( $self, $debug ) = @_; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 + if ( $self->{debug} < 0 ); } sub set { @@ -1656,6 +2186,8 @@ sub new { bless $self, $class; $$self{master_object} = $object; + push( @{ $$self{states} }, '0','5','10','15','20','25','30','35','40' ); + $object->register( $self, 'hum_sp' ); $self->set( $object->get_sp_hum, 'poll' ); @@ -1742,4 +2274,136 @@ sub set { } } -1; +package Venstar_Colortouch_Mode; + +@Venstar_Colortouch_Mode::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + + $object->register( $self, 'mode' ); + $self->set( $object->get_mode, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( lc $p_state eq "cooling" ) or ( lc $p_state eq "heating" ) or ( lc $p_state eq "cool" ) or ( lc $p_state eq "heat" ) or ( lc $p_state eq "auto" ) or ( lc $p_state eq "off" ) ) { + $$self{master_object}->set_mode($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Mode] Error. Unknown set state $p_state" + ); + } + } +} + +package Venstar_Colortouch_Heat_sp; + +@Venstar_Colortouch_Heat_sp::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object, $scale ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + if ((defined $scale) and (lc $scale eq "F")) { + push( @{ $$self{states} }, '65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); + $self->{lower_limit} = 65; + $self->{upper_limit} = 80; + } else { + push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; + } + + $object->register( $self, 'heat_sp' ); + $self->set( $object->get_sp_heat, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { + if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { + $$self{master_object}->set_heat_sp($p_state); + } else { + main::print_log("[Venstar Colortouch Heat_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); + } + } + else { + main::print_log( + "[Venstar Colortouch Heat_SP] Error. Unknown set state $p_state" + ); + } + } +} + +package Venstar_Colortouch_Cool_sp; + +@Venstar_Colortouch_Cool_sp::ISA = ('Generic_Item'); + +sub new { + my ( $class, $object, $scale ) = @_; + + my $self = {}; + bless $self, $class; + + $$self{master_object} = $object; + if ((defined $scale) and (lc $scale eq "F")) { + push( @{ $$self{states} }, '58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); + $self->{lower_limit} = 58; + $self->{upper_limit} = 80; + } else { + push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; + } + + $object->register( $self, 'cool_sp' ); + $self->set( $object->get_sp_cool, 'poll' ); + return $self; + +} + +sub set { + my ( $self, $p_state, $p_setby ) = @_; + + if ( $p_setby eq 'poll' ) { + $self->SUPER::set($p_state); + } + else { + if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { + if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { + $$self{master_object}->set_cool_sp($p_state); + } else { + main::print_log("[Venstar Colortouch Cool_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); + } + } + else { + main::print_log("[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state"); + } + } +} + +1; \ No newline at end of file From 76df6ed62ee9cfae26e7aa5c5ace9e8642b02cd1 Mon Sep 17 00:00:00 2001 From: H Plato Date: Thu, 17 Nov 2016 18:57:10 -0700 Subject: [PATCH 14/14] Uses process items to background all communication to reduce pauses --- lib/Venstar_Colortouch.pm | 1794 ++++++++++++++++++++++--------------- 1 file changed, 1065 insertions(+), 729 deletions(-) diff --git a/lib/Venstar_Colortouch.pm b/lib/Venstar_Colortouch.pm index 3d8ac175f..35d5a1d9a 100644 --- a/lib/Venstar_Colortouch.pm +++ b/lib/Venstar_Colortouch.pm @@ -1,10 +1,12 @@ package Venstar_Colortouch; -<<<<<<< HEAD -# v2.0.11 + +# v2.0.14 # background > 2 for polling -======= -# v2.0.3 ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 + +#check linesl829 l477 l493 l203 +#TODO - check that data is current before issuing command. within 2 poll periods. +# Does this even really need to happen. If the data isn't current, then polling again +# Doesn't really buy anything. use strict; use warnings; @@ -52,7 +54,7 @@ use Data::Dumper; # - TEST temp setpoint bounds checking # - changing heating/cooling setpoints for heating/cooling only stats does not need both setpoints? # - add in communication tracker for background requests -# - verify command sets work both with existing poll data, and have a poll data gap. +# - verify command sets work both with existing poll data, and have a poll data gap. # - verify that network issues don't escalate CPU usage (by _push_json_data being continually called) # - add in the commercial stat fields # - incorporate Steve's approach for more efficient detection of changed values. @@ -71,56 +73,57 @@ $rest{alerts} = "query/alerts"; $rest{control} = "control"; $rest{settings} = "settings"; - sub new { my ( $class, $host, $poll, $debug ) = @_; my $self = {}; bless $self, $class; - $self->{data} = undef; - $self->{api_ver} = 0; - $self->{child_object} = undef; - $self->{config}->{cache_time} = 30; #is this still necessary? - $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} if defined $::config_params{venstar_config_cache_time}; - $self->{config}->{tz} = $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes + $self->{data} = undef; + $self->{api_ver} = 0; + $self->{child_object} = undef; + $self->{config}->{cache_time} = 30; #is this still necessary? + $self->{config}->{cache_time} = $::config_params{venstar_config_cache_time} + if defined $::config_params{venstar_config_cache_time}; + $self->{config}->{tz} = + $::config_params{time_zone}; #TODO Need to figure out DST for print runtimes $self->{config}->{poll_seconds} = 10; $self->{config}->{poll_seconds} = $poll if ($poll); - $self->{config}->{poll_seconds} = 1 if ( $self->{config}->{poll_seconds} < 1 ); - $self->{updating} = 0; - $self->{data}->{retry} = 0; - $self->{host} = $host; -<<<<<<< HEAD - $self->{debug} = 5; -======= - $self->{debug} = 0; ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $self->{debug} = $debug if ($debug); - $self->{debug} = 0 if ( $self->{debug} < 0 ); - $self->{loglevel} = 1; - $self->{status} = ""; - $self->{timeout} = 15; #300; -<<<<<<< HEAD - $self->{background} = 1; #0 for direct, 1 for set commands, 2 for poll and set commands -======= - $self->{background} = 1; ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $self->{poll_data_timestamp} = 0; - $self->{max_poll_queue} = 3; - $self->{max_cmd_queue} = 5; - $self->{cmd_process_retry_limit}= 6; - if ($self->{background}) { - @{$self->{poll_queue}} = (); - $self->{poll_data_file} = "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; - unlink "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; - $self->{poll_process} = new Process_Item; - $self->{poll_process}->set_output($self->{poll_data_file}); - @{$self->{cmd_queue}} = (); - $self->{cmd_data_file} = "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; - unlink "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; - $self->{cmd_process} = new Process_Item; - $self->{cmd_process}->set_output($self->{cmd_data_file}); - &::MainLoop_post_add_hook( \&Venstar_Colortouch::process_check, 0, $self ); - } - $self->{timer} = new Timer; + $self->{config}->{poll_seconds} = 1 + if ( $self->{config}->{poll_seconds} < 1 ); + $self->{updating} = 0; + $self->{data}->{retry} = 0; + $self->{host} = $host; + $self->{debug} = 0; + $self->{debug} = $debug if ($debug); + $self->{debug} = 0 if ( $self->{debug} < 0 ); + $self->{loglevel} = 1; + $self->{status} = ""; + $self->{timeout} = 15; #for http direct mode; + $self->{background} = + 2; #0 for direct, 1 for set commands, 2 for poll and set commands + $self->{poll_data_timestamp} = 0; + $self->{max_poll_queue} = 3; + $self->{max_cmd_queue} = 5; + $self->{cmd_process_retry_limit} = 6; + + if ( $self->{background} ) { + @{ $self->{poll_queue} } = (); + $self->{poll_data_file} = + "$::config_parms{data_dir}/venstar_poll_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_poll_" + . $self->{host} . ".data"; + $self->{poll_process} = new Process_Item; + $self->{poll_process}->set_output( $self->{poll_data_file} ); + @{ $self->{cmd_queue} } = (); + $self->{cmd_data_file} = + "$::config_parms{data_dir}/venstar_cmd_" . $self->{host} . ".data"; + unlink "$::config_parms{data_dir}/venstar_cmd_" + . $self->{host} . ".data"; + $self->{cmd_process} = new Process_Item; + $self->{cmd_process}->set_output( $self->{cmd_data_file} ); + &::MainLoop_post_add_hook( \&Venstar_Colortouch::process_check, + 0, $self ); + } + $self->{timer} = new Timer; $self->_init; $self->start_timer; return $self; @@ -129,7 +132,9 @@ sub new { sub _poll_check { my ($self) = @_; - main::print_log("[Venstar Colortouch] _poll_check initiated"); + main::print_log("[Venstar Colortouch] _poll_check initiated") + if ( $self->{debug} ); + #main::run (sub {&Venstar_Colortouch::get_data($self)}); #spawn this off to run in the background $self->get_data(); } @@ -137,13 +142,12 @@ sub _poll_check { sub get_data { my ($self) = @_; - main::print_log("[Venstar Colortouch] get_data initiated"); + main::print_log("[Venstar Colortouch] get_data initiated") + if ( $self->{debug} ); $self->poll; -<<<<<<< HEAD - $self->process_data unless ($self->{background} > 1); #for background tasks, data will be processed when process completed. -======= - $self->process_data unless ($self->{background}); #for background tasks, data will be processed when process completed. ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 + $self->process_data + unless ( $self->{background} > 1 ) + ; #for background tasks, data will be processed when process completed. } sub _init { @@ -154,26 +158,38 @@ sub _init { $state[2] = "cooling"; $state[3] = "lockout"; $state[4] = "error"; - my ( $isSuccessResponse1, $stat ) = $self->_get_JSON_data('api',"direct"); + my ( $isSuccessResponse1, $stat ) = + $self->_get_JSON_data( 'api', "direct" ); if ($isSuccessResponse1) { - $self->{api_ver} = $stat->{api_ver}; + $self->{api_ver} = $stat->{api_ver}; - if ( ( $stat->{api_ver} > 3 ) and ( $stat->{type} eq "residential" or $stat->{type} eq "commercial" ) ) { + if ( ( $stat->{api_ver} > 3 ) + and + ( $stat->{type} eq "residential" or $stat->{type} eq "commercial" ) + ) + { $self->{type} = $stat->{type}; - main::print_log("[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}"); + main::print_log( + "[Venstar Colortouch] $stat->{type} Venstar ColorTouch found with api level $stat->{api_ver}" + ); if ( $self->poll("direct") ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Data Successfully Retrieved" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Data Successfully Retrieved" ); $self->{active} = 1; $self->{previous}->{tempunits} = $self->{data}->{tempunits}; $self->{previous}->{name} = $self->{data}->{name}; foreach my $key1 ( keys $self->{data}->{info} ) { - $self->{previous}->{info}->{$key1} = $self->{data}->{info}->{$key1}; + $self->{previous}->{info}->{$key1} = + $self->{data}->{info}->{$key1}; } - $self->{previous}->{sensors}->{sensors}[0]->{temp} = $self->{data}->{sensors}->{sensors}[0]->{temp}; - $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; + $self->{previous}->{sensors}->{sensors}[0]->{temp} = + $self->{data}->{sensors}->{sensors}[0]->{temp}; + $self->{previous}->{sensors}->{sensors}[0]->{hum} = + $self->{data}->{sensors}->{sensors}[0]->{hum}; ## set states based on available mode ### Strange, if this set is here, then the timer is not defined. #print "db: set " . $state[ $self->{data}->{info}->{state} ] . "=" . $self->{data}->{info}->{state} . "\n"; @@ -182,452 +198,554 @@ sub _init { } else { - main::print_log("[Venstar Colortouch] Problem retrieving initial data"); + main::print_log( + "[Venstar Colortouch] Problem retrieving initial data"); $self->{active} = 0; return ('1'); } } else { - main::print_log("[Venstar Colortouch] Unknown device " . $self->{host} ); + main::print_log( + "[Venstar Colortouch] Unknown device " . $self->{host} ); $self->{active} = 0; return ('1'); } } else { - main::print_log( "[Venstar Colortouch] Error. Unable to connect to " . $self->{host} ); + main::print_log( "[Venstar Colortouch] Error. Unable to connect to " + . $self->{host} ); $self->{active} = 0; return ('1'); } } sub poll { - my ($self,$method) = @_; - $method = "" unless (defined $method); -<<<<<<< HEAD - if (($self->{background} > 1) and (lc $method ne "direct")) { -======= - if (($self->{background}) and (lc $method ne "direct")) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - main::print_log("[Venstar Colortouch] Background Polling initiated") if ( $self->{debug} ); - } else { - main::print_log("[Venstar Colortouch] Direct Polling initiated") if ( $self->{debug} ); - } - #spawn off info. - #might had to add into the main loop to check if the object's data has changed. - #$self->{process_pid}->{{$self->{process}->pid} = "poll_info"; - my ( $isSuccessResponse1, $info ) = $self->_get_JSON_data('info',$method); - my ( $isSuccessResponse2, $sensors ) = $self->_get_JSON_data('sensors',$method); - -<<<<<<< HEAD - if (( $isSuccessResponse1 and $isSuccessResponse2 ) and (($self->{background} < 2) or (lc $method eq "direct"))) { - $self->{poll_data_timestamp} = &main::get_tickcount(); -======= - if (( $isSuccessResponse1 and $isSuccessResponse2 ) and ((!$self->{background}) or (lc $method eq "direct"))) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $self->{data}->{tempunits} = $info->{tempunits}; - $self->{data}->{name} = $info->{name}; - $self->{data}->{info} = $info; - $self->{data}->{sensors} = $sensors; - $self->{data}->{timestamp} = time; - $self->{data}->{retry} = 0; + my ( $self, $method ) = @_; + $method = "" unless ( defined $method ); + + if ( ( $self->{background} > 1 ) and ( $method ne "direct" ) ) { + main::print_log("[Venstar Colortouch] Background Polling initiated") + if ( $self->{debug} ); + $self->_get_JSON_data( 'info', $method ); + #$self->_get_JSON_data('sensors',$method); return ('1'); } + else { + main::print_log("[Venstar Colortouch] Direct Polling initiated") + if ( $self->{debug} ); + my ( $isSuccessResponse1, $info ) = + $self->_get_JSON_data( 'info', $method ); + my ( $isSuccessResponse2, $sensors ) = + $self->_get_JSON_data( 'sensors', $method ); + + if ( $isSuccessResponse1 and $isSuccessResponse2 ) { + $self->{poll_data_timestamp} = &main::get_tickcount(); + $self->{data}->{tempunits} = $info->{tempunits}; + $self->{data}->{name} = $info->{name}; + $self->{data}->{info} = $info; + $self->{data}->{sensors} = $sensors; + $self->{data}->{timestamp} = time; + $self->{data}->{retry} = 0; + + return ('1'); + } + } } sub process_check { - my ($self) = @_; -#Need to catch error 500's 403's and update communication tracker and success:false messages to write to log -<<<<<<< HEAD -# as a safety measure, just check that the timer's active + my ($self) = @_; + + #Need to catch error 500's 403's and update communication tracker and success:false messages to write to log + # as a safety measure, just check that the timer's active if ( defined $self->{timer} ) { - if ($self->{timer}->inactive()) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] INFO, timer is not active. ") if (($self->{debug}) or ($self->{loglevel} > 2)); - #$self->start_timer(); - } - } else { -# main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! timer is not defined!"); - } -# $self->start_timer if ($self->{timer}->inactive()); -======= ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - return unless (defined $self->{poll_process}); - if ($self->{poll_process}->done_now()) { - my $com_status = "online"; - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background poll " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); - - my $file_data = &main::file_read($self->{poll_data_file}); - #for some reason get_url adds garbage to the output. Clean out the characters before and after the json - print "debug: file_data=$file_data\n" if ($self->{debug}); - my ($json_data) = $file_data =~ /({.*})/; - print "debug: json_data=$json_data\n" if ($self->{debug}); - unless ($file_data) { - main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by poll"); - return; - } - my $data; - eval { $data = JSON::XS->new->decode( $json_data ); }; + if ( $self->{timer}->inactive() ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] INFO, timer is not active. " ) + if ( ( $self->{debug} ) or ( $self->{loglevel} > 2 ) ); + + #$self->start_timer(); + } + } + else { + # main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! timer is not defined!"); + } + + # $self->start_timer if ($self->{timer}->inactive()); + return unless ( defined $self->{poll_process} ); + if ( $self->{poll_process}->done_now() ) { + my $com_status = "online"; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Background poll " + . $self->{poll_process_mode} + . " process completed" ) + if ( $self->{debug} ); + + my $file_data = &main::file_read( $self->{poll_data_file} ); + + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + print "debug: file_data=$file_data\n" if ( $self->{debug} ); + my ($json_data) = $file_data =~ /({.*})/; + print "debug: json_data=$json_data\n" if ( $self->{debug} ); + unless ( ($file_data) and ($json_data) ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! bad data returned by poll" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! file data is $file_data. json data is $json_data" + ); + return; + } + my $data; + eval { $data = JSON::XS->new->decode($json_data); }; + # catch crashes: if ($@) { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! JSON file parser crashed! $@\n"; $com_status = "offline"; - } else { - if (keys $data) { - $self->{poll_data_timestamp} = &main::get_tickcount(); - if ($self->{poll_process_mode} eq "info") { - $self->{data}->{tempunits} = $data->{tempunits}; - $self->{data}->{name} = $data->{name}; - $self->{data}->{info} = $data; - $self->{data}->{timestamp} = &main::get_tickcount(); - } elsif ($self->{poll_process_mode} eq "sensors") { - $self->{data}->{sensors} = $data; - $self->{data}->{timestamp} = &main::get_tickcount(); - } - $self->process_data(); - } else { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; - $com_status = "offline"; - } - } - if (scalar @{$self->{poll_queue}}) { - my $cmd_string = shift @{$self->{poll_queue}}; - my ($mode,$cmd) = split/\|/,$cmd_string; - $self->{poll_process}->set($cmd); - $self->{poll_process}->start(); - $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; - $self->{poll_process_mode} = $mode; - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Poll Queue " . $self->{poll_process}->pid() . " mode=$mode cmd=$cmd") if ($self->{debug}); - - } - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} ne $com_status ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); - $self->{status} = $com_status; - $self->{child_object}->{comm}->set( $com_status, 'poll' ); - } - } - } - return unless (defined $self->{cmd_process}); - if ($self->{cmd_process}->done_now()) { - my $com_status = "online"; -<<<<<<< HEAD - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background Command " . $self->{cmd_process_name} . " process completed") if ($self->{debug}); -======= - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background Command " . $self->{poll_process_mode} . " process completed") if ($self->{debug}); ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - - my $file_data = &main::file_read($self->{cmd_data_file}); - unless ($file_data) { - main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! no data returned by command"); - return; - } - #for some reason get_url adds garbage to the output. Clean out the characters before and after the json - my ($json_data) = $file_data =~ /({.*})/; - my $data; - eval { $data = JSON::XS->new->decode( $json_data ); }; + } + else { + if ( keys $data ) { + $self->{poll_data_timestamp} = &main::get_tickcount(); + if ( $self->{poll_process_mode} eq "info" ) { + $self->{data}->{tempunits} = $data->{tempunits}; + $self->{data}->{name} = $data->{name}; + $self->{data}->{info} = $data; + $self->{data}->{timestamp} = &main::get_tickcount(); + } + elsif ( $self->{poll_process_mode} eq "sensors" ) { + $self->{data}->{sensors} = $data; + $self->{data}->{timestamp} = &main::get_tickcount(); + } + $self->process_data(); + } + else { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; + } + } + if ( scalar @{ $self->{poll_queue} } ) { + my $cmd_string = shift @{ $self->{poll_queue} }; + my ( $mode, $cmd ) = split /\|/, $cmd_string; + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{ $self->{poll_process}->pid() } = $mode + ; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Poll Queue " + . $self->{poll_process}->pid() + . " mode=$mode cmd=$cmd" ) + if ( $self->{debug} ); + + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() . " to " + . $com_status . "..." + if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } + } + return unless ( defined $self->{cmd_process} ); + if ( $self->{cmd_process}->done_now() ) { + my $com_status = "online"; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Background Command " + . $self->{cmd_process_name} + . " process completed" ) + if ( $self->{debug} ); + + my $file_data = &main::file_read( $self->{cmd_data_file} ); + unless ($file_data) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! no data returned by command" ); + return; + } + + #for some reason get_url adds garbage to the output. Clean out the characters before and after the json + my ($json_data) = $file_data =~ /({.*})/; + my $data; + eval { $data = JSON::XS->new->decode($json_data); }; + # catch crashes: if ($@) { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON file parser crashed! $@\n"; + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! JSON file parser crashed! $@\n"; $com_status = "offline"; - - } else { - if (keys $data) { - if ($data->{success} eq "true") { - shift @{$self->{cmd_queue}}; #remove the command from queue since it was successful - $self->{cmd_process_retry} = 0; - $self->poll; - } else { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING Issued command was unsuccessful (success=" . $data->{success} .") , retrying..."; - if ($self->{cmd_process_retry} > $self->{cmd_process_retry_limit}) { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR Issued command max retries reached. Abandoning command attempt..."; - shift @{$self->{cmd_queue}}; - $self->{cmd_process_retry} = 0; - $com_status = "offline"; - } else { - $self->{cmd_process_retry}++; - } - } - } else { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! Returned data not structured! Not processing..."; - $com_status = "offline"; - } - } - if (scalar @{$self->{cmd_queue}}) { - my $cmd = @{$self->{cmd_queue}}[0]; #grab the first command, but don't take it off. -<<<<<<< HEAD - $self->{cmd_process}->set($cmd); - $self->{cmd_process}->start(); - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{cmd_process}->pid() . " cmd=$cmd") if ($self->{debug}); -======= - $self->{poll_process}->set($cmd); - $self->{poll_process}->start(); - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Command Queue " . $self->{poll_process}->pid() . " cmd=$cmd") if ($self->{debug}); ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - } - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} ne $com_status ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to " . $com_status . "..." if ( $self->{loglevel} ); - $self->{status} = $com_status; - $self->{child_object}->{comm}->set( $com_status, 'poll' ); - } - } - } - + + } + else { + if ( keys $data ) { + if ( $data->{success} eq "true" ) { + shift @{ $self->{cmd_queue} + }; #remove the command from queue since it was successful + $self->{cmd_process_retry} = 0; + $self->poll; + } + else { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] WARNING Issued command was unsuccessful (success=" + . $data->{success} + . ") , retrying..."; + if ( $self->{cmd_process_retry} > + $self->{cmd_process_retry_limit} ) + { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR Issued command max retries reached. Abandoning command attempt..."; + shift @{ $self->{cmd_queue} }; + $self->{cmd_process_retry} = 0; + $com_status = "offline"; + } + else { + $self->{cmd_process_retry}++; + } + } + } + else { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! Returned data not structured! Not processing..."; + $com_status = "offline"; + } + } + if ( scalar @{ $self->{cmd_queue} } ) { + my $cmd = + @{ $self->{cmd_queue} }[0] + ; #grab the first command, but don't take it off. + $self->{cmd_process}->set($cmd); + $self->{cmd_process}->start(); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Command Queue " + . $self->{cmd_process}->pid() + . " cmd=$cmd" ) + if ( $self->{debug} ); + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne $com_status ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() . " to " + . $com_status . "..." + if ( $self->{loglevel} ); + $self->{status} = $com_status; + $self->{child_object}->{comm}->set( $com_status, 'poll' ); + } + } + } + } #------------------------------------------------------------------------------------ sub _get_JSON_data { my ( $self, $mode, $method ) = @_; -<<<<<<< HEAD - if (($self->{background} > 1) and (lc $method ne "direct")) { -======= - if (($self->{background}) and (lc $method ne "direct")) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - - my $cmd = 'get_url "http://' . $self->{host} . "/$rest{$mode}" . '"'; - if ($self->{poll_process}->done()) { - $self->{poll_process}->set($cmd); - $self->{poll_process}->start(); - $self->{poll_process_pid}->{$self->{poll_process}->pid()}=$mode; #capture the type of information requested in order to parse; - $self->{poll_process_mode} = $mode; - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{poll_process}->pid() . " command $mode, $cmd") if ($self->{debug}); - - } else { - if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{poll_queue}} . ". Queing command $mode, $cmd") if ($self->{debug}); - push @{$self->{poll_queue}}, "$mode|$cmd"; -#TODO: queue shouldn't grow for polling. Since a poll is 2 queries, the queue should only be 3 items. Otherwise it will grow every poll -# if there are device issues. - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_poll_queue} . ". Command discarded."); - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} ne "offline" ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); - $self->{status} = "offline"; - $self->{child_object}->{comm}->set( "offline", 'poll' ); - } - } - } - } - } else { - unless ( $self->{updating} ) { - - $self->{updating} = 1; - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); - - my $host = $self->{host}; - - my $request = HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); - $request->content_type("application/x-www-form-urlencoded"); - - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" if $self->{debug}; - - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - my $isSuccessResponse = $responseCode < 400; - $self->{updating} = 0; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to get data. Response code $responseCode"); -print "Venstar. status=" . $self->{status}; -if (defined $self->{child_object}->{comm}) { - print " Tracker defined\n"; -} else { - print " Tracker UNDEFINED\n"; -} - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); - $self->{status} = "offline"; - $self->{child_object}->{comm}->set( "offline", 'poll' ); - } - } - return ('0'); - } else { - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "offline" ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to online..." if ( $self->{loglevel} ); - $self->{status} = "online"; - $self->{child_object}->{comm}->set( "online", 'poll' ); - } - } - } - my $response; - eval { $response = JSON::XS->new->decode( $responseObj->content ); }; - - # catch crashes: - if ($@) { - print "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR! JSON parser crashed! $@\n"; - return ('0'); - } else { - return ( $isSuccessResponse, $response ); - } - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, not fetching data due to operation in progress" ); - return ('0'); - } - } + if ( ( $self->{background} > 1 ) and ( lc $method ne "direct" ) ) { + + my $cmd = 'get_url "http://' . $self->{host} . "/$rest{$mode}" . '"'; + if ( $self->{poll_process}->done() ) { + $self->{poll_process}->set($cmd); + $self->{poll_process}->start(); + $self->{poll_process_pid}->{ $self->{poll_process}->pid() } = $mode + ; #capture the type of information requested in order to parse; + $self->{poll_process_mode} = $mode; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Backgrounding " + . $self->{poll_process}->pid() + . " command $mode, $cmd" ) + if ( $self->{debug} ); + + } + else { + if ( scalar @{ $self->{poll_queue} } < $self->{max_poll_queue} ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Queue is " + . scalar @{ $self->{poll_queue} } + . ". Queing command $mode, $cmd" ) + if ( $self->{debug} ); + push @{ $self->{poll_queue} }, "$mode|$cmd"; + + #TODO: queue shouldn't grow for polling. Since a poll is 2 queries, the queue should only be 3 items. Otherwise it will grow every poll + # if there are device issues. + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] WARNING. Queue has grown past " + . $self->{max_poll_queue} + . ". Command discarded." ); + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} ne "offline" ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() + . " to offline..." + if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + } + } + } + else { + unless ( $self->{updating} ) { + + $self->{updating} = 1; + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); + + my $host = $self->{host}; + + my $request = + HTTP::Request->new( POST => "http://$host/$rest{$mode}" ); + $request->content_type("application/x-www-form-urlencoded"); + + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" + if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + my $isSuccessResponse = $responseCode < 400; + $self->{updating} = 0; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Warning, failed to get data. Response code $responseCode" + ); + print "Venstar. status=" . $self->{status}; + if ( defined $self->{child_object}->{comm} ) { + print " Tracker defined\n"; + } + else { + print " Tracker UNDEFINED\n"; + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "online" ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() + . " to offline..." + if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + return ('0'); + } + else { + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "offline" ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() + . " to online..." + if ( $self->{loglevel} ); + $self->{status} = "online"; + $self->{child_object}->{comm}->set( "online", 'poll' ); + } + } + } + my $response; + eval { $response = JSON::XS->new->decode( $responseObj->content ); }; + + # catch crashes: + if ($@) { + print "[Venstar Colortouch:" + . $self->{data}->{name} + . "] ERROR! JSON parser crashed! $@\n"; + return ('0'); + } + else { + return ( $isSuccessResponse, $response ); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Warning, not fetching data due to operation in progress" + ); + return ('0'); + } + } } sub _push_JSON_data { my ( $self, $type, $params, $method ) = @_; - my ( @fan, @fanstate, @modename, @statename, @schedule, @home, @schedulestat ); - $fan[0] = "auto"; - $fan[1] = "on"; - $fanstate[0] = "off"; - $fanstate[1] = "running"; - $home[0] = "home"; - $home[1] = "away"; - $modename[0] = "off"; - $modename[1] = "heating"; - $modename[2] = "cooling"; - $modename[3] = "auto"; - $statename[0] = "idle"; - $statename[1] = "heating"; - $statename[2] = "cooling"; - $statename[3] = "lockout"; - $statename[4] = "error"; - $schedule[0] = "morning (occupied1)"; - $schedule[1] = "day (occupied2)"; - $schedule[2] = "evening (occupied3)"; - $schedule[3] = "night (occupied4)"; - $schedule[255] = "inactive"; + my ( @fan, @fanstate, @modename, @statename, @schedule, @home, + @schedulestat ); + $fan[0] = "auto"; + $fan[1] = "on"; + $fanstate[0] = "off"; + $fanstate[1] = "running"; + $home[0] = "home"; + $home[1] = "away"; + $modename[0] = "off"; + $modename[1] = "heating"; + $modename[2] = "cooling"; + $modename[3] = "auto"; + $statename[0] = "idle"; + $statename[1] = "heating"; + $statename[2] = "cooling"; + $statename[3] = "lockout"; + $statename[4] = "error"; + $schedule[0] = "morning (occupied1)"; + $schedule[1] = "day (occupied2)"; + $schedule[2] = "evening (occupied3)"; + $schedule[3] = "night (occupied4)"; + $schedule[255] = "inactive"; $schedulestat[0] = "off"; $schedulestat[1] = "on"; -#4.08, schedulepart is now the schedule type. schedule 0 is off, + #4.08, schedulepart is now the schedule type. schedule 0 is off, my $cmd; - $method = "" unless (defined $method); - -#For background tasks, we want up to date data, ie returned within the last poll period. -#recursively calling the same subroutine might be a memory or performance hog, but need to effectively -#'suspend' the data push until we get valid data. -<<<<<<< HEAD - print "venstar, command: poll_timestamp=" . $self->{poll_data_timestamp} . " poll_seconds = " . $self->{config}->{poll_seconds} . " get_tickcount=" . &main::get_tickcount() . "\n";# if ($self->{debug}); - print "venstar, command: issuing poll due to old data\n" if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()); - print "venstar, command: retrying command\n" if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()); - - if (($self->{background} > 1) and (lc $method ne "direct")) { -======= - print "db: poll_timestamp=" . $self->{poll_data_timestamp} . " poll_seconds = " . $self->{config}->{poll_seconds} . " get_tickcount=" . &main::get_tickcount() . "\n" if ($self->{debug}); - if (($self->{background}) and (lc $method ne "direct")) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()) { - $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more - if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()) { #give up after 5 minutes of trying - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING: retrying command attempt due to stale poll data!" ); -<<<<<<< HEAD - &Venstar_Colortouch::_push_json_data($self,$type,$params); -======= - $self->_push_json_data($type,$params); ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR: Abandoning command attempt due to stale poll data!" ); - return ('1'); - } - } - } - - - #print "VCT DB: $params\n"; -<<<<<<< HEAD - $self->stop_timer unless ($self->{background} > 1); #stop timer to prevent a clash of updates -======= - $self->stop_timer unless ($self->{background}); #stop timer to prevent a clash of updates ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 + $method = "" unless ( defined $method ); + + #For background tasks, we want up to date data, ie returned within the last poll period. + #recursively calling the same subroutine might be a memory or performance hog, but need to effectively + #'suspend' the data push until we get valid data. + + # if (($self->{background} > 1) and (lc $method ne "direct")) { + # if (($self->{poll_data_timestamp} + ($self->{config}->{poll_seconds} * 1000)) < &main::get_tickcount()) { + # $self->poll() if (scalar @{$self->{poll_queue}} < $self->{max_poll_queue}); #once max reached, no sense adding more + # if ($self->{poll_data_timestamp} + 300000 > &main::get_tickcount()) { #give up after 5 minutes of trying + # main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING: retrying command attempt due to stale poll data!" ); + # &Venstar_Colortouch::_push_JSON_data($self,$type,$params); + # } else { + # main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] ERROR: Abandoning command attempt due to stale poll data!" ); + # return ('1'); + # } + # } + # } + + # $self->stop_timer; # unless ($self->{background} > 1); #stop timer to prevent a clash of updates if ( $type eq 'settings' ) { - #for testing purposes, curl is: - #curl --data "tempunits=1&away=0&schedule=0&hum_setpoint=30&dehum_setpoint=35" http://ip/settings - -# my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; -# my %info = %$thedata; -# my %cinfo = %$thedata; -# my @changearr; -# -# while ($params =~ /(\w+)=(\d+)/g) { -# print "[Venstar Colortouch:" -# . $self->{data}->{name} -# . "] _push_JSON_data match: $1 = $2\n"; -# $info{$1} = $2; -# if ($info{$1} ne $cinfo{$1}) { -# main::print_log( "[Venstar Colortouch:" -# . $self->{data}->{name} -# . "] Changing $1 from $cinfo{$1} to $info{$1}" ); -# push(@changearr, "$1=$info{$1}"); -# } -# } - -# $cmd = join ('&', @changearr); -# main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd; # if $self->{debug}; - - my ( $newunits, $newaway, $newholiday, $newsched, $newhumsp, $newdehumsp ); - my ($units) = $params =~ /tempunits=(\d+)/; - my ($away) = $params =~ /away=(\d+)/; - my ($holiday) = $params =~ /holiday=(\d+)/; - my ($override) = $params =~ /override=(\d+)/; - my ($overridetime) = $params =~ /overridetime=(\d+)/; - my ($forceunocc) = $params =~ /forceunocc=(\d+)/; - my ($sched) = $params =~ /schedule=(\d+)/; + #for testing purposes, curl is: + #curl --data "tempunits=1&away=0&schedule=0&hum_setpoint=30&dehum_setpoint=35" http://ip/settings + + # my ( $isSuccessResponse, $thedata ) = $self->_get_setting_params; + # my %info = %$thedata; + # my %cinfo = %$thedata; + # my @changearr; + # + # while ($params =~ /(\w+)=(\d+)/g) { + # print "[Venstar Colortouch:" + # . $self->{data}->{name} + # . "] _push_JSON_data match: $1 = $2\n"; + # $info{$1} = $2; + # if ($info{$1} ne $cinfo{$1}) { + # main::print_log( "[Venstar Colortouch:" + # . $self->{data}->{name} + # . "] Changing $1 from $cinfo{$1} to $info{$1}" ); + # push(@changearr, "$1=$info{$1}"); + # } + # } + + # $cmd = join ('&', @changearr); + # main::print_log( "Sending Settings command $cmd to " . $self->{host} ) if $cmd; # if $self->{debug}; + + my ( + $newunits, $newaway, $newholiday, + $newsched, $newhumsp, $newdehumsp + ); + my ($units) = $params =~ /tempunits=(\d+)/; + my ($away) = $params =~ /away=(\d+)/; + my ($holiday) = $params =~ /holiday=(\d+)/; + my ($override) = $params =~ /override=(\d+)/; + my ($overridetime) = $params =~ /overridetime=(\d+)/; + my ($forceunocc) = $params =~ /forceunocc=(\d+)/; + my ($sched) = $params =~ /schedule=(\d+)/; my $hum; my ($humsp) = $params =~ /hum_setpoint=(\d+)/; - my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/; #need to add in dehumidifier stuff at some point - my $humidity_change = 0; - - my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ); - my ($choliday,$coverride,$coverridetime,$cforceunocc); - - -<<<<<<< HEAD - if (($self->{background} > 1) and (lc $method ne "direct")) { -======= - if (($self->{background}) and (lc $method ne "direct")) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $cunits = $self->{data}->{info}->{tempunits}; - $caway = $self->{data}->{info}->{away}; - $csched = $self->{data}->{info}->{schedule}; - $chumsp = $self->{data}->{info}->{hum_setpoint}; - $cdehumsp = $self->{data}->{info}->{dehum_setpoint}; - } else { - ($isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, $cdehumsp ) = $self->_get_setting_params; - #($choliday,$coverride,$coverridetime,$cforceunocc); - } + my ($dehumsp) = $params =~ /dehum_setpoint=(\d+)/ + ; #need to add in dehumidifier stuff at some point + my $humidity_change = 0; + + my ( $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, + $cdehumsp ); + my ( $choliday, $coverride, $coverridetime, $cforceunocc ); + + if ( ( $self->{background} > 1 ) and ( lc $method ne "direct" ) ) { + $cunits = $self->{data}->{info}->{tempunits}; + $caway = $self->{data}->{info}->{away}; + $csched = $self->{data}->{info}->{schedule}; + $chumsp = $self->{data}->{info}->{hum_setpoint}; + $cdehumsp = $self->{data}->{info}->{dehum_setpoint}; + } + else { + ( + $isSuccessResponse, $cunits, $caway, $csched, $chum, $chumsp, + $cdehumsp + ) = $self->_get_setting_params; + + #($choliday,$coverride,$coverridetime,$cforceunocc); + } $units = $cunits if ( not defined $units ); $units = 1 if ( ( $units eq "C" ) or ( $units eq "c" ) ); $units = 0 if ( ( $units eq "F" ) or ( $units eq "f" ) ); - $away = $caway if ( not defined $away ); - $holiday = $choliday if ( not defined $holiday ); - $override = $coverride if ( not defined $override ); - $overridetime = $coverridetime if ( not defined $overridetime ); - $forceunocc = $cforceunocc if ( not defined $forceunocc ); - $sched = $csched if ( not defined $sched ); - $hum = $chum if ( not defined $hum ); + $away = $caway if ( not defined $away ); + $holiday = $choliday if ( not defined $holiday ); + $override = $coverride if ( not defined $override ); + $overridetime = $coverridetime if ( not defined $overridetime ); + $forceunocc = $cforceunocc if ( not defined $forceunocc ); + $sched = $csched if ( not defined $sched ); + $hum = $chum if ( not defined $hum ); + #v4.08, dehum_sp is humidify, and hum_sp is dehumidify. - if ($self->{api_ver} >=5) { - $humsp = $cdehumsp if ( not defined $humsp ); - } else { - $humsp = $chumsp if ( not defined $humsp ); + if ( $self->{api_ver} >= 5 ) { + $humsp = $cdehumsp if ( not defined $humsp ); + } + else { + $humsp = $chumsp if ( not defined $humsp ); } - if ($self->{api_ver} >=5) { - $dehumsp = $chumsp if ( not defined $dehumsp ); - } else { - $dehumsp = $cdehumsp if ( not defined $dehumsp ); + if ( $self->{api_ver} >= 5 ) { + $dehumsp = $chumsp if ( not defined $dehumsp ); + } + else { + $dehumsp = $cdehumsp if ( not defined $dehumsp ); } -#print "venstar db: params = $params\n"; -#print "units=$units, away=$away, sched=$sched, humsp=$humsp, dehumsp=$dehumsp\n"; -#print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; + #print "venstar db: params = $params\n"; + #print "units=$units, away=$away, sched=$sched, humsp=$humsp, dehumsp=$dehumsp\n"; + #print "cunits=$cunits, caway=$caway, csched=$csched, chum=$chum, chumsp=$chumsp, cdehumsp=$cdehumsp\n"; if ( $cunits ne $units ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Units from $cunits to $units" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Units from $cunits to $units" ); $newunits = $units; } else { @@ -635,15 +753,19 @@ sub _push_JSON_data { } if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $home[$caway] to $home[$away]" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $home[$caway] to $home[$away]" ); $newaway = $away; } else { $newaway = $caway; } - if ( ($self->{type} eq "commercial") and ($choliday ne $holiday) ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $choliday to $holiday" ); + if ( ( $self->{type} eq "commercial" ) and ( $choliday ne $holiday ) ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $choliday to $holiday" ); $newholiday = $holiday; } else { @@ -651,7 +773,9 @@ sub _push_JSON_data { } if ( $caway ne $away ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Away from $caway to $away" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Away from $caway to $away" ); $newaway = $away; } else { @@ -659,57 +783,90 @@ sub _push_JSON_data { } if ( $csched ne $sched ) { - if ($self->{api_ver} >=5) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedulestat[$csched] to $schedulestat[$sched]"); - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]"); - } + if ( $self->{api_ver} >= 5 ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Schedule from $schedulestat[$csched] to $schedulestat[$sched]" + ); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Schedule from $schedule[$csched] to $schedule[$sched]" + ); + } $newsched = $sched; } else { $newsched = $csched; } - if ($self->{api_ver} >=5) { - if ( $cdehumsp ne $humsp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $cdehumsp to $humsp\n" ); - $newhumsp = $humsp; - $humidity_change = 1; - } else { - $newhumsp = $cdehumsp; - } - - if ( $chumsp ne $dehumsp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] *Changing Dehumidity Setpoint from $chumsp to $dehumsp\n" ); - $newdehumsp = $dehumsp; - } else { - $newdehumsp = $chumsp; - } - } else { - if ( $chumsp ne $humsp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Humidity Setpoint from $chumsp to $humsp\n" ); - $newhumsp = $humsp; - $humidity_change = 1; - } else { - $newhumsp = $chumsp; - } - - if ( $cdehumsp ne $dehumsp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing Dehumidity Setpoint from $cdehumsp to $dehumsp\n" ); - $newdehumsp = $dehumsp; - } else { - $newdehumsp = $cdehumsp; - } - } - -## to set v4.08, humidity to 32%, this is needed "tempunits=1&away=0&schedule=0&hum_setpoint=32&dehum_setpoint=38" + if ( $self->{api_ver} >= 5 ) { + if ( $cdehumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Humidity Setpoint from $cdehumsp to $humsp\n" + ); + $newhumsp = $humsp; + $humidity_change = 1; + } + else { + $newhumsp = $cdehumsp; + } + + if ( $chumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] *Changing Dehumidity Setpoint from $chumsp to $dehumsp\n" + ); + $newdehumsp = $dehumsp; + } + else { + $newdehumsp = $chumsp; + } + } + else { + if ( $chumsp ne $humsp ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Humidity Setpoint from $chumsp to $humsp\n" + ); + $newhumsp = $humsp; + $humidity_change = 1; + } + else { + $newhumsp = $chumsp; + } + + if ( $cdehumsp ne $dehumsp ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing Dehumidity Setpoint from $cdehumsp to $dehumsp\n" + ); + $newdehumsp = $dehumsp; + } + else { + $newdehumsp = $cdehumsp; + } + } + +## to set v4.08, humidity to 32%, this is needed "tempunits=1&away=0&schedule=0&hum_setpoint=32&dehum_setpoint=38" ## so humidity setpoint hasn't changed? - if ($self->{api_ver} >=5) { - # v4.08 has changed humidification settings - # - need 6 % point delta on humidity and dehumidity. - $newdehumsp = $newhumsp + 6 if ((($newhumsp >= $newdehumsp) or (($newdehumsp - $newhumsp) < 6))); - } - $cmd = "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; - main::print_log( "Sending Settings command [$cmd] to " . $self->{host} ) if $self->{debug}; + if ( $self->{api_ver} >= 5 ) { + + # v4.08 has changed humidification settings + # - need 6 % point delta on humidity and dehumidity. + $newdehumsp = $newhumsp + 6 + if ( + ( + ( $newhumsp >= $newdehumsp ) + or ( ( $newdehumsp - $newhumsp ) < 6 ) + ) + ); + } + $cmd = + "tempunits=$newunits&away=$newaway&schedule=$newsched&hum_setpoint=$newhumsp&dehum_setpoint=$newdehumsp"; + main::print_log( "Sending Settings command [$cmd] to " . $self->{host} ) + if $self->{debug}; } elsif ( $type eq 'control' ) { @@ -724,25 +881,32 @@ sub _push_JSON_data { my ($heattemp) = $params =~ /heattemp=(\d+\.?\d?)/; #need decimals my ($cooltemp) = $params =~ /cooltemp=(\d+\.?\d?)/; - my ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool); - -<<<<<<< HEAD - if (($self->{background} > 1) and (lc $method ne "direct")) { -======= - if (($self->{background}) and (lc $method ne "direct")) { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $cmode = $self->{data}->{info}->{mode}; - $cfan = $self->{data}->{info}->{fan}; - $cheattemp = $self->{data}->{info}->{heattemp}; - $ccooltemp = $self->{data}->{info}->{cooltemp}; - $setpointdelta = $self->{data}->{info}->{setpointdelta}; - $minheat = $self->{data}->{info}->{heattempmin}; - $maxheat = $self->{data}->{info}->{heattempmax}; - $mincool = $self->{data}->{info}->{cooltempmin}; - $maxcool = $self->{data}->{info}->{cooltempmax}; - - } else { - ($isSuccessResponse, $cmode, $cfan, $cheattemp, $ccooltemp, $setpointdelta, $minheat, $maxheat, $mincool, $maxcool) = $self->_get_control_params; + my ( + $isSuccessResponse, $cmode, $cfan, + $cheattemp, $ccooltemp, $setpointdelta, + $minheat, $maxheat, $mincool, + $maxcool + ); + + if ( ( $self->{background} > 1 ) and ( lc $method ne "direct" ) ) { + $cmode = $self->{data}->{info}->{mode}; + $cfan = $self->{data}->{info}->{fan}; + $cheattemp = $self->{data}->{info}->{heattemp}; + $ccooltemp = $self->{data}->{info}->{cooltemp}; + $setpointdelta = $self->{data}->{info}->{setpointdelta}; + $minheat = $self->{data}->{info}->{heattempmin}; + $maxheat = $self->{data}->{info}->{heattempmax}; + $mincool = $self->{data}->{info}->{cooltempmin}; + $maxcool = $self->{data}->{info}->{cooltempmax}; + + } + else { + ( + $isSuccessResponse, $cmode, $cfan, + $cheattemp, $ccooltemp, $setpointdelta, + $minheat, $maxheat, $mincool, + $maxcool + ) = $self->_get_control_params; } $mode = $cmode if ( not defined $mode ); @@ -750,9 +914,11 @@ sub _push_JSON_data { $heattemp = $cheattemp if ( !$heattemp ); $cooltemp = $ccooltemp if ( !$cooltemp ); - if ( $cmode ne $mode ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing mode from $modename[$cmode] to $modename[$mode]"); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing mode from $modename[$cmode] to $modename[$mode]" + ); $newmode = $mode; } else { @@ -760,7 +926,9 @@ sub _push_JSON_data { } if ( $cfan ne $fan ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing fan from $fan[$cfan] to $fan[$fan]" ); $newfan = $fan; } else { @@ -768,7 +936,9 @@ sub _push_JSON_data { } if ( $heattemp ne $cheattemp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing heat setpoint from $cheattemp to $heattemp" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing heat setpoint from $cheattemp to $heattemp" ); $newheatsp = $heattemp; } else { @@ -776,7 +946,9 @@ sub _push_JSON_data { } if ( $cooltemp ne $ccooltemp ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Changing cool setpoint from $ccooltemp to $cooltemp" ); $newcoolsp = $cooltemp; } else { @@ -784,24 +956,37 @@ sub _push_JSON_data { } if ( ( $newcoolsp > $maxcool ) or ( $newcoolsp < $mincool ) ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool"); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error: New cooling setpoint $newcoolsp out of bounds $mincool - $maxcool" + ); $newcoolsp = $ccooltemp; } if ( ( $newheatsp > $maxheat ) or ( $newheatsp < $minheat ) ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat"); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error: New heating setpoint $newheatsp out of bounds $minheat - $maxheat" + ); $newheatsp = $cheattemp; } if ( ( $newheatsp - $newcoolsp ) > $setpointdelta ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta"); - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Not setting setpoints" ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Error: Cooling ($newcoolsp) and Heating ($newheatsp) setpoints need to be less than setpoint $setpointdelta" + ); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Not setting setpoints" ); $newcoolsp = $ccooltemp; $newheatsp = $cheattemp; } - $cmd = "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; - main::print_log( "Sending Control command $cmd to " . $self->{host} ) if $self->{debug}; + $cmd = + "mode=$newmode&fan=$newfan&heattemp=$newheatsp&cooltemp=$newcoolsp"; + main::print_log( "Sending Control command $cmd to " . $self->{host} ) + if $self->{debug}; } else { @@ -809,99 +994,122 @@ sub _push_JSON_data { main::print_log("Unknown mode!"); return ( '1', 'error' ); } - my $isSuccessResponse; - my $response; - if (($self->{background}) and (lc $method ne "direct")) { - $isSuccessResponse = 1; #set these to successful, since the process_data will indicate if a setting was unsuccessful. - $response = "success"; - my $cmd = 'get_url -post "' .$cmd . '" "http://' . $self->{host} . "/$rest{$type}" . '"'; - push @{$self->{cmd_queue}}, "$cmd"; - if ($self->{cmd_process}->done()) { - $self->{cmd_process}->set($cmd); -<<<<<<< HEAD - $self->{cmd_process_name} = $cmd; -======= ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - $self->{cmd_process}->start(); - $self->{cmd_process_retry} = 0; - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Backgrounding " . $self->{cmd_process}->pid() . " command $cmd") if ($self->{debug}); - - } else { - if (scalar @{$self->{cmd_queue}} < $self->{max_cmd_queue}) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Queue is " . scalar @{$self->{cmd_queue}} . ". Queing command $cmd") if ($self->{debug}); - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] WARNING. Queue has grown past " . $self->{max_cmd_queue} . ". Command discarded."); - } - } - } else { - - my $ua = new LWP::UserAgent( keep_alive => 1 ); - $ua->timeout( $self->{timeout} ); - - my $host = $self->{host}; - - my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); - $request->content_type("application/x-www-form-urlencoded"); - $request->content($cmd) if $cmd; - - my $responseObj = $ua->request($request); - print $responseObj->content . "\n--------------------\n" if $self->{debug}; - - my $responseCode = $responseObj->code; - print 'Response code: ' . $responseCode . "\n" if $self->{debug}; - $isSuccessResponse = $responseCode < 400; - if ( !$isSuccessResponse ) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Warning, failed to push data. Response code $responseCode" ); -print "Venstar. status=" . $self->{status}; -if (defined $self->{child_object}->{comm}) { - print " Tracker defined\n"; -} else { - print " Tracker UNDEFINED\n"; -} - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "online" ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Communication Tracking object found. Updating from " . $self->{child_object}->{comm}->state() . " to offline..." if ( $self->{loglevel} ); - $self->{status} = "offline"; - $self->{child_object}->{comm}->set( "offline", 'poll' ); + my $isSuccessResponse; + my $response; + if ( ( $self->{background} ) and ( lc $method ne "direct" ) ) { + $isSuccessResponse = 1 + ; #set these to successful, since the process_data will indicate if a setting was unsuccessful. + $response = "success"; + my $cmd = + 'get_url -post "' + . $cmd + . '" "http://' + . $self->{host} + . "/$rest{$type}" . '"'; + push @{ $self->{cmd_queue} }, "$cmd"; + if ( $self->{cmd_process}->done() ) { + $self->{cmd_process}->set($cmd); + $self->{cmd_process_name} = $cmd; + $self->{cmd_process}->start(); + $self->{cmd_process_retry} = 0; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Backgrounding " + . $self->{cmd_process}->pid() + . " command $cmd" ) + if ( $self->{debug} ); + + } + else { + if ( scalar @{ $self->{cmd_queue} } < $self->{max_cmd_queue} ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Queue is " + . scalar @{ $self->{cmd_queue} } + . ". Queing command $cmd" ) + if ( $self->{debug} ); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] WARNING. Queue has grown past " + . $self->{max_cmd_queue} + . ". Command discarded." ); } } } else { - if ( defined $self->{child_object}->{comm} ) { - if ( $self->{status} eq "offline" ) { - main::print_log "[Venstar Colortouch:" + + my $ua = new LWP::UserAgent( keep_alive => 1 ); + $ua->timeout( $self->{timeout} ); + + my $host = $self->{host}; + + my $request = HTTP::Request->new( POST => "http://$host/$rest{$type}" ); + $request->content_type("application/x-www-form-urlencoded"); + $request->content($cmd) if $cmd; + + my $responseObj = $ua->request($request); + print $responseObj->content . "\n--------------------\n" + if $self->{debug}; + + my $responseCode = $responseObj->code; + print 'Response code: ' . $responseCode . "\n" if $self->{debug}; + $isSuccessResponse = $responseCode < 400; + if ( !$isSuccessResponse ) { + main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} - . "] Communication Tracking object found. Updating from " - . $self->{child_object}->{comm}->state() - . " to online..." - if ( $self->{loglevel} ); - $self->{status} = "online"; - $self->{child_object}->{comm}->set( "online", 'poll' ); + . "] Warning, failed to push data. Response code $responseCode" + ); + print "Venstar. status=" . $self->{status}; + if ( defined $self->{child_object}->{comm} ) { + print " Tracker defined\n"; } + else { + print " Tracker UNDEFINED\n"; + } + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "online" ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() + . " to offline..." + if ( $self->{loglevel} ); + $self->{status} = "offline"; + $self->{child_object}->{comm}->set( "offline", 'poll' ); + } + } + } + else { + if ( defined $self->{child_object}->{comm} ) { + if ( $self->{status} eq "offline" ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Communication Tracking object found. Updating from " + . $self->{child_object}->{comm}->state() + . " to online..." + if ( $self->{loglevel} ); + $self->{status} = "online"; + $self->{child_object}->{comm}->set( "online", 'poll' ); + } + } + + #my $response = JSON::XS->new->decode ($responseObj->content); + ($response) = $responseObj->content =~ /\{\"(.*)\":/; } - #my $response = JSON::XS->new->decode ($responseObj->content); - ($response) = $responseObj->content =~ /\{\"(.*)\":/; } -<<<<<<< HEAD - } - #print Dumper $response if $self->{debug}; - print "response=$response\n" if $self->{debug}; - -# remove this poll since the stat pauses after being set? -# if ( $response eq "success" ) { -# $self->poll(); -# $self->process_data() unless ($self->{background} >1); -# } - $self->start_timer unless ($self->{background} > 1); - -======= #print Dumper $response if $self->{debug}; print "response=$response\n" if $self->{debug}; - $self->poll if ( $response eq "success" ); - $self->start_timer; - } ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 + + # remove this poll since the stat pauses after being set? + # if ( $response eq "success" ) { + # $self->poll(); + # $self->process_data() unless ($self->{background} >1); + # } + $self->start_timer; # unless ($self->{background} > 1); + return ( $isSuccessResponse, $response ); } @@ -930,7 +1138,8 @@ sub _get_control_params { sub _get_setting_params { my ($self) = @_; - my ( $isSuccessResponse, $info ) = $self->_get_JSON_data('info',"direct"); + my ( $isSuccessResponse, $info ) = + $self->_get_JSON_data( 'info', "direct" ); return ( $isSuccessResponse, $info->{tempunits}, $info->{away}, $info->{schedule}, $info->{hum}, $info->{hum_setpoint}, $info->{dehum_setpoint} ); @@ -951,10 +1160,9 @@ sub stop_timer { sub start_timer { my ($self) = @_; -<<<<<<< HEAD -======= - print "db: timer has started\n"; ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 + unless ( defined $self->{timer} ) { + $self->{timer} = new Timer; #HP: why do timers get undefined?? + } if ( defined $self->{timer} ) { $self->{timer}->set( $self->{config}->{poll_seconds}, sub { &Venstar_Colortouch::_poll_check($self) }, -1 ); @@ -967,16 +1175,22 @@ sub start_timer { } sub background_enable { - my ($self,$level) = @_; - $level = 1 unless (defined $level); - $self->{background} = $level; - main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode enabled (level " . $level . ")"); + my ( $self, $level ) = @_; + $level = 1 unless ( defined $level ); + $self->{background} = $level; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Background mode enabled (level " + . $level + . ")" ); } sub background_disable { my ($self) = @_; - $self->{background} = 0; - main::print_log("[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode disabled. Now in direct mode."); + $self->{background} = 0; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Background mode disabled. Now in direct mode." ); } sub print_info { @@ -1022,17 +1236,27 @@ sub print_info { . "] Device " . $self->{data}->{name} . " is " . $type - . " Thermostat with API level " . $self->{api_ver} ); - - if ($self->{background}) { - if ($self->{background} > 1) { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Background mode enabled"); - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Hybrid mode enabled (Background commands, direct polling)"); - } - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Direct mode enabled"); - } + . " Thermostat with API level " + . $self->{api_ver} ); + + if ( $self->{background} ) { + if ( $self->{background} > 1 ) { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Background mode enabled" ); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Hybrid mode enabled (Background commands, direct polling)" + ); + } + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Direct mode enabled" ); + } main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -1079,7 +1303,8 @@ sub print_info { . $self->{data}->{name} . "] Current Humidity:" . $self->{data}->{info}->{hum} - . "%" ) if ($self->{api_ver} < 5); + . "%" ) + if ( $self->{api_ver} < 5 ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -1103,7 +1328,8 @@ sub print_info { . $self->{data}->{info}->{cooltempmax} . "$unit" ); my $hum_value = $self->{data}->{info}->{hum_setpoint}; - $hum_value = $self->{data}->{info}->{dehum_setpoint} if ($self->{api_ver} >= 5); + $hum_value = $self->{data}->{info}->{dehum_setpoint} + if ( $self->{api_ver} >= 5 ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidity:\t" @@ -1115,7 +1341,8 @@ sub print_info { . "] Dehumidity:" . $self->{data}->{info}->{dehum_setpoint} . "%" ) - unless (( $self->{data}->{info}->{dehum_setpoint} == 99 ) or ($self->{api_ver} >= 5)); + unless ( ( $self->{data}->{info}->{dehum_setpoint} == 99 ) + or ( $self->{api_ver} >= 5 ) ); main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} @@ -1154,7 +1381,10 @@ sub process_data { # set state of self for state # for any registered child selfs, update their state if - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Processing Data...") if ($self->{debug}); + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Processing Data..." ) + if ( $self->{debug} ); my ( @fan, @fanstate, @mode, @state, @schedule ); $fan[0] = "auto"; @@ -1222,7 +1452,9 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{fan} = $self->{data}->{info}->{fan}; if ( defined $self->{child_object}->{fanstate} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Fan Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Fan Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{fanstate} ->set_mode( $fan[ $self->{data}->{info}->{fan} ] ); @@ -1239,7 +1471,9 @@ sub process_data { $self->{previous}->{info}->{fanstate} = $self->{data}->{info}->{fanstate}; if ( defined $self->{child_object}->{fanstate} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Fanstate Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Fan state Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{fanstate} ->set( $fanstate[ $self->{data}->{info}->{fanstate} ], 'poll' ); @@ -1253,16 +1487,14 @@ sub process_data { ) if ( $self->{loglevel} ); $self->{previous}->{info}->{mode} = $self->{data}->{info}->{mode}; if ( defined $self->{child_object}->{mode} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Mode Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Mode Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{mode} - ->set( $mode[$self->{data}->{info}->{mode}] ); + ->set( $mode[ $self->{data}->{info}->{mode} ] ); } -<<<<<<< HEAD -======= - ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 } if ( $self->{previous}->{info}->{state} != $self->{data}->{info}->{state} ) @@ -1292,7 +1524,9 @@ sub process_data { $self->{previous}->{info}->{schedule} = $self->{data}->{info}->{schedule}; if ( defined $self->{child_object}->{sched} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Schedule Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Schedule Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{sched} ->set( $sched[ $self->{data}->{info}->{schedule} ], 'poll' ); @@ -1310,7 +1544,9 @@ sub process_data { $self->{data}->{info}->{schedulepart}; } - if ( $self->{type} eq "residential" and $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) { + if ( $self->{type} eq "residential" + and $self->{previous}->{info}->{away} != $self->{data}->{info}->{away} ) + { my $away = "home mode"; $away = "away mode" if ( $self->{data}->{info}->{away} ); main::print_log( "[Venstar Colortouch:" @@ -1321,7 +1557,10 @@ sub process_data { $self->{previous}->{info}->{away} = $self->{data}->{info}->{away}; } - if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{holiday} != $self->{data}->{info}->{holiday} ) { + if ( $self->{type} eq "commercial" + and $self->{previous}->{info}->{holiday} != + $self->{data}->{info}->{holiday} ) + { my $holiday = "observing holiday"; $holiday = "no holiday" if ( $self->{data}->{info}->{holiday} ); main::print_log( "[Venstar Colortouch:" @@ -1332,7 +1571,10 @@ sub process_data { $self->{previous}->{info}->{holiday} = $self->{data}->{info}->{holiday}; } - if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{override} != $self->{data}->{info}->{override} ) { + if ( $self->{type} eq "commercial" + and $self->{previous}->{info}->{override} != + $self->{data}->{info}->{override} ) + { my $override = "off"; $override = "on" if ( $self->{data}->{info}->{override} ); main::print_log( "[Venstar Colortouch:" @@ -1340,19 +1582,27 @@ sub process_data { . "] Thermostat override changed to" . $override ) if ( $self->{loglevel} ); - $self->{previous}->{info}->{override} = $self->{data}->{info}->{override}; + $self->{previous}->{info}->{override} = + $self->{data}->{info}->{override}; } - if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{overridetime} != $self->{data}->{info}->{overridetime} ) { + if ( $self->{type} eq "commercial" + and $self->{previous}->{info}->{overridetime} != + $self->{data}->{info}->{overridetime} ) + { main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat overridetime changed to" . $self->{data}->{info}->{overridetime} ) if ( $self->{loglevel} ); - $self->{previous}->{info}->{overridetime} = $self->{data}->{info}->{overridetime}; + $self->{previous}->{info}->{overridetime} = + $self->{data}->{info}->{overridetime}; } - if ( $self->{type} eq "commercial" and $self->{previous}->{info}->{forceunocc} != $self->{data}->{info}->{forceunocc} ) { + if ( $self->{type} eq "commercial" + and $self->{previous}->{info}->{forceunocc} != + $self->{data}->{info}->{forceunocc} ) + { my $forceunocc = "off"; $forceunocc = "on" if ( $self->{data}->{info}->{forceunocc} ); main::print_log( "[Venstar Colortouch:" @@ -1360,7 +1610,8 @@ sub process_data { . "] Thermostat forceunocc changed to" . $forceunocc ) if ( $self->{loglevel} ); - $self->{previous}->{info}->{forceunocc} = $self->{data}->{info}->{forceunocc}; + $self->{previous}->{info}->{forceunocc} = + $self->{data}->{info}->{forceunocc}; } if ( $self->{previous}->{info}->{spacetemp} != @@ -1373,7 +1624,9 @@ sub process_data { $self->{previous}->{info}->{spacetemp} = $self->{data}->{info}->{spacetemp}; if ( defined $self->{child_object}->{temp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Temperature Sensor Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Temperature Sensor Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{temp} ->set( $self->{data}->{info}->{spacetemp}, 'poll' ); @@ -1390,7 +1643,9 @@ sub process_data { $self->{previous}->{info}->{heattemp} = $self->{data}->{info}->{heattemp}; if ( defined $self->{child_object}->{heat_sp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Heat Setpoint Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Heat Setpoint Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{heat_sp} ->set( $self->{data}->{info}->{heattemp}, 'poll' ); @@ -1429,7 +1684,9 @@ sub process_data { $self->{previous}->{info}->{cooltemp} = $self->{data}->{info}->{cooltemp}; if ( defined $self->{child_object}->{cool_sp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Cooling Setpoint Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Cooling Setpoint Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{cool_sp} ->set( $self->{data}->{info}->{cooltemp}, 'poll' ); @@ -1458,33 +1715,64 @@ sub process_data { $self->{data}->{info}->{cooltempmax}; } - if ( $self->{previous}->{info}->{dehum_setpoint} != $self->{data}->{info}->{dehum_setpoint} ) { - if ($self->{api_ver} >=5) { #v5, dehum_setpoint is now the humidity setpoint? - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}") if ( $self->{loglevel} ); - $self->{previous}->{info}->{dehum_setpoint} = $self->{data}->{info}->{dehum_setpoint}; - if ( defined $self->{child_object}->{hum_sp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidify Child object found. Updating..." if ( $self->{loglevel} ); - $self->{child_object}->{hum_sp}->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); + if ( $self->{previous}->{info}->{dehum_setpoint} != + $self->{data}->{info}->{dehum_setpoint} ) + { + if ( $self->{api_ver} >= 5 ) + { #v5, dehum_setpoint is now the humidity setpoint? + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}" + ) if ( $self->{loglevel} ); + $self->{previous}->{info}->{dehum_setpoint} = + $self->{data}->{info}->{dehum_setpoint}; + if ( defined $self->{child_object}->{hum_sp} ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Humidify Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{hum_sp} + ->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); } - } else { - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat dehumidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}") if ( $self->{loglevel} ); - $self->{previous}->{info}->{dehum_setpoint} = $self->{data}->{info}->{dehum_setpoint}; - if ( defined $self->{child_object}->{dehum_sp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Dehumidify Child object found. Updating..." if ( $self->{loglevel} ); - $self->{child_object}->{dehum_sp}->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); + } + else { + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat dehumidity setpoint changed from $self->{previous}->{info}->{dehum_setpoint} to $self->{data}->{info}->{dehum_setpoint}" + ) if ( $self->{loglevel} ); + $self->{previous}->{info}->{dehum_setpoint} = + $self->{data}->{info}->{dehum_setpoint}; + if ( defined $self->{child_object}->{dehum_sp} ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Dehumidify Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{dehum_sp} + ->set( $self->{data}->{info}->{dehum_setpoint}, 'poll' ); } } } - if ( $self->{previous}->{info}->{hum_setpoint} != $self->{data}->{info}->{hum_setpoint} ) { - if ($self->{api_ver} < 5) { #v5, hum_setpoint is now the humidity sensor? - main::print_log( "[Venstar Colortouch:" . $self->{data}->{name} . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{hum_setpoint} to $self->{data}->{info}->{hum_setpoint}") if ( $self->{loglevel} ); - $self->{previous}->{info}->{hum_setpoint} = $self->{data}->{info}->{hum_setpoint}; - if ( defined $self->{child_object}->{hum_sp} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidify Child object found. Updating..." if ( $self->{loglevel} ); - $self->{child_object}->{hum_sp} ->set( $self->{data}->{info}->{hum_setpoint}, 'poll' ); - } - } + if ( $self->{previous}->{info}->{hum_setpoint} != + $self->{data}->{info}->{hum_setpoint} ) + { + if ( $self->{api_ver} < 5 ) + { #v5, hum_setpoint is now the humidity sensor? + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Thermostat humidity setpoint changed from $self->{previous}->{info}->{hum_setpoint} to $self->{data}->{info}->{hum_setpoint}" + ) if ( $self->{loglevel} ); + $self->{previous}->{info}->{hum_setpoint} = + $self->{data}->{info}->{hum_setpoint}; + if ( defined $self->{child_object}->{hum_sp} ) { + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Humidify Child object found. Updating..." + if ( $self->{loglevel} ); + $self->{child_object}->{hum_sp} + ->set( $self->{data}->{info}->{hum_setpoint}, 'poll' ); + } + } } #if ($self->{previous}->{info}->{hum} != $self->{data}->{info}->{hum}) { @@ -1513,7 +1801,9 @@ sub process_data { $self->{previous}->{sensors}->{sensors}[0]->{hum} = $self->{data}->{sensors}->{sensors}[0]->{hum}; if ( defined $self->{child_object}->{hum} ) { - main::print_log "[Venstar Colortouch:" . $self->{data}->{name} . "] Humidity Sensor Child object found. Updating..." + main::print_log "[Venstar Colortouch:" + . $self->{data}->{name} + . "] Humidity Sensor Child object found. Updating..." if ( $self->{loglevel} ); $self->{child_object}->{hum} ->set( $self->{data}->{sensors}->{sensors}[0]->{hum}, 'poll' ); @@ -1552,9 +1842,9 @@ sub print_runtimes { # User access methods sub get_apiver { - my ($self) = @_; - - return ($self->{api_ver}); + my ($self) = @_; + + return ( $self->{api_ver} ); } sub get_mode { @@ -1630,14 +1920,17 @@ sub get_sp_cool { sub get_sp_hum { my ($self) = @_; - my $value = $self->{data}->{info}->{hum_setpoint}; - $value = $self->{data}->{info}->{dehum_setpoint} if ($self->{api_ver} >= 5); - return ( $value ); + my $value = $self->{data}->{info}->{hum_setpoint}; + $value = $self->{data}->{info}->{dehum_setpoint} + if ( $self->{api_ver} >= 5 ); + return ($value); } sub get_sp_dehum { my ($self) = @_; - main::print_log("[Venstar_Colortouch]: WARNING, api v5 humidity settings are questionable.") if ($self->{api_ver} >= 5); + main::print_log( + "[Venstar_Colortouch]: WARNING, api v5 humidity settings are questionable." + ) if ( $self->{api_ver} >= 5 ); return ( $self->{data}->{info}->{dehum_setpoint} ); } @@ -2142,13 +2435,22 @@ sub set_debug { sub set { my ( $self, $p_state, $p_setby ) = @_; - if ( $p_setby eq 'poll' ) { - print "db: in super set\n"; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] DB super::set, in master set, p_state=$p_state, p_setby=$p_setby" + ); + $self->SUPER::set($p_state); + $self->start_timer if ( $self->{background} == 2 ); + } else { - print "db: in mode set\n"; + main::print_log( "[Venstar Colortouch:" + . $self->{data}->{name} + . "] DB set_mode, in master set, p_state=$p_state, p_setby=$p_setby" + ); + $self->set_mode($p_state); } } @@ -2270,8 +2572,8 @@ sub new { bless $self, $class; $$self{master_object} = $object; - push( @{ $$self{states} }, '0','5','10','15','20','25','30','35','40' ); - + push( @{ $$self{states} }, + '0', '5', '10', '15', '20', '25', '30', '35', '40' ); $object->register( $self, 'hum_sp' ); $self->set( $object->get_sp_hum, 'poll' ); @@ -2383,13 +2685,18 @@ sub set { $self->SUPER::set($p_state); } else { - if ( ( lc $p_state eq "cooling" ) or ( lc $p_state eq "heating" ) or ( lc $p_state eq "cool" ) or ( lc $p_state eq "heat" ) or ( lc $p_state eq "auto" ) or ( lc $p_state eq "off" ) ) { + if ( ( lc $p_state eq "cooling" ) + or ( lc $p_state eq "heating" ) + or ( lc $p_state eq "cool" ) + or ( lc $p_state eq "heat" ) + or ( lc $p_state eq "auto" ) + or ( lc $p_state eq "off" ) ) + { $$self{master_object}->set_mode($p_state); } else { main::print_log( - "[Venstar Colortouch Mode] Error. Unknown set state $p_state" - ); + "[Venstar Colortouch Mode] Error. Unknown set state $p_state" ); } } } @@ -2405,18 +2712,24 @@ sub new { bless $self, $class; $$self{master_object} = $object; -<<<<<<< HEAD - if ((defined $scale) and (lc $scale eq "F")) { -======= - if (lc $scale eq "F") { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - push( @{ $$self{states} }, '65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); - $self->{lower_limit} = 65; - $self->{upper_limit} = 80; - } else { - push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); - $self->{lower_limit} = 17; - $self->{upper_limit} = 26; + if ( ( defined $scale ) and ( lc $scale eq "F" ) ) { + push( + @{ $$self{states} }, + '65', '66', '67', '68', '69', '70', '71', '72', + '73', '74', '75', '76', '77', '78', '79', '80' + ); + $self->{lower_limit} = 65; + $self->{upper_limit} = 80; + } + else { + push( + @{ $$self{states} }, + '17', '17.5', '18', '18.5', '19', '19.5', '20', + '20.5', '21', '21.5', '22', '22.5', '23', '23.5', + '24', '24.5', '25', '25.5', '26' + ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; } $object->register( $self, 'heat_sp' ); @@ -2433,11 +2746,18 @@ sub set { } else { if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { - if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { - $$self{master_object}->set_heat_sp($p_state); - } else { - main::print_log("[Venstar Colortouch Heat_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); - } + if ( ( $p_state >= $self->{lower_limit} ) + and ( $p_state <= $self->{upper_limit} ) ) + { + $$self{master_object}->set_heat_sp($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Heat_SP] Error. $p_state out of limits (" + . $self->{lower_limit} . " to " + . $self->{upper_limit} + . ")" ); + } } else { main::print_log( @@ -2458,18 +2778,25 @@ sub new { bless $self, $class; $$self{master_object} = $object; -<<<<<<< HEAD - if ((defined $scale) and (lc $scale eq "F")) { -======= - if (lc $scale eq "F") { ->>>>>>> 13f9535f4edd56649d67488324a58ad0de2d0c33 - push( @{ $$self{states} }, '58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80' ); - $self->{lower_limit} = 58; - $self->{upper_limit} = 80; - } else { - push( @{ $$self{states} }, '17','17.5','18','18.5','19','19.5','20','20.5','21','21.5','22','22.5','23','23.5','24','24.5','25','25.5','26' ); - $self->{lower_limit} = 17; - $self->{upper_limit} = 26; + if ( ( defined $scale ) and ( lc $scale eq "F" ) ) { + push( + @{ $$self{states} }, + '58', '59', '60', '61', '62', '63', '64', '65', + '66', '67', '68', '69', '70', '71', '72', '73', + '74', '75', '76', '77', '78', '79', '80' + ); + $self->{lower_limit} = 58; + $self->{upper_limit} = 80; + } + else { + push( + @{ $$self{states} }, + '17', '17.5', '18', '18.5', '19', '19.5', '20', + '20.5', '21', '21.5', '22', '22.5', '23', '23.5', + '24', '24.5', '25', '25.5', '26' + ); + $self->{lower_limit} = 17; + $self->{upper_limit} = 26; } $object->register( $self, 'cool_sp' ); @@ -2486,16 +2813,25 @@ sub set { } else { if ( ( $p_state >= 0 ) and ( $p_state <= 98 ) ) { - if (($p_state >= $self->{lower_limit}) and ($p_state <= $self->{upper_limit})) { - $$self{master_object}->set_cool_sp($p_state); - } else { - main::print_log("[Venstar Colortouch Cool_SP] Error. $p_state out of limits (" . $self->{lower_limit} . " to " . $self->{upper_limit} . ")"); + if ( ( $p_state >= $self->{lower_limit} ) + and ( $p_state <= $self->{upper_limit} ) ) + { + $$self{master_object}->set_cool_sp($p_state); + } + else { + main::print_log( + "[Venstar Colortouch Cool_SP] Error. $p_state out of limits (" + . $self->{lower_limit} . " to " + . $self->{upper_limit} + . ")" ); } } else { - main::print_log("[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state"); + main::print_log( + "[Venstar Colortouch Cool_SP] Error. Unknown set state $p_state" + ); } } } -1; \ No newline at end of file +1;