-
Notifications
You must be signed in to change notification settings - Fork 7
PerlExampleScript
The Perl modules within this project provide support for reading and writing all of the thermostats' settings and status that can be accessed via their Wi-Fi system protocol interface. It is relatively simple to create new scripts using these libraries. Alternatively, there is a script that provides the same access from other languages using a JSON control interface.
To illustrate the process we will create a new script called heatmiser_on.pl
that sets the target temperature of a single thermostat to the value specified on the command line.
The script starts with a standard hashbang (#!
) line specifying that the Perl interpreter should be used to run the program:
#!/usr/bin/perl
To help with debugging and catch typos quickly turn on some extra diagnostics (this is optional, but recommended):
use strict;
use warnings;
Add the script's directory to the search path for loading Perl modules:
use Cwd 'abs_path';
use File::Basename;
use lib dirname(abs_path $0);
The first activity is to parse the options specified on the command line and to combine them with any configuration file that may exist.
The script will optionally take the thermostat's hostname and PIN access code, but will always require the temperature to be specified:
./heatmiser_on.pl -h heatmiser -p 1234 25
These parameters will be processed using the Getopt::Std library that comes with all Perl distributions. This is suitable for simple scripts, but can be easily substituted if more advanced parsing is required.
use Getopt::Std;
my ($prog) = $0 =~ /([^\\\/]+$)/;
sub VERSION_MESSAGE { print "Heatmiser Wi-Fi Thermostat Set Temperature v1\n"; }
sub HELP_MESSAGE { print "Usage: $prog [-h <host>] [-p <pin>] <temperature>\n"; }
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our ($opt_h, $opt_p);
getopts('h:p:');
At this point $opt_h
and $opt_p
will contain the hostname and PIN access code if specified on the command line (or will be set to undef
if those parameters were not specified). The temperature parameter will be left in the @ARGV
array, so extract that too:
die "No temperature specified\n" unless scalar @ARGV == 1 and $ARGV[0] =~ /^\d+$/;
my $temperature = $ARGV[0];
The next step is to read the configuration file to pick up default values. The heatmiser_config.pm
library handles this, so include it in the script:
use heatmiser_config;
The /etc/heatmiser.conf
and ~/.heatmiser
configuration files are automatically loaded; it is not necessary to load them explicitly. Use the command line parameters to override settings from the configuration files:
heatmiser_config::set(host => [h => $opt_h], pin => [p => $opt_p]);
This specifies two configuration options that should be set. Taking the first as an example, it is setting a configuration option called host
based on the $opt_h
value extracted from the command line. If $opt_h
is undef
then it will leave the value obtained from the configuration file. If the configuration file also did not specify a value then an error will be reported, suggesting that a value should be set either via a HOST <value>
entry in a configuration file or an -h <value>
command line switch.
The scripts use several standard configuration options, but new ones can be added simply by setting their values in the same way. A few of the standard options are allowed to not have values set, but any new ones are required to have a value otherwise an error will be reported.
The host
configuration option is treated specially. It is parsed as a space-separated list of host names and returned as an array reference when its value is requested.
The main Perl module used to communicate with the thermostat is heatmiser_wifi.pm
so include it in the script:
use heatmiser_wifi;
Create an object to handle communication with the thermostat:
my $heatmiser = new heatmiser_wifi(host => heatmiser_config::get_item('host')->[0],
heatmiser_config::get(qw(pin)));
The constructor for the heatmiser_wifi
object is passed a hash with two (key => value)
pairs:
- The first is the
host
(IP address or hostname) to connect to, which is set to the first entry of the array of hosts that has been configured. - The second is the
pin
(PIN access code) for the thermostat's Wi-Fi system protocol interface. Theheatmiser_config::get
method takes a list of configuration option names and returns a hash mapping those names to values.
The created object can then be used to read the thermostat's Device Control Block (DCB):
my @pre_dcb = $heatmiser->read_dcb();
The raw DCB is an array of octets exactly as returned by the thermostat. To make it easier to process, this is converted into a more meaningful Perl data structure, allowing the current target temperature to be displayed:
my $pre_status = $heatmiser->dcb_to_status(@pre_dcb);
my $units = $pre_status->{config}->{units};
print "Before: $pre_status->{heating}->{target}$units\n";
Setting the thermostat's target temperature is essentially the reverse of the procedure used to read it. The first step is to convert a description of the required settings into the corresponding DCB array:
my @items = $heatmiser->status_to_dcb($pre_status,
enabled => 1,
runmode => 'heating',
holiday => { enabled => 0 },
heating => { target => $temperature });
The decoded DCB read earlier from the thermostat is required to be able to format the DCB correctly since its format varies between different models.
To ensure that the thermostat is active it is also being switched on (enabled => 1
), set to normal heating mode (runmode => 'heating'
) instead of frost protection, and any holiday is cancelled (holiday => { enabled => 0 }
). Obviously the temperature is also set to the specified value (heating => { target => $temperature }
).
Note that only some of the settings can be written via the thermostat's Wi-Fi system protocol interface.
The DCB can then be written to the thermostat to set the temperature:
my @post_dcb = $heatmiser->write_dcb(@items);
This also reads the DCB again, so the target temperature that has been set can be displayed:
my $post_status = $heatmiser->dcb_to_status(@post_dcb);
print "After: $post_status->{heating}->{target}$units\n";
Putting all of the pieces together, here is the final script to allow the thermostat's target temperature to be set from the command line:
#!/usr/bin/perl
# Catch errors quickly
use strict;
use warnings;
# Allow use of modules in the same directory
use Cwd 'abs_path';
use File::Basename;
use lib dirname(abs_path $0);
# Useful libraries
use Getopt::Std;
use heatmiser_config;
use heatmiser_wifi;
# Command line options
my ($prog) = $0 =~ /([^\\\/]+$)/;
sub VERSION_MESSAGE { print "Heatmiser Wi-Fi Thermostat Set Temperature v1\n"; }
sub HELP_MESSAGE { print "Usage: $prog [-h <host>] [-p <pin>] <temperature>\n"; }
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our ($opt_h, $opt_p);
getopts('h:p:');
die "No temperature specified\n" unless scalar @ARGV == 1 and $ARGV[0] =~ /^\d+$/;
my $temperature = $ARGV[0];
heatmiser_config::set(host => [h => $opt_h], pin => [p => $opt_p]);
# Connect to the first configured or specified thermostat
my $heatmiser = new heatmiser_wifi(host => heatmiser_config::get_item('host')->[0],
heatmiser_config::get(qw(pin)));
my @pre_dcb = $heatmiser->read_dcb();
my $pre_status = $heatmiser->dcb_to_status(@pre_dcb);
my $units = $pre_status->{config}->{units};
print "Before: $pre_status->{heating}->{target}$units\n";
# Set the temperature, ensuring that the heating is enabled
my @items = $heatmiser->status_to_dcb($pre_status,
enabled => 1,
runmode => 'heating',
holiday => { enabled => 0 },
heating => { target => $temperature });
my @post_dcb = $heatmiser->write_dcb(@items);
my $post_status = $heatmiser->dcb_to_status(@post_dcb);
print "After: $post_status->{heating}->{target}$units\n";
# That's all folks
exit;
Hopefully this provides sufficient introduction to be able to produce new scripts for interrogating or controlling the thermostats. Refer to the Heatmiser V3 System Protocol documentation and Perl DCB data structure documentation for details of all the settings and status that can be read or written.
Home | Installation | Troubleshooting | Contact | © Copyright 2011-2015 Alexander Thoukydides
Due to Heatmiser's lack of support for their pre-Neo models I have replaced mine with a Nest Learning Thermostat (3rd generation).
- Project Home
- Installation Instructions
- Reference Information
- Acknowledgements
- Contact
- Heatmiser Websites
- Related Projects
- Other Resources