#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# ails.  You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.


# Example SNMP walk:
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.1 Temperature.DescName
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.2 Temperature.Value
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.3 Temperature.Offset
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.4 Temperature.SetPtHighAlarm
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.5 Temperature.SetPtHighWarning
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.6 Temperature.SetPtLowWarning
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.7 Temperature.SetPtLowAlarm
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.8 Temperature.Hysteresis
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.9 Temperature.Status
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.10 Temperature.Category
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.11 Access.DescName
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.12 Access.Value
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.13 Access.Sensitivity
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.14 Access.Delay
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.15 Access.Status
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.16 Access.Category
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.17 Input 1.DescName
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.18 Input 1.Value
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.19 Input 1.Logic
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.20 Input 1.Delay
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.21 Input 1.Status
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.22 Input 1.Category
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.23 Input 2.DescName
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.24 Input 2.Value
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.25 Input 2.Logic
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.26 Input 2.Delay
# .1.3.6.1.4.1.2606.7.4.2.2.1.3.1.27 Input 2.Status
# ...
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.1 Temperature
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.2 31.00 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.3 -3.70 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.4 50.00 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.5 40.00 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.6 10.00 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.7 5.00 <B0>C
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.8 0.10 %
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.9 OK
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.10 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.11 Door
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.12 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.13 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.14 10 s
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.15 Closed
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.16 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.17 Input_1
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.18 1
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.19 0:Off / 1:On
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.20 0.5 s
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.21 On
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.22 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.23 Input_2
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.24 0
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.25 0:Off / 1:On
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.26 0.5 s
# .1.3.6.1.4.1.2606.7.4.2.2.1.10.1.27 Off

# .1.3.6.1.4.1.2606.7.4.1.2.1.2.1 CMCIII-PU
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.2 CMCIII-GAT
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.3 CMCIII-GAT
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.4 CMCIII-IO3
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.5 CMCIII-SEN
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.6 CMCIII-SEN
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.7 CMCIII-SEN
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.8 CMCIII-ACC
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.9 CMCIII-ACC
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.10 CMCIII-ACC
# .1.3.6.1.4.1.2606.7.4.1.2.1.2.11 CMCIII-ACC
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.1 CMCIII-PU
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.2 CAN_BUS_UNIT_I
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.3 CAN_BUS_UNIT_II
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.4 CMCIII-IO3
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.5 Access Rack_1
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.6 Access Rack_2
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.7 Access Rack_3
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.8 Left Door
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.9 Right Door
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.10 Side Exit
# .1.3.6.1.4.1.2606.7.4.1.2.1.3.11 Emergency Exit
#

# Example for info:
# [['1.1', 'Temperature.DescName', 'Temperature'],
# ['1.2', 'Temperature.Value', '31.00 \xb0C'],
# ['1.3', 'Temperature.Offset', '-3.70 \xb0C'],
# ['1.4', 'Temperature.SetPtHighAlarm', '50.00 \xb0C'],
# ['1.5', 'Temperature.SetPtHighWarning', '40.00 \xb0C'],
# ['1.6', 'Temperature.SetPtLowWarning', '10.00 \xb0C'],
# ['1.7', 'Temperature.SetPtLowAlarm', '5.00 \xb0C'],
# ['1.8', 'Temperature.Hysteresis', '0.10 %'],
# ['1.9', 'Temperature.Status', 'OK'],
# ['1.10', 'Temperature.Category', '0'],
# ['1.11', 'Access.DescName', 'Door'],
# ['1.12', 'Access.Value', '0'],
# ['1.13', 'Access.Sensitivity', '0'],
# ['1.14', 'Access.Delay', '10 s'],
# ['1.15', 'Access.Status', 'Closed'],
# ['1.25', 'Input 2.Logic', '0:Off / 1:On'],
# ['1.26', 'Input 2.Delay', '0.5 s'],
# ['1.27', 'Input 2.Status', 'Off'],
# ['1.28', 'Input 2.Category', '0'],
# ['1.29', 'Output.DescName', 'Alarm Relay'],
# ['1.30', 'Output.Relay', 'Off'],
# ['1.31', 'Output.Logic', '0:Off / 1:On'],
# ['1.32', 'Output.Status', 'Off'],
# ['1.33', 'Output.Category', '0'],
# ['1.34', 'System.V24 Port.DescName', 'V24 Unit'],
# ['1.35', 'System.V24 Port.Device', 'NONE'],
# ['1.36', 'System.V24 Port.Message', '--'],

# Convert info dictionary:

# {('1', 'Access'): {'Category': '0',
#                    'Delay': '10 s',
#                    'DescName': 'Door',
#                    'Sensitivity': '0',
#                    'Status': 'Closed',
#                    'Value': '0'},
#  ('1', 'Input 1'): {'Category': '0',
#                     'Delay': '0.5 s',
#                     'DescName': 'Input_1',
#                     'Logic': '0:Off / 1:On',
#                     'Status': 'On',
#                     'Value': '1'},
#  ('1', 'Input 2'): {'Category': '0',
#                     'Delay': '0.5 s',
#                     'DescName': 'Input_2',
#                     'Logic': '0:Off / 1:On',
#                     'Status': 'Off',
#                     'Value': '0'},


def parse_cmciii_inputs(info):
    parsed = {}
    for endoid, varname, value in info:
        unit, eid = endoid.split(".") # "1.8" -> "1"
        item_name, key = varname.rsplit(".", 1)
        item = (unit, item_name)
        parsed.setdefault(item, {})[key] = value
    return parsed

def parse_units(info):
    units = []
    num = 0
    for unit_type, descname, state in info:
        num =+ 1
        # no blanks in names since we use blanks in items
        # later to split between unit_name and item_name
        descname = re.sub(" ", "_", descname)
        if descname == '':
            descname = unit_type+"-"+str(num)
        units.append((unit_type, descname, state))
    return units

def get_unit_number(units,unit_name):
    namelist = map(list, zip(*units))[1]
    if unit_name in namelist:
        return str(namelist.index(unit_name)+1)

cmciii_snmp_info = [
                        [ ".1.3.6.1.4.1.2606.7.4.2.2.1", [
                            OID_END,
                            3, # Variable names in this subtree, e.g. Temperature.SetPtHighAlarm
                            10, # Actual values in this subtree
                            ]
                        ],
                        [ ".1.3.6.1.4.1.2606.7.4.1.2.1", [
                            2, # Type of unit
                            3, # Descriptive name of unit
                            6, # State
                            ]
                        ]
                    ]

cmciii_scan =  lambda oid: ".1.3.6.1.4.1.2606.7" in oid(".1.3.6.1.2.1.1.2.0")

#   .--psm current---------------------------------------------------------.
#   |                                                           _          |
#   |      _ __  ___ _ __ ___     ___ _   _ _ __ _ __ ___ _ __ | |_        |
#   |     | '_ \/ __| '_ ` _ \   / __| | | | '__| '__/ _ \ '_ \| __|       |
#   |     | |_) \__ \ | | | | | | (__| |_| | |  | | |  __/ | | | |_        |
#   |     | .__/|___/_| |_| |_|  \___|\__,_|_|  |_|  \___|_| |_|\__|       |
#   |     |_|                                                              |
#   +----------------------------------------------------------------------+


def inventory_cmciii_psm_current(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if item_name.startswith("PSM_") and item_name.endswith(".Unit"):
            item_name = re.sub(r'Unit$','Current',item_name)
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )
    return inventory

def check_cmciii_psm_current(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    itemtemp = re.sub(r'Current$','Unit',item_name)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, itemtemp))
    if not entry:
        return 3, "No such PSM found"

    descr = entry.get('DescName')
    typ = entry.get('Unit Type')
    status = entry.get('Status')
    current = entry.get('Value')
    serial = entry.get('Serial Number')
    position = entry.get('Mounting Position')
    max_current = entry.get('SetPtHighAlarm')
    min_current = entry.get('SetPtLowAlarm')
    if status == "OK":
        state = 0
        statind = ""
    else:
        state = 2
        statind = status+" "
    strom, einheit = current.split(" ")
    max = max_current.split(" ")[0]
    min = min_current.split(" ")[0]

    perfdata = [ ( "current", strom+einheit, 0, 0, min, max) ]

    infotext =  "%s%s: Current %s (%s/%s), Type %s, Serial %s, Position %s" \
        % ( statind, descr, current, min_current, max_current, typ, serial, position)

    return (state, infotext, perfdata)

check_info['cmciii.psm_current'] = {
    "check_function"      : check_cmciii_psm_current,
    "inventory_function"  : inventory_cmciii_psm_current,
    "has_perfdata"        : True,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--psm plugs-----------------------------------------------------------.
#   |                                        _                             |
#   |            _ __  ___ _ __ ___    _ __ | |_   _  __ _ ___             |
#   |           | '_ \/ __| '_ ` _ \  | '_ \| | | | |/ _` / __|            |
#   |           | |_) \__ \ | | | | | | |_) | | |_| | (_| \__ \            |
#   |           | .__/|___/_| |_| |_| | .__/|_|\__,_|\__, |___/            |
#   |           |_|                   |_|            |___/                 |
#   +----------------------------------------------------------------------+

def inventory_cmciii_psm_plugs(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if item_name.startswith("PSM_") and re.search(r'\.Plug\d$',item_name):
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )
    return inventory

def check_cmciii_psm_plugs(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, item_name))
    if not entry:
        return 3, "No such PSM_Plug found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    if status == "OK":
        state = 0
    else:
        state = 2

    infotext =  "%s: %s" % ( descr, status)

    return (state, infotext)

check_info['cmciii.psm_plugs'] = {
    "check_function"      : check_cmciii_psm_plugs,
    "inventory_function"  : inventory_cmciii_psm_plugs,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--IO------------------------------------------------------------------.
#   |                              ___ ___                                 |
#   |                             |_ _/ _ \                                |
#   |                              | | | | |                               |
#   |                              | | |_| |                               |
#   |                             |___\___/                                |
#   |                                                                      |
#   +----------------------------------------------------------------------+

def inventory_cmciii_io(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if re.match(r'(Input|Output)',item_name) and re.match(r'CMCIII-IO', unit_type ):
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )

    return inventory

def check_cmciii_io(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, item_name))
    if not entry:
        return 3, "No such IO channel found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    value = entry.get('Value')
    logic = entry.get('Logic')
    relay = entry.get('Relay')
    delay = entry.get('Delay')
    grouping = entry.get('Grouping')

    if relay: # output relay
        if status == "OK":
            state = 0
            sym = ""
        else:
            state = 2
            sym = "(!!)"
        infotext =  "%s: %s%s, Relay %s, Grouping %s, Logic %s" % \
                         ( descr, status, sym, relay, grouping, logic)
    else: # input relay
        if status == "OK":
            state = 0
            sym = ""
        elif status == "Off":
            state = 0
            sym = ""
        elif status == "On":
            state = 1
            sym = "(!)"
        else:
            state = 2
            sym = "(!!)"
        infotext =  "%s: %s, Status %s%s, Logic %s, Delay %s" % \
                        ( descr, status, value, sym, logic, delay)

    return (state, infotext)

check_info['cmciii.io'] = {
    "check_function"      : check_cmciii_io,
    "inventory_function"  : inventory_cmciii_io,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--access--------------------------------------------------------------.
#   |                                                                      |
#   |                      __ _  ___ ___ ___  ___ ___                      |
#   |                     / _` |/ __/ __/ _ \/ __/ __|                     |
#   |                    | (_| | (_| (_|  __/\__ \__ \                     |
#   |                     \__,_|\___\___\___||___/___/                     |
#   |                                                                      |
#   +----------------------------------------------------------------------+

def inventory_cmciii_access(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if item_name == "Access":
            inventory.append( (unit_name+" Access", None) )
    return inventory

def check_cmciii_access(item, params, info):
    parsed = parse_cmciii_inputs(info[0])
    unit_name = re.sub(r' Access','',item)
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, "Access"))
    if not entry:
        return 3, "No such Access data found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    value = entry.get('Value')
    delay = entry.get('Delay')
    sensitivity = entry.get('Sensitivity')

    if status == "Closed":
        state = 0
        sym = ""
    else:
        state = 2
        sym = "(!!)"

    infotext =  "%s %s%s, Value %s, Delay %s, Sens. %s " % \
                         ( descr, status, sym, value, delay, sensitivity)

    return (state, infotext)

check_info['cmciii.access'] = {
    "check_function"      : check_cmciii_access,
    "inventory_function"  : inventory_cmciii_access,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--temp----------------------------------------------------------------.
#   |                       _                                              |
#   |                      | |_ ___ _ __ ___  _ __                         |
#   |                      | __/ _ \ '_ ` _ \| '_ \                        |
#   |                      | ||  __/ | | | | | |_) |                       |
#   |                       \__\___|_| |_| |_| .__/                        |
#   |                                        |_|                           |
#   +----------------------------------------------------------------------+

def inventory_cmciii_temp(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if "Temperature" in item_name and "Air." not in item_name:
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )
    return inventory

def parse_temp(val):
    if re.search(" B0 ", val):
        # is encoded string
        val = re.sub(" ","", val).decode("hex")
    val, t_unit = val.split(" ", 1)
    # omit special characters in unit, because they
    # cause problems in rrdtools
    t_unit = re.sub("[^\w]","", t_unit)
    return val, t_unit

def check_cmciii_temp(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, item_name))
    if not entry:
        return 3, "No such temperature data found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    offset = entry.get('Offset')

    value, t_unit = parse_temp(entry.get('Value'))
    highalarm = parse_temp(entry.get('SetPtHighAlarm'))[0]
    highwarning = parse_temp(entry.get('SetPtHighWarning'))[0]
    lowalarm = parse_temp(entry.get('SetPtLowAlarm'))[0]
    lowwarning = parse_temp(entry.get('SetPtLowWarning'))[0]

    if status == "OK":
        state = 0
        status = ""
    else:
        state = 2

    perfdata = [ ("temp", value+t_unit, 0, 0 ) ]

    infotext =  "%s %s %s - Limits low %s/%s high %s/%s, Offset %s" % \
                         ( descr, value, status, lowalarm, lowwarning, highwarning, highalarm, offset )

    return (state, infotext, perfdata)

check_info['cmciii.temp'] = {
    "check_function"      : check_cmciii_temp,
    "inventory_function"  : inventory_cmciii_temp,
    "service_description" : "%s",
    "has_perfdata"        : True,
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--can current---------------------------------------------------------.
#   |                                                         _            |
#   |         ___ __ _ _ __     ___ _   _ _ __ _ __ ___ _ __ | |_          |
#   |        / __/ _` | '_ \   / __| | | | '__| '__/ _ \ '_ \| __|         |
#   |       | (_| (_| | | | | | (__| |_| | |  | | |  __/ | | | |_          |
#   |        \___\__,_|_| |_|  \___|\__,_|_|  |_|  \___|_| |_|\__|         |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'
def inventory_cmciii_can_current(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if item_name.startswith('System.CAN') and item_name.endswith('Current'):
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )
    return inventory

def check_cmciii_can_current(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, item_name))
    if not entry:
        return 3, "No such temperature data found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    value = re.sub(r' ','',entry.get('Value'))
    hysteresis = entry.get('Hysteresis')
    highalarm = entry.get('SetPtHighAlarm').split(" ")[0]
    highwarning = entry.get('SetPtHighWarning').split(" ")[0]

    if status == "OK":
        state = 0
        status = ""
    else:
        state = 2

    perfdata = [ ("current", value, highwarning, highalarm ) ]

    infotext =  "%s %s %s - Limits %s/%s, Hysteresis %s" % \
                         ( descr, value, status, highwarning, highalarm, hysteresis)

    return (state, infotext, perfdata)

check_info['cmciii.can_current'] = {
    "check_function"      : check_cmciii_can_current,
    "inventory_function"  : inventory_cmciii_can_current,
    "service_description" : "%s",
    "has_perfdata"        : True,
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--sensor--------------------------------------------------------------.
#   |                                                                      |
#   |                    ___  ___ _ __  ___  ___  _ __                     |
#   |                   / __|/ _ \ '_ \/ __|/ _ \| '__|                    |
#   |                   \__ \  __/ | | \__ \ (_) | |                       |
#   |                   |___/\___|_| |_|___/\___/|_|                       |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_cmciii_sensor(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if re.match(r'CMCIII-SEN', unit_type ):
            inventory.append((unit_name+" Sensor", None))
    return inventory

def check_cmciii_sensor(item, params, info):
    parsed = parse_cmciii_inputs(info[0])
    unit_name = re.sub(r' Sensor','',item)
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, "Input"))
    if not entry:
        return 3, "No such sensor unit found"

    status = entry.get('Status')

    if status == "Closed":
        state = 0
    else:
        state = 2

    return (state, status)

check_info['cmciii.sensor'] = {
    "check_function"      : check_cmciii_sensor,
    "inventory_function"  : inventory_cmciii_sensor,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--state---------------------------------------------------------------.
#   |                            _        _                                |
#   |                        ___| |_ __ _| |_ ___                          |
#   |                       / __| __/ _` | __/ _ \                         |
#   |                       \__ \ || (_| | ||  __/                         |
#   |                       |___/\__\__,_|\__\___|                         |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_cmciii_state(info):
    inventory = []
    units = parse_units(info[1])
    for unit_type, unit_name, state in units:
        inventory.append((unit_name+" State", None))
    return inventory

def check_cmciii_state(item, params, info):
    unit_name = re.sub(r' State','',item)
    units = parse_units(info[1])
    unit_number = get_unit_number(units, unit_name)
    entry = units[int(unit_number)-1]
    stati = {
       1 : ( "not available", 3 ),
       2 : ( "OK", 0 ),
       3 : ( "detect", 1),
       4 : ( "lost", 2),
       5 : ( "changed", 0),
       6 : ( "error", 2),
    }
    status = stati.get(saveint(entry[2]), ( "unknown", 3 ) )

    infotext = "Device returns internal state \"%s\"" % status[0]
    return (int(status[1]), infotext )


check_info['cmciii.state'] = {
    "check_function"      : check_cmciii_state,
    "inventory_function"  : inventory_cmciii_state,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
}

#.
#   .--humidity------------------------------------------------------------.
#   |              _                     _     _ _ _                       |
#   |             | |__  _   _ _ __ ___ (_) __| (_) |_ _   _               |
#   |             | '_ \| | | | '_ ` _ \| |/ _` | | __| | | |              |
#   |             | | | | |_| | | | | | | | (_| | | |_| |_| |              |
#   |             |_| |_|\__,_|_| |_| |_|_|\__,_|_|\__|\__, |              |
#   |                                                  |___/               |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

def inventory_cmciii_humidity(info):
    inventory = []
    parsed = parse_cmciii_inputs(info[0])
    units = parse_units(info[1])
    for (unit, item_name), entry in parsed.iteritems():
        unit_type, unit_name = units[int(unit)-1][0:2]
        if item_name == 'Humidity' and unit_type == 'CMCIII-HUM':
            inventory.append(  ("%s %s" % (unit_name, item_name), None)  )

    return inventory

def parse_hum(val):
    return val.split(" ", 1)

def check_cmciii_humidity(item, params, info):
    unit_name, item_name = item.split(" ", 1)
    parsed = parse_cmciii_inputs(info[0])
    unit = get_unit_number(parse_units(info[1]), unit_name)
    entry = parsed.get((unit, item_name))
    if not entry:
        return 3, "No such temperature data found"

    descr = entry.get('DescName')
    status = entry.get('Status')
    offset = entry.get('Offset')

    value = parse_hum(entry.get('Value'))[0]
    highalarm = parse_hum(entry.get('SetPtHighAlarm'))[0]
    highwarning = parse_hum(entry.get('SetPtHighWarning'))[0]
    lowalarm = parse_hum(entry.get('SetPtLowAlarm'))[0]
    lowwarning = parse_hum(entry.get('SetPtLowWarning'))[0]

    if status == "OK":
        state = 0
        status = ""
    else:
        state = 2

    perfdata = [ ("hum", value, 0, 0 ) ]

    infotext =  "%s %s%% %s - Limits low %s/%s high %s/%s, Offset %s" % \
                         ( descr, value, status, lowalarm, lowwarning, highwarning, highalarm, offset )

    return (state, infotext, perfdata)

check_info['cmciii.humidity'] = {
    "check_function"      : check_cmciii_humidity,
    "inventory_function"  : inventory_cmciii_humidity,
    "service_description" : "%s",
    "snmp_scan_function"  : cmciii_scan,
    "snmp_info"           : cmciii_snmp_info,
    "has_perfdata"        : True,
}
#.
