mirror of
https://github.com/alexreinert/homematic_check_mk.git
synced 2023-10-10 13:37:02 +02:00
Initial commit
This commit is contained in:
commit
594331e987
55
addon/addon/common.tcl
Normal file
55
addon/addon/common.tcl
Normal file
@ -0,0 +1,55 @@
|
||||
set ADDON_NAME "check_mk_agent"
|
||||
|
||||
proc log { message } {
|
||||
global ADDON_NAME
|
||||
exec logger "$ADDON_NAME - $message"
|
||||
}
|
||||
|
||||
proc save_to_file { fileName content } {
|
||||
set fd -1
|
||||
|
||||
set fd [open $fileName w]
|
||||
if { $fd != -1 } then {
|
||||
puts -nonewline $fd $content
|
||||
close $fd
|
||||
} else {
|
||||
error "could not write file $fileName"
|
||||
}
|
||||
}
|
||||
|
||||
proc load_from_file { fileName } {
|
||||
set fd -1
|
||||
|
||||
set fd [open $fileName r]
|
||||
if { $fd != -1 } then {
|
||||
set result [read $fd]
|
||||
close $fd
|
||||
} else {
|
||||
error "could not read file $fileName"
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
set PID_FILE "/var/lock/$ADDON_NAME.pid"
|
||||
|
||||
proc is_running { } {
|
||||
global PID_FILE
|
||||
return [file exists $PID_FILE]
|
||||
}
|
||||
|
||||
proc write_pid_file { } {
|
||||
global PID_FILE
|
||||
save_to_file $PID_FILE [pid]
|
||||
}
|
||||
|
||||
proc read_pid_file { } {
|
||||
global PID_FILE
|
||||
return [load_from_file $PID_FILE]
|
||||
}
|
||||
|
||||
proc remove_pid_file { } {
|
||||
global PID_FILE
|
||||
file delete $PID_FILE
|
||||
}
|
||||
|
178
addon/addon/server.tcl
Normal file
178
addon/addon/server.tcl
Normal file
@ -0,0 +1,178 @@
|
||||
#!/bin/tclsh
|
||||
load tclrega.so
|
||||
load tclrpc.so
|
||||
|
||||
source [file join [file dirname [info script]] common.tcl]
|
||||
|
||||
proc handle_connection { channelId clientAddress clientPort } {
|
||||
if { [catch {
|
||||
log "connection accepted from $clientAddress:$clientPort"
|
||||
|
||||
puts $channelId "<<<check_mk>>>"
|
||||
puts $channelId "Version: [get_version]"
|
||||
puts $channelId "AgentOS: HomeMatic"
|
||||
puts $channelId "Hostname: [info hostname]"
|
||||
|
||||
puts $channelId "<<<mem>>>"
|
||||
puts $channelId [string trim [load_from_file /proc/meminfo]]
|
||||
|
||||
puts $channelId "<<<cpu>>>"
|
||||
puts $channelId "[string trim [load_from_file /proc/loadavg]] [exec grep -E ^(P|p)rocessor < /proc/cpuinfo | wc -l]"
|
||||
|
||||
puts $channelId "<<<uptime>>>"
|
||||
puts $channelId [string trim [load_from_file /proc/uptime]]
|
||||
|
||||
puts $channelId "<<<kernel>>>"
|
||||
puts $channelId [clock seconds]
|
||||
puts $channelId [string trim [load_from_file /proc/vmstat]]
|
||||
puts $channelId [string trim [load_from_file /proc/stat]]
|
||||
|
||||
if { [file exists /sys/class/thermal/thermal_zone0/temp] == 1} {
|
||||
puts $channelId "<<<lnx_thermal>>>"
|
||||
puts $channelId "thermal_zone0 enabled [string trim [load_from_file /sys/class/thermal/thermal_zone0/type]] [string trim [load_from_file /sys/class/thermal/thermal_zone0/temp]]"
|
||||
}
|
||||
|
||||
if { [file exists /proc/net/tcp6] == 1 } {
|
||||
puts $channelId "<<<tcp_conn_stats>>>"
|
||||
puts $channelId "[exec cat /proc/net/tcp /proc/net/tcp6 2>/dev/null | awk { /:/ { c[$4]++; } END { for (x in c) { print x, c[x]; } } }]"
|
||||
} else {
|
||||
puts $channelId "<<<tcp_conn_stats>>>"
|
||||
puts $channelId "[exec cat /proc/net/tcp 2>/dev/null | awk { /:/ { c[$4]++; } END { for (x in c) { print x, c[x]; } } }]"
|
||||
}
|
||||
|
||||
puts $channelId "<<<lnx_if>>>"
|
||||
puts $channelId "\[start_iplink\]"
|
||||
puts $channelId "[exec ip link]"
|
||||
puts $channelId "\[end_iplink\]"
|
||||
|
||||
puts $channelId "<<<lnx_if:sep(58)>>>"
|
||||
puts $channelId "[exec sed 1,2d /proc/net/dev]"
|
||||
|
||||
if { [regexp CCU2 [exec grep Hardware < /proc/cpuinfo]] == 0 } {
|
||||
puts $channelId "<<<df>>>"
|
||||
puts $channelId "[exec df -PTk | sed 1d]"
|
||||
|
||||
puts $channelId "<<<mounts>>>"
|
||||
puts $channelId "[exec grep ^/dev < /proc/mounts]"
|
||||
|
||||
puts $channelId "<<<diskstat>>>"
|
||||
puts $channelId "[clock seconds]"
|
||||
puts $channelId "[exec egrep { (x?[shv]d[a-z]*|cciss/c[0-9]+d[0-9]+|emcpower[a-z]+|dm-[0-9]+|VxVM.*|mmcblk.*|dasd[a-z]*|bcache[0-9]+|nvme[0-9]+n[0-9]+) } < /proc/diskstats]"
|
||||
}
|
||||
|
||||
if { [file exists /usr/bin/ntpq] == 1 } {
|
||||
puts $channelId "<<<ntp>>>"
|
||||
puts $channelId "[exec ntpq -np | sed -e 1,2d -e {s/^\(.\)/\1 /} -e {s/^ /%/}]"
|
||||
}
|
||||
|
||||
puts $channelId "<<<homematic:sep(59)>>>"
|
||||
puts $channelId [string trim [get_homematic_check_result]]
|
||||
foreach dev [xmlrpc http://127.0.0.1:2001/ listBidcosInterfaces] {
|
||||
foreach {key value} [split $dev] {
|
||||
set values($key) $value
|
||||
}
|
||||
set address $values(ADDRESS)
|
||||
foreach key [array names values] {
|
||||
if { $key != "ADDRESS" } {
|
||||
puts $channelId "$address;$key;$values($key)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flush $channelId
|
||||
|
||||
close $channelId
|
||||
} err] } {
|
||||
log $err
|
||||
[catch { close $channelId }]
|
||||
}
|
||||
}
|
||||
|
||||
proc get_homematic_check_result { } {
|
||||
array set result [rega_script {
|
||||
string _svcId;
|
||||
object _svc;
|
||||
string _devId;
|
||||
object _dev;
|
||||
string _chId;
|
||||
object _ch;
|
||||
string _dpId;
|
||||
object _dp;
|
||||
string _name;
|
||||
|
||||
foreach (_svcId, dom.GetObject(ID_SERVICES).EnumUsedIDs()) {
|
||||
_svc = dom.GetObject(_svcId);
|
||||
if (_svc.AlState() == asOncoming) {
|
||||
_dp = dom.GetObject(_svc.AlTriggerDP());
|
||||
_ch = dom.GetObject(_dp.Channel());
|
||||
_dev = dom.GetObject(_ch.Device());
|
||||
|
||||
WriteLine("SVC_MSG;" # _dev.Name() # ";" # _svc.Name().StrValueByIndex (".", 1).StrValueByIndex ("-", 0) # ";" # _dp.Timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (_chId, dom.GetObject("Monitored").EnumUsedIDs()) {
|
||||
_ch = dom.GetObject(_chId);
|
||||
|
||||
_dev = dom.GetObject(_ch.Device());
|
||||
WriteLine(_ch.Name() # ";HSSTYPE;" # _dev.HssType());
|
||||
|
||||
foreach (_dpId, _ch.DPs()) {
|
||||
_dp = dom.GetObject(_dpId);
|
||||
|
||||
if (_dp.Value()) {
|
||||
_name = _dp.Name().StrValueByIndex(".", 2);
|
||||
WriteLine(_ch.Name() # ";" # _name # ";" # _dp.Value() # ";" # _dp.Timestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
return $result(STDOUT)
|
||||
}
|
||||
|
||||
proc get_homematic_bidcos_devices { } {
|
||||
foreach dev [xmlrpc http://127.0.0.1:2001/ listBidcosInterfaces] {
|
||||
foreach {key value} [split $dev] {
|
||||
puts "$key=$value"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc read_var { filename varname } {
|
||||
set fd [open $filename r]
|
||||
set var ""
|
||||
if { $fd >=0 } {
|
||||
while { [gets $fd buf] >=0 } {
|
||||
if [regexp "^ *$varname *= *(.*)$" $buf dummy var] break
|
||||
}
|
||||
close $fd
|
||||
}
|
||||
return $var
|
||||
}
|
||||
|
||||
proc get_version { } {
|
||||
return [read_var /boot/VERSION VERSION]
|
||||
}
|
||||
|
||||
proc main { } {
|
||||
startup
|
||||
|
||||
socket -server handle_connection 6556
|
||||
log "check_mk agent started and waiting for connections..."
|
||||
|
||||
vwait forever
|
||||
}
|
||||
|
||||
proc startup { } {
|
||||
if {[is_running]} then {
|
||||
error "already running"
|
||||
}
|
||||
|
||||
write_pid_file
|
||||
}
|
||||
|
||||
if { [catch { main } err] } then {
|
||||
log $err
|
||||
exit 1
|
||||
}
|
||||
|
20
addon/addon/stop.tcl
Normal file
20
addon/addon/stop.tcl
Normal file
@ -0,0 +1,20 @@
|
||||
#!/bin/tclsh
|
||||
|
||||
source [file join [file dirname [info script]] common.tcl]
|
||||
|
||||
proc main { } {
|
||||
if { [is_running] } then {
|
||||
set pid [read_pid_file]
|
||||
|
||||
catch { exec kill -KILL $pid }
|
||||
remove_pid_file
|
||||
}
|
||||
|
||||
log "stopped"
|
||||
}
|
||||
|
||||
if { [catch { main } errorMessage] } then {
|
||||
log $errorMessage
|
||||
exit 1
|
||||
}
|
||||
|
47
addon/rc.d/check_mk_agent
Normal file
47
addon/rc.d/check_mk_agent
Normal file
@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
|
||||
ADDONNAME=check_mk_agent
|
||||
ADDONDIR=/usr/local/addons/${ADDONNAME}
|
||||
#WWWDIR=/usr/local/etc/config/addons/www/${ADDONNAME}
|
||||
RCDDIR=/usr/local/etc/config/rc.d
|
||||
|
||||
case "$1" in
|
||||
|
||||
""|start)
|
||||
tclsh $ADDONDIR/server.tcl &
|
||||
;;
|
||||
|
||||
stop)
|
||||
tclsh $ADDONDIR/stop.tcl
|
||||
;;
|
||||
|
||||
restart|reload)
|
||||
tclsh $ADDONDIR/stop.tcl
|
||||
sleep 2
|
||||
tclsh $ADDONDIR/server.tcl &
|
||||
;;
|
||||
|
||||
info)
|
||||
echo "Info: <b>(Inoffical) check_mk agent</b>"
|
||||
echo "Version: 1.0"
|
||||
echo "Name: check_mk_agent"
|
||||
echo "Operations: uninstall restart"
|
||||
;;
|
||||
|
||||
uninstall)
|
||||
tclsh $ADDONDIR/stop.tcl
|
||||
|
||||
rm -rf ${ADDONDIR}
|
||||
rm -rf ${WWWDIR}
|
||||
rm -f ${RCDDIR}/${ADDONNAME}
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: check_mk_agent {start|stop|restart|info|uninstall}" >&2
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit $?
|
||||
|
22
addon/update_script
Normal file
22
addon/update_script
Normal file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
ADDONNAME=check_mk_agent
|
||||
CONFIG_DIR=/usr/local/etc/config
|
||||
ADDON_DIR=/usr/local/addons/${ADDONNAME}
|
||||
RCD_DIR=${CONFIG_DIR}/rc.d
|
||||
|
||||
mount | grep /usr/local 2>&1 >/dev/null
|
||||
if [ $? -eq 1 ]; then
|
||||
mount /usr/local
|
||||
fi
|
||||
|
||||
# create necessary directories
|
||||
mkdir -p ${ADDON_DIR}
|
||||
chmod 755 ${ADDON_DIR}
|
||||
mkdir -p ${RCD_DIR}
|
||||
chmod 755 ${RCD_DIR}
|
||||
|
||||
cp -af addon/* ${ADDON_DIR}
|
||||
cp -af rc.d/* ${RCD_DIR}
|
||||
|
||||
sync
|
167
check/checks/homematic
Normal file
167
check/checks/homematic
Normal file
@ -0,0 +1,167 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
def parse_homematic(info):
|
||||
result = { }
|
||||
for line in info:
|
||||
key = line[0]
|
||||
device = result.get(key)
|
||||
if key == 'SVC_MSG':
|
||||
if device is None:
|
||||
device = []
|
||||
result[key] = device
|
||||
device.append(line[1:])
|
||||
else:
|
||||
if device is None:
|
||||
device = { }
|
||||
result[key] = device
|
||||
device[line[1]] = line[2:]
|
||||
return result
|
||||
|
||||
def inventory_homematic(parsed):
|
||||
yield 'Low Battery devices', None
|
||||
yield 'Unreachable devices', None
|
||||
yield 'Service Messages', None
|
||||
|
||||
def check_homematic(item, params, parsed):
|
||||
messages = parsed.get('SVC_MSG', None);
|
||||
|
||||
state = 0
|
||||
devices = []
|
||||
|
||||
if messages is not None:
|
||||
for msg in messages:
|
||||
if item == 'Low Battery devices':
|
||||
if msg[1] == 'LOWBAT':
|
||||
state = 2
|
||||
devices.append(msg[0])
|
||||
elif item == 'Unreachable devices':
|
||||
if msg[1] == 'UNREACH':
|
||||
state = 2
|
||||
devices.append(msg[0])
|
||||
elif msg[1] == 'STICKY_UNREACH':
|
||||
state = max(state, 1)
|
||||
devices.append(msg[0])
|
||||
elif item == 'Service Messages':
|
||||
if msg[1] not in [ 'LOWBAT', 'UNREACH', 'STICKY_UNREACH' ]:
|
||||
if msg[1] in [ 'CONFIG_PENDING', 'DEVICE_IN_BOOTLOADER', 'UPDATE_PENDING', 'USBH_POWERFAIL' ]:
|
||||
state = max(state, 1)
|
||||
else:
|
||||
state = 2
|
||||
devices.append(msg[0] + ':' + msg[1])
|
||||
|
||||
if state == 0:
|
||||
return 0, 'No issues reported'
|
||||
|
||||
return state, ', '.join(devices)
|
||||
|
||||
check_info['homematic'] = {
|
||||
'check_function': check_homematic,
|
||||
'inventory_function': inventory_homematic,
|
||||
'parse_function': parse_homematic,
|
||||
'service_description': "Homematic %s",
|
||||
}
|
||||
|
||||
def inventory_homematic_humidity(parsed):
|
||||
for line in parsed:
|
||||
data = parsed[line]
|
||||
if isinstance(data, dict):
|
||||
temperature = data.get('TEMPERATURE', data.get('ACTUAL_TEMPERATURE', None))
|
||||
humidity = data.get('HUMIDITY', None)
|
||||
|
||||
if temperature is not None and humidity is not None:
|
||||
yield line, { }
|
||||
|
||||
# data = parsed[line]
|
||||
# if isinstance(data, dict) and data.get('HSSTYPE', 'UNKNOWN') in [ [u'HM-WDS10-TH-O'], [u'HM-WDS20-TH-O'], [u'HM-WDS40-TH-I'], [u'HM-CC-TC'], [u'HM-WDS100-C6-O-2'], [u'HM-WDS100-C6-O'], [u'HM-TC-IT-WM-W-EU'], [u'HM-WDS40-TH-I-2'], [u'HM-WDC7000'], [u'HMIP-WTH'], [u'HmIP-WTH-2'], [u'HmIP-STH'], [u'HmIP-STHD'], [u'HmIP-BWTH'], [u'HmIP-BWTH24'] ]:
|
||||
# yield line, { }
|
||||
|
||||
def check_homematic_humidity(item, params, parsed):
|
||||
for line in parsed:
|
||||
if line == item:
|
||||
data = parsed[line]
|
||||
temperature = data.get('TEMPERATURE', data.get('ACTUAL_TEMPERATURE', None))
|
||||
humidity = data.get('HUMIDITY', None)
|
||||
|
||||
if humidity is None or temperature is None:
|
||||
return 3, 'Humidity or temperature are not available'
|
||||
|
||||
temperature = float(temperature[0])
|
||||
humidity = float(humidity[0])
|
||||
|
||||
if humidity <= params['critical'][0] or humidity >= params['critical'][1]:
|
||||
state = 2
|
||||
elif humidity <= params['warning'][0] or humidity >= params['warning'][1]:
|
||||
state = 1
|
||||
else:
|
||||
state = 0
|
||||
|
||||
message = "Temperature: %f, Humidity: %f" % (temperature, humidity)
|
||||
perfdata = [
|
||||
('temperature', temperature),
|
||||
('humidity', humidity),
|
||||
]
|
||||
|
||||
return state, message, perfdata
|
||||
|
||||
factory_settings['homematic_humidity_default_levels'] = {
|
||||
'warning' : (40, 60),
|
||||
'critical': (35, 65),
|
||||
}
|
||||
|
||||
homematic_humidity_default_levels = { }
|
||||
|
||||
check_info['homematic.humidity'] = {
|
||||
'check_function': check_homematic_humidity,
|
||||
'inventory_function': inventory_homematic_humidity,
|
||||
'service_description': "Humidity %s",
|
||||
'group': 'homematic_humidity',
|
||||
'default_levels_variable': 'homematic_humidity_default_levels',
|
||||
'has_perfdata': True,
|
||||
}
|
||||
|
||||
def inventory_homematic_dutycycle(parsed):
|
||||
for line in parsed:
|
||||
data = parsed[line]
|
||||
if isinstance(data, dict) and data.get('DUTY_CYCLE', None) is not None:
|
||||
yield line, { }
|
||||
|
||||
def check_homematic_dutycycle(item, params, parsed):
|
||||
for line in parsed:
|
||||
if line == item:
|
||||
data = parsed[line]
|
||||
dutycycle = data.get('DUTY_CYCLE', None)
|
||||
|
||||
if dutycycle is None :
|
||||
return 3, 'Duty cycle is not available'
|
||||
|
||||
dutycycle = float(dutycycle[0])
|
||||
if dutycycle >= params['critical']:
|
||||
state = 2
|
||||
elif dutycycle >= params['warning']:
|
||||
state = 1
|
||||
else:
|
||||
state = 0
|
||||
|
||||
message = "Duty cycle: %f" % (dutycycle)
|
||||
perfdata = [
|
||||
('dutycycle', dutycycle, params['warning'], params['critical'], 0, 100),
|
||||
]
|
||||
|
||||
return state, message, perfdata
|
||||
|
||||
factory_settings['homematic_dutycycle_default_levels'] = {
|
||||
'warning' : 50,
|
||||
'critical': 70,
|
||||
}
|
||||
|
||||
homematic_dutycycle_default_levels = { }
|
||||
|
||||
check_info['homematic.dutycycle'] = {
|
||||
'check_function': check_homematic_dutycycle,
|
||||
'inventory_function': inventory_homematic_dutycycle,
|
||||
'service_description': "Duty cycle %s",
|
||||
'group': 'homematic_dutycycle',
|
||||
'default_levels_variable': 'homematic_dutycycle_default_levels',
|
||||
'has_perfdata': True,
|
||||
}
|
||||
|
20
check/homematic
Normal file
20
check/homematic
Normal file
@ -0,0 +1,20 @@
|
||||
{'author': 'Alexander Reinert <alex@areinert.de>',
|
||||
'description': 'Homematic device checks to use with the CCU check_mk_agent addon',
|
||||
'download_url': 'https://github.com/alexreinert/homematic_check_mk',
|
||||
'files': {'agents': [],
|
||||
'bin': [],
|
||||
'checkman': [],
|
||||
'checks': ['homematic'],
|
||||
'doc': [],
|
||||
'inventory': [],
|
||||
'lib': [],
|
||||
'mibs': [],
|
||||
'notifications': [],
|
||||
'pnp-templates': [],
|
||||
'web': ['plugins/perfometer/homematic.py',
|
||||
'plugins/wato/homematic.py']},
|
||||
'name': 'homematic',
|
||||
'title': 'Homematic',
|
||||
'version': '1.0',
|
||||
'version.min_required': '1.2.8',
|
||||
'version.packaged': '1.4.0p5'}
|
9
check/web/plugins/perfometer/homematic.py
Normal file
9
check/web/plugins/perfometer/homematic.py
Normal file
@ -0,0 +1,9 @@
|
||||
def perfometer_check_mk_homematic_dutycycle(row, check_command, perf_data):
|
||||
dutycycle = float(perf_data[0][1])
|
||||
state = row["service_state"]
|
||||
color = { 0: "#39f", 1: "#ff2", 2: "#f22", 3: "#fa2" }[state]
|
||||
|
||||
return "%.0f%%" % dutycycle, perfometer_linear(dutycycle, color)
|
||||
|
||||
perfometers["check_mk-homematic.dutycycle"] = perfometer_check_mk_homematic_dutycycle
|
||||
|
59
check/web/plugins/wato/homematic.py
Normal file
59
check/web/plugins/wato/homematic.py
Normal file
@ -0,0 +1,59 @@
|
||||
checkgroups = []
|
||||
subgroup_os = _("Temperature, Humidity, Electrical Parameters, etc.")
|
||||
|
||||
register_check_parameters(
|
||||
subgroup_os,
|
||||
"homematic_humidity",
|
||||
_("Homematic Humidity"),
|
||||
Dictionary(
|
||||
title = _("Parameters for the Homematic Humidity check"),
|
||||
help = _(""),
|
||||
elements = [
|
||||
( "warning",
|
||||
Tuple(
|
||||
title = _("Warning level of humidity"),
|
||||
elements = [
|
||||
Integer(title = _("Lower than"), default_value = 40),
|
||||
Integer(title = _("Higher than"), default_value = 60),
|
||||
],
|
||||
),
|
||||
),
|
||||
( "critical",
|
||||
Tuple(
|
||||
title = _("Critical level of humidity"),
|
||||
elements = [
|
||||
Integer(title = _("Lower than"), default_value = 30),
|
||||
Integer(title = _("Higher than"), default_value = 70),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextAscii(
|
||||
title = _("Name of the device"),
|
||||
),
|
||||
match_type = "dict",
|
||||
)
|
||||
|
||||
register_check_parameters(
|
||||
subgroup_os,
|
||||
"homematic_dutycycle",
|
||||
_("Homematic Duty cycle"),
|
||||
Dictionary(
|
||||
title = _("Parameters for the Homematic Duty cycle check"),
|
||||
help = _(""),
|
||||
elements = [
|
||||
( "warning",
|
||||
Integer(title = _("Warning level of duty cycle"), default_value = 50)
|
||||
),
|
||||
( "critical",
|
||||
Integer(title = _("Critical level of duty cycle"), default_value = 70)
|
||||
),
|
||||
],
|
||||
),
|
||||
TextAscii(
|
||||
title = _("Name of the device"),
|
||||
),
|
||||
match_type = "dict",
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user