Added support for I2C MCP9808 sensor (closes #4) based on https://github.com/ControlEverythingCommunity/MCP9808/blob/master/Python/MCP9808.py
This commit is contained in:
@@ -2,10 +2,10 @@
|
|||||||
#
|
#
|
||||||
# check_temperature - Nagios temperature check for DS18B20 sensor on RaspberryPi
|
# check_temperature - Nagios temperature check for DS18B20 sensor on RaspberryPi
|
||||||
#
|
#
|
||||||
# Version 1.1, latest version, documentation and bugtracker available at:
|
# Version 1.2, latest version, documentation and bugtracker available at:
|
||||||
# https://gitlab.lindenaar.net/scripts/nagios-plugins
|
# https://gitlab.lindenaar.net/scripts/nagios-plugins
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017 Frederik Lindenaar
|
# Copyright (c) 2017 - 2019 Frederik Lindenaar
|
||||||
#
|
#
|
||||||
# This script is free software: you can redistribute and/or modify it under the
|
# This script is free software: you can redistribute and/or modify it under the
|
||||||
# terms of version 3 of the GNU General Public License as published by the Free
|
# terms of version 3 of the GNU General Public License as published by the Free
|
||||||
@@ -27,15 +27,21 @@ from argparse import ArgumentParser as StandardArgumentParser, FileType, \
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Constants (no need to change but allows for easy customization)
|
# Constants (no need to change but allows for easy customization)
|
||||||
VERSION="1.1"
|
VERSION="1.2"
|
||||||
PROG_NAME=splitext(basename(__file__))[0]
|
PROG_NAME=splitext(basename(__file__))[0]
|
||||||
PROG_VERSION=PROG_NAME + ' ' + VERSION
|
PROG_VERSION=PROG_NAME + ' ' + VERSION
|
||||||
SENSOR_SCALE=1000
|
|
||||||
|
CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp'
|
||||||
|
I2C_MCP9808_CONFIG_ADDR=0x1
|
||||||
|
I2C_MCP9808_CONFIG = [ 0x00, 0x00 ] # continuous conversion (power-up default)
|
||||||
|
I2C_MCP9808_PRECISION_ADDR=0x08
|
||||||
|
I2C_MCP9808_PRECISION=3 # 0=0.5, 1=0.25, 2=0.125, 3=0.0625 degr. C
|
||||||
|
I2C_MCP9808_TEMP_ADDR=0x05
|
||||||
W1_SENSOR_DEV_DIR = '/sys/bus/w1/devices/'
|
W1_SENSOR_DEV_DIR = '/sys/bus/w1/devices/'
|
||||||
W1_SENSOR_DEV_PREFIX = '28-'
|
W1_SENSOR_DEV_PREFIX = '28-'
|
||||||
W1_SENSOR_DEV_SUFFIX = '/w1_slave'
|
W1_SENSOR_DEV_SUFFIX = '/w1_slave'
|
||||||
W1_SENSOR_READ_RETRIES=10
|
W1_SENSOR_READ_RETRIES=10
|
||||||
CPU_SENSOR_DEV = '/sys/class/thermal/thermal_zone0/temp'
|
W1_SENSOR_SCALE=1000
|
||||||
|
|
||||||
LOG_FORMAT='%(levelname)s - %(message)s'
|
LOG_FORMAT='%(levelname)s - %(message)s'
|
||||||
LOG_FORMAT_FILE='%(asctime)s - ' + LOG_FORMAT
|
LOG_FORMAT_FILE='%(asctime)s - ' + LOG_FORMAT
|
||||||
@@ -81,15 +87,15 @@ class SetLogFile(StoreAction):
|
|||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
def convert_celcius(temp_read):
|
def convert_celcius(temp_read, scale = 1):
|
||||||
"""Converts raw temperature sensore value to degrees Celcius"""
|
"""Converts raw temperature sensore value to degrees Celcius"""
|
||||||
return float(temp_read) / float(SENSOR_SCALE)
|
return float(temp_read) / float(scale)
|
||||||
CONVERT_CELCIUS = ( convert_celcius, 'C', 'Celcius' )
|
CONVERT_CELCIUS = ( convert_celcius, 'C', 'Celcius' )
|
||||||
|
|
||||||
|
|
||||||
def convert_farenheit(temp_read):
|
def convert_farenheit(temp_read, scale = 1):
|
||||||
"""Converts raw temperature sensore value to degrees Farenheit"""
|
"""Converts raw temperature sensore value to degrees Farenheit"""
|
||||||
return float(temp_read * 9) / float(5 * SENSOR_SCALE) + 32.0
|
return float(temp_read * 9) / float(5 * scale) + 32.0
|
||||||
CONVERT_FARENHEIT = ( convert_farenheit, 'F', 'Farenheit' )
|
CONVERT_FARENHEIT = ( convert_farenheit, 'F', 'Farenheit' )
|
||||||
|
|
||||||
|
|
||||||
@@ -98,6 +104,11 @@ def isempty(string):
|
|||||||
return string is None or len(string) == 0
|
return string is None or len(string) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def hex_int(string):
|
||||||
|
"""Use int()'s auto-detection to parse 10-base and 16-base (0x..) numbers"""
|
||||||
|
return int(string, 0);
|
||||||
|
|
||||||
|
|
||||||
def read_rpi_cpu_temp(args):
|
def read_rpi_cpu_temp(args):
|
||||||
"""Reads CPU temperature and converts it to desired unit, returns temperature"""
|
"""Reads CPU temperature and converts it to desired unit, returns temperature"""
|
||||||
with open(args.file, 'r') as f:
|
with open(args.file, 'r') as f:
|
||||||
@@ -111,6 +122,51 @@ def read_rpi_cpu_temp(args):
|
|||||||
return temp, 1
|
return temp, 1
|
||||||
|
|
||||||
|
|
||||||
|
def read_i2c_mcp9808_temp(args):
|
||||||
|
"""Returns temperature from I2C MCP9808 sensor in desired unit"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
import smbus
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
import smbus2 as smbus
|
||||||
|
except ImportError:
|
||||||
|
logger.critical("Unable to import either smbus or smbus2 library");
|
||||||
|
raise ImportError("missing I2C library, please install python-smbus or smbus2");
|
||||||
|
|
||||||
|
try:
|
||||||
|
bus = smbus.SMBus(args.i2cbus) # Get I2C bus
|
||||||
|
except OSError as e:
|
||||||
|
logger.critical(e)
|
||||||
|
raise IOError("Invalid I2C bus: %d" % args.i2cbus)
|
||||||
|
|
||||||
|
try:
|
||||||
|
bus.write_i2c_block_data(args.address, I2C_MCP9808_CONFIG_ADDR,
|
||||||
|
I2C_MCP9808_CONFIG)
|
||||||
|
bus.write_byte_data(args.address, I2C_MCP9808_PRECISION_ADDR,
|
||||||
|
I2C_MCP9808_PRECISION)
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
# Read temperature (0x05), 2 bytes (MSB, LSB)
|
||||||
|
data = bus.read_i2c_block_data(args.address, I2C_MCP9808_TEMP_ADDR, 2)
|
||||||
|
logger.debug('Temperature sensor data from MCP9808 %#02x: 0x%02x%02x',
|
||||||
|
args.address, data[0], data[1])
|
||||||
|
|
||||||
|
# Convert the data to 13-bits
|
||||||
|
temp_read = ((data[0] & 0x1F) * 256) + data[1]
|
||||||
|
if temp_read > 4095 :
|
||||||
|
temp_read -= 8192
|
||||||
|
|
||||||
|
temp = args.converter[0](temp_read, 16)
|
||||||
|
logger.debug('Temperature sensor value %d is %.2f%s',
|
||||||
|
temp_read, temp, args.converter[1])
|
||||||
|
return temp, 1
|
||||||
|
except IOError as e:
|
||||||
|
logger.critical(e)
|
||||||
|
raise IOError("Error while communicating with I2C device %#02x" %
|
||||||
|
args.address)
|
||||||
|
|
||||||
|
|
||||||
def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR,
|
def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR,
|
||||||
prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX):
|
prefix=W1_SENSOR_DEV_PREFIX, suffix=W1_SENSOR_DEV_SUFFIX):
|
||||||
"""Auto-determine sensor datafile name (unless args.file is set)"""
|
"""Auto-determine sensor datafile name (unless args.file is set)"""
|
||||||
@@ -138,8 +194,8 @@ def get_w1_sensor_device_filename(args, dev_dir=W1_SENSOR_DEV_DIR,
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def read_w1_temp(args):
|
def read_w1_ds18b20_temp(args):
|
||||||
"""Reads sensor data and converts it to desired unit, returns temperature"""
|
"""Returns temperature from 1-wire ds18b20 sensor in desired unit"""
|
||||||
device_file = get_w1_sensor_device_filename(args)
|
device_file = get_w1_sensor_device_filename(args)
|
||||||
lines=[ '' ]
|
lines=[ '' ]
|
||||||
tries = 0
|
tries = 0
|
||||||
@@ -160,7 +216,7 @@ def read_w1_temp(args):
|
|||||||
errmsg = 'temperature sensor data format is not supported'
|
errmsg = 'temperature sensor data format is not supported'
|
||||||
else:
|
else:
|
||||||
temp_read = int(lines[1][equals_pos+2:])
|
temp_read = int(lines[1][equals_pos+2:])
|
||||||
temp = args.converter[0](temp_read)
|
temp = args.converter[0](temp_read, W1_SENSOR_SCALE)
|
||||||
logger.debug('Temperature sensor value %d is %.2f%s', temp_read,
|
logger.debug('Temperature sensor value %d is %.2f%s', temp_read,
|
||||||
temp, args.converter[1])
|
temp, args.converter[1])
|
||||||
return temp, tries
|
return temp, tries
|
||||||
@@ -174,11 +230,7 @@ def parse_args():
|
|||||||
|
|
||||||
# Setup argument parser, the workhorse gluing it all together
|
# Setup argument parser, the workhorse gluing it all together
|
||||||
parser = ArgumentParser(
|
parser = ArgumentParser(
|
||||||
epilog='(*) by default the script will look for the first device that '
|
description='Nagios check plugin for temperature sensors on RaspberryPi'
|
||||||
'matches %s* in %s, if multiple entries are found -s or -f must '
|
|
||||||
'be used to specify which sensor to read.' %
|
|
||||||
(W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR),
|
|
||||||
description='Nagios check plugin for 1-wire temp. sensor on RaspberryPi'
|
|
||||||
)
|
)
|
||||||
parser.add_argument('-V', '--version',action="version",version=PROG_VERSION)
|
parser.add_argument('-V', '--version',action="version",version=PROG_VERSION)
|
||||||
|
|
||||||
@@ -218,6 +270,16 @@ def parse_args():
|
|||||||
help='read built-in Raspberry Pi CPU temperature')
|
help='read built-in Raspberry Pi CPU temperature')
|
||||||
cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0)
|
cmdparser.set_defaults(func=read_rpi_cpu_temp, cmdparser=cmdparser, retries=0)
|
||||||
|
|
||||||
|
i2cparser = ArgumentParser(add_help=False)
|
||||||
|
i2cparser.add_argument('-a', '--address', type=hex_int,
|
||||||
|
help='I2C Address of sensor, use 0x.. for hex (*)')
|
||||||
|
i2cparser.add_argument('-b', '--i2cbus', default=1, type=int,
|
||||||
|
help='I2C Bus to use (defaults to 1)')
|
||||||
|
cmdparser = subparser.add_parser('i2c_mcp9808', parents=[i2cparser],
|
||||||
|
help='read I2C connected MCP9808 sensor',
|
||||||
|
epilog='(*) default I2C address for an MCP9808 is 0x18')
|
||||||
|
cmdparser.set_defaults(func=read_i2c_mcp9808_temp, cmdparser=cmdparser, retries=0, address=0x18)
|
||||||
|
|
||||||
w1parser = ArgumentParser(add_help=False)
|
w1parser = ArgumentParser(add_help=False)
|
||||||
pgroup = w1parser.add_mutually_exclusive_group(required=False)
|
pgroup = w1parser.add_mutually_exclusive_group(required=False)
|
||||||
pgroup.add_argument('-s', '--serial',
|
pgroup.add_argument('-s', '--serial',
|
||||||
@@ -228,8 +290,12 @@ def parse_args():
|
|||||||
help='number of times to retry reading sensor data when'
|
help='number of times to retry reading sensor data when'
|
||||||
' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES)
|
' unstable (defaults to %d)' % W1_SENSOR_READ_RETRIES)
|
||||||
cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser],
|
cmdparser = subparser.add_parser('w1_ds18b20', parents=[w1parser],
|
||||||
help='read 1-wire connected DS18b20 sensor')
|
help='read 1-wire connected DS18b20 sensor',
|
||||||
cmdparser.set_defaults(func=read_w1_temp, cmdparser=cmdparser)
|
epilog='(*) by default the script will look for the first device that '
|
||||||
|
'matches %s* in %s, if multiple entries are found -s or -f must '
|
||||||
|
'be used to specify which sensor to read.' %
|
||||||
|
(W1_SENSOR_DEV_PREFIX, W1_SENSOR_DEV_DIR))
|
||||||
|
cmdparser.set_defaults(func=read_w1_ds18b20_temp, cmdparser=cmdparser)
|
||||||
|
|
||||||
# parse arguments and post-process command line options
|
# parse arguments and post-process command line options
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -268,7 +334,7 @@ if __name__ == '__main__':
|
|||||||
except (KeyboardInterrupt) as e:
|
except (KeyboardInterrupt) as e:
|
||||||
nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read aborted by user')
|
nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read aborted by user')
|
||||||
|
|
||||||
except (IOError, ValueError) as e:
|
except (IOError, ValueError, ImportError) as e:
|
||||||
nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read failed: %s' % e)
|
nagios_exit(NAGIOS_UNKNOWN,'temperature sensor read failed: %s' % e)
|
||||||
|
|
||||||
elapse = endtime-starttime
|
elapse = endtime-starttime
|
||||||
|
|||||||
Reference in New Issue
Block a user