#!/usr/bin/perl -w
##########################################################################################################
#
# Munin plugin to monitor values reported by a Voltcraft TCM 220 or 320 temperature swicth module 
# connected to a serial port. A cable needs to be soldered to the module's dedicated data output pads: 
# pad no. 9 (GND) to pin no. 5 of the serial port, pad no. 17 (DATA_OUT) to pin no. 2 of the serial port 
# and pad no. 18 (CLOCK) to pin no. 3 of the serial port. For more details, check out the documentation 
# that came with your Voltcraft TCM 220/320 temperature swicth module.
#
# In order to use this plugin, copy it to the munin's plugin directory (eg. /usr/share/munin/plugins)
# under the name "voltcraft_tcm220_". Don't change this filename! Follow these steps:
#
# 1. Symlink it to munin's configured plugins directory (eg. /etc/munin/plugins) with names suggesting
#    the locations of the  modules you wish to monitor, eg: "voltcraft_tcm220_serverroom1".
#
#    Important: make sure to use the same names in your symlinks and other config places!
#
# 2. In /etc/munin/plugin-conf.d/munin-node add the following, to be able to contact the modules via 
#    serial ports (obviously replacing these with your own data):
#
#    [voltcraft_tcm220_serverroom1]
#    user root				# Need to run the plugin as root, in order to access the RS232 port
#    group root
#    env.Port /dev/ttyS0		# Serial port where the module is connected to
#    env.Location Server Room 1		# Friendly name for the module location, printed on the graph
#    env.CH1name Ceiling		# Friendly name for the CH1 sensor location
#    env.CH1crit 35			# Critical temperature value which when reached, Munin should alert
#    env.CH2name Floor			# Friendly name for the CH2 sensor location
#    env.CH2crit 35			# Critical temperature value which when reached, Munin should alert
#
# 3. Restart the munin node by 'service munin-node restart'.
#
#    If all went well, after 5 minutes or so you should have tne new module's graphs listed on the Web 
#    Interface of Munin.
#
# Note: the plugin waits maximum 11 seconds for the module to report the current temperature values to
# the serial port. If no value is reported, or the serial cable is unplugged, it returns Undefined to 
# Munin. According to the documentation, the module can be configured to report values every 2 seconds
# instead the 10 seconds default - this can be used to speed up the data acquisition process, however
# it can decrease battery life (with default values, two AAA alkaline batteries last cca 2 years).
#
# Tested & working with munin v.2.0.19-2 on Ubuntu LTS 12.04.4
# Created in 2014 by robi
#  v0.2 - added checksum validation
#  v0.1 - initial version
#########################################################################################################
## Magic Markers
 #%# family=manual
#########################################################################################################
use diagnostics;
use strict;
use Device::SerialPort;
use warnings;

#########################################################################################################
## Receive environmentals
my $Portty = $ENV{'Port'};
my $Location = $ENV{'Location'};
my $CH1name = $ENV{'CH1name'};
my $CH1crit = $ENV{'CH1crit'};
my $CH2name = $ENV{'CH2name'};
my $CH2crit = $ENV{'CH2crit'};

##########################################################################################################
## Determine scriptname
my $Modname = undef;
$0 =~ /voltcraft_tcm220_(.+)*$/;
unless ($Modname = $1) {
print "Sorry, you did not symlink correctly the script! Please symlink as eg. voltcraft_tcm220_serverroom1\n";
  exit 2;
}

##########################################################################################################
## Configuration
if(exists $ARGV[0] and $ARGV[0] eq "config") {
  print "graph_args --base 1000\n";
  print "graph_title " . $Location . " temperatures\n";
  print "graph_vlabel degrees Celsius\n";
  print "graph_category sensors\n";
  print "graph_scale no\n";
  print "graph_info Measured temperature values in " . $Location . " by a Voltcraft TCM 220 temperature switch module.\n";
  print "ch1.label " . $CH1name . "\n";
  print "ch1.critical ". $CH1crit . "\n";
  print "ch1.info " . $CH1name . " temperature, connected to CH1.\n";
  print "ch2.label " . $CH2name . "\n";
  print "ch2.critical ". $CH2crit . "\n";
  print "ch2.info " . $CH2name . " temperature, connected to CH2.\n";
  print "\n";
  exit;
}

##########################################################################################################
## Initiate serial connection
my $port = Device::SerialPort->new($Portty);
$port->baudrate(9600);
$port->parity("none");
$port->handshake("none");
$port->databits(8);
$port->stopbits(1);
$port->read_char_time(0);
$port->read_const_time(20);

##########################################################################################################
## Execution
my $response = "";
my $resptime = (time() + 10);

while (1) {
	my $curtime = time();
	my ($count, $data) = $port->read(255);
	if ($count > 0) {
		$response .= $data;
		last if ($data // "");
	}
	last if ($curtime > $resptime);
}

my $ch1temp = "U";
my $ch2temp = "U";

if ($response // "") {
	my $hex = unpack 'H*', $response; 
#	print "$hex\n";	#$hex should be something like "abc102999babc2017677" for +29.9 and +17.6

	my $ch1sum = substr($hex, 4, 1) + substr($hex, 5, 1) + substr($hex, 6, 1) + substr($hex, 7, 1);
	my $ch1sul = substr(sprintf("%02d", $ch1sum), 1, 1);

        my $ch2sum = substr($hex, 14, 1) + substr($hex, 15, 1) + substr($hex, 16, 1) + substr($hex, 17, 1);
        my $ch2sul = substr(sprintf("%02d", $ch2sum), 1, 1);

	if (substr($hex, 9, 1) == $ch1sul) {
		my $ch1z = "";
		if (substr($hex, 4, 1) == "5") {
			$ch1z ="-";
		}
		$ch1temp = $ch1z . substr($hex, 5, 2) . "." . substr($hex, 7, 1);
	}

	if (substr($hex, 19, 1) == $ch2sul) {
		my $ch2z = "";
		if (substr($hex, 14, 1) == "5") {
			$ch2z ="-";
		}
		$ch2temp = $ch2z . substr($hex, 15, 2) . "." . substr($hex, 17, 1);
	}
}

print "ch1.value $ch1temp\n";
print "ch2.value $ch2temp\n";
print "\n";

##########################################################################################################
## End
