#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2016             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-
# tails. 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.


# <<<tinkerforge:sep(44)>>>
# temperature,6QHSgJ.a.tiq,2181
# humidity,6QHSgJ.c.ugg,250
# ambient,6JLy11.c.uKA,124


# based on customers investigation
factory_settings['tinkerforge_humidity_default_levels'] = {
    'upper': (50, 55),
    'lower': (40, 35)
}


def parse_tinkerforge(info):
    # biggest trouble here is generating sensible item names as tho ones
    # provided to us are simply random-generated

    def gen_pos(parent, pos):
        if parent == "0":
            res = ""
        else:
            res = "%s%s" % (gen_pos(*master_index[parent]), pos)
        return res

    # first, go through all readings and group them by brick(let) type.
    # On this opportunity, also create an index of master bricks which we need
    # to query the stack topology
    master_index = {}
    temp = {}
    for line in info:
        brick_type, path = line[:2]
        try:
            brick_type, subtype = brick_type.split(".")
        except ValueError:
            subtype = None
        parent, pos, uid = path.split(".")

        if brick_type == "master":
            master_index[uid] = (parent, pos)

        values = line[2:]
        temp.setdefault(brick_type, []).append((parent, pos, subtype, values))

    # now go through all the bricks again and sort them within each brick_type-group by their
    # position in the topology. items higher up in the topology come first, and among
    # "siblings" they are sorted by the port on this host.
    res = {}
    for brick_type, bricks in temp.iteritems():
        counter = 1
        for brick in sorted(bricks,
                            key=lambda b: gen_pos(b[0], b[1]).rjust(len(master_index) + 1, ' ')):
            name = str(counter)
            if brick[2]:
                name = "%s %s" % (brick[2], counter)
            res.setdefault(brick_type, {})[name] = brick[3]
            counter += 1

    return res


def inventory_tinkerforge(brick_type, parsed):
    for path in parsed.get(brick_type, {}).keys():
        yield path, {}


def check_tinkerforge_master(item, params, parsed):
    if 'master' in parsed and item in parsed['master']:
        try:
            voltage, current, chip_temp = parsed['master'][item]
            yield 0, "%.1f mV" % float(voltage)
            yield 0, "%.1f mA" % float(current)
            yield check_temperature(float(chip_temp) / 10.0, params, "tinkerforge_%s" % item)
        except:
            yield 2, parsed['master'][item][0], []


def check_tinkerforge_temperature(item, params, parsed):
    if 'temperature' in parsed and item in parsed['temperature']:
        reading = float(parsed['temperature'][item][0]) / 100.0
        return check_temperature(reading, params, "tinkerforge_%s" % item)


def check_tinkerforge_ambient(item, params, parsed):
    if 'ambient' in parsed and item in parsed['ambient']:
        reading = float(parsed['ambient'][item][0]) / 100.0
        if not params:
            params = None
        status, infotext, perfdata = check_levels(reading, 'brightness', params, unit="lx")
        infotext = "%.1f lx" % (reading) + infotext
        perfdata.append(('brightness', reading))
        return status, infotext, perfdata


def check_tinkerforge_humidity(item, params, parsed):
    def expect_order(*args):
        arglist = filter(lambda x: x != None, args)
        sorted_by_val = sorted(enumerate(arglist), key=lambda x: x[1])
        return max([abs(x[0] - x[1][0]) for x in enumerate(sorted_by_val)])

    if 'humidity' in parsed and item in parsed['humidity']:
        reading = float(parsed['humidity'][item][0]) / 10.0
        status = expect_order(params['lower'][1], params['lower'][0],
                              reading,
                              params['upper'][0], params['upper'][1])
        if status != 0:
            level_msg = " (warn/crit below %.1f/%.1f or above %.1f/%.1f)" % \
                (params['lower'][0], params['lower'][1],
                 params['upper'][0], params['upper'][1])
        else:
            level_msg = ""
        return (status, "%.1f %%RH%s" % (reading, level_msg),
                [('humidity', reading, params['upper'][0], params['upper'][1])])


def check_tinkerforge_motion(item, params, parsed):
    def test_in_period(time, periods):
        time_mins = time[0] * 60 + time[1]
        for per in periods:
            per_mins_low  = per[0][0] * 60 + per[0][1]
            per_mins_high = per[1][0] * 60 + per[1][1]
            if time_mins >= per_mins_low and time_mins < per_mins_high:
                return True
        return False

    weekdays = ["monday", "tuesday", "wednesday", "thursday",
                "friday", "saturday", "sunday"]
    if 'motion' in parsed and item in parsed['motion']:
        today = time.localtime()
        if 'time_periods' in params:
            periods = params['time_periods'][weekdays[today.tm_wday]]
        else:
            periods = [((0, 0), (24, 0))]
        reading = int(parsed['motion'][item][0])
        if reading == 1:
            status = test_in_period((today.tm_hour, today.tm_min), periods) and 1 or 0
            return status, "Motion detected", [('motion', reading)]
        else:
            return 0, "No motion detected", [('motion', reading)]


check_info['tinkerforge'] = {
    'inventory_function'  : lambda parsed: inventory_tinkerforge('master', parsed),
    'check_function'      : check_tinkerforge_master,
    'parse_function'      : parse_tinkerforge,
    'has_perfdata'        : False,
    'service_description' : "Master %s"
}


check_info['tinkerforge.temperature'] = {
    'inventory_function'      : lambda parsed: inventory_tinkerforge('temperature', parsed),
    'check_function'          : check_tinkerforge_temperature,
    'has_perfdata'            : True,
    'service_description'     : "Temperature %s",
    'group'                   : 'temperature',
    'includes'                : ['temperature.include']
}


check_info['tinkerforge.ambient'] = {
    'inventory_function'      : lambda parsed: inventory_tinkerforge('ambient', parsed),
    'check_function'          : check_tinkerforge_ambient,
    'has_perfdata'            : True,
    'group'                   : 'brightness',
    'service_description'     : "Ambient Light %s"
}


check_info['tinkerforge.humidity'] = {
    'inventory_function'      : lambda parsed: inventory_tinkerforge('humidity', parsed),
    'check_function'          : check_tinkerforge_humidity,
    'has_perfdata'            : True,
    'group'                   : 'humidity',
    'service_description'     : "Humidity %s",
    'default_levels_variable' : 'tinkerforge_humidity_default_levels'
}


check_info['tinkerforge.motion'] = {
    'inventory_function'      : lambda parsed: inventory_tinkerforge('motion', parsed),
    'check_function'          : check_tinkerforge_motion,
    'has_perfdata'            : True,
    'group'                   : 'motion',
    'service_description'     : "Motion Detector %s",
}

