Talking Back to the Thermostat


Summary

The next step in my KRL thermostat experiment is to start sending the thermostat directives. This post describes a project to set the color LED according to the temperature trend.

A few weeks ago, I put up the details of how I've enabled a Radio Thermostat CT-30 to work with Kynetx. The thermostat, through an endpoint, is raising a thermostat:new_temperature event periodically (currently every 60 seconds) with the temperature event attribute set to the current temperature.

Using the KRL Cosm module, I was able to log the temperature data to Cosm using a simple rule:

rule process_temperature {
  select when thermostat new_temperature
  cosm:update("Temperature", event:attr("temperature"));
}

Radstat Directives

Of course, I'd like to do more than log the temperature—as fun as that is. I'd like eventually to control the thermostat according to the data in a Google calendar. That means talking back to the thermostat, through the endpoint. To get started, I decided to just control the lights on the front of the thermostat. You can control the LED on the CT-30 to be green, yellow, red or off. The thermostat API changes the LED color when you POST a JSON data structure that looks like this:

{"energy_led" : <num>}

to /tstat/led on the thermometer where <num> represents the color.

KRL could POST directly to the thermostat if we opened up tunnel through the firewall to it, but I'd rather not. Instead, I determined to return a KRL directive document to the endpoint and have it interact with the thermostat. This preserves the endpoint as an abstraction of the thermostat. I can stop thinking of the details of the thermostat API and just use functions in a KRL radstat module (for Radio Thermostat).

For now, the radstat module is simple, just a few user defined actions. The one I want to highlight is the set_color() action that sets the LED color:

set_color = defaction(color) {
  send_directive("radstat") with 
     led = color
};

The color is given as one of "green," "yellow," "red," or "off." The endpoint translates the strings to the API's requirements. I went over the details of the endpoint in the previous blog post, but we ignored directives. What's changed is this chunk of Perl code that reads the results from raising the event and processes directives:

my $response = 
    $event->raise({'temperature' => 
                   $scalar->{ 'temp' }});

foreach my $d (@{$response->{'directives'}}) {
  if ($d->{'name'} eq 'radstat') {
    my $o = $d->{'options'};
    if ($o->{'message'}) {
      radstat_request($ua, 
                      "$thermo_url/pma", 
                      "message set", 
                      {message => $o->{'message'}} );
    } elsif ($o->{'led'}) {
      my $color = 
          ($o->{'led'} eq "green")  ? 1 :
          ($o->{'led'} eq "yellow") ? 2 :
          ($o->{'led'} eq "red")    ? 4 :
                                      0 ;   # off
      radstat_request($ua, 
                      "$thermo_url/led", 
                      "LED color change ($o->{led})" , 
                      {energy_led => $color});
   }
}

The function radstat_request() makes the HTTP POST to the thermostat and handles any errors.

Using the set_color() action

To make this interesting, I decided to set the color depending on the trend of the temperature. Green if it's getting cooler, red if it's getting warmer and yellow if it's constant. Here's the rule that does that.

rule set_trend_light {
  select when thermostat new_temperature
  pre {
    temperature = event:attr("temperature");
    color = 
      within_bound(temperature, 
                   ent:avg_temp, 0.2) => "yellow" |
      (temperature < ent:avg_temp)    => "green"  |
      (temperature > ent:avg_temp)    => "red"    | 
                                         "off"    ;
  }
  radstat:set_color(color);
  fired {
    set ent:last_temp_trend color;
  }
}\t\t      

We calculate the trend color in the prelude, comparing the current temperature to a running average temperature (here's a blog post describing how that's calculated) using the within_bound() function to debounce the trend signal. The action in this rule, radstat:set_color(color), sets the color on the thermostat to the calculated trend color. Note that this rule is operating independently from the rule that updates Cosm.

Next Up

Now that the endpoint can process directives, I plan to work on making the KRL ruleset function as the thermostat, reading desired temperature setpoints from a Google calendar and changing the settings in the thermostat appropriately. All the pieces are on hand now, it's simply a matter of assembly.