#!/usr/bin/python3
#
# -*- coding: utf-8 -*-
#
# check_rtl433: check rtl433 sensor data
#
# Copyright 2022, Philippe Kueck <projects at unixadm dot org>
#
# This software may be freely redistributed under the terms of the GNU
# general public license version 2.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import sys
from time import time
from datetime import datetime
import json
from argparse import ArgumentParser

def nagexit(exitc, statusline, perfdata=[]):
    print("{0}: {1}|{2}".format(
        {0:'OK',1:'WARNING',2:'CRITICAL',3:'UNKNOWN'}[exitc],
        "\n".join(statusline),
        " ".join(perfdata)
    ))
    sys.exit(exitc)

def check(val, warn, crit):
    if warn < crit <= val or val <= crit < warn: return 2
    if warn <= val <= crit or crit <= val <= warn: return 1
    return 0

def check_val(val, ref):
    if ref is None: return True
    return val == ref

def check_rtl433():
    data = None
    fh = open(options.file)
    for sensor_data in fh:
        tmp = json.loads(sensor_data)
        if 'model' not in tmp or tmp['model'] != options.model:
            continue
        if 'channel' in tmp and not check_val(tmp['channel'], options.channel):
            continue
        if 'id' in tmp and not check_val(tmp['id'], options.id):
            continue
        if 'rid' in tmp and not check_val(tmp['rid'], options.rid):
            continue
        data = tmp

    if data is None:
        nagexit(3, ["Sensor not found"])

    if options.maxage is not None and 'time' in data:
        ts = datetime.strptime(data['time'], "%Y-%m-%d %H:%M:%S").timestamp()
        if time() > ts + options.maxage:
            nagexit(3, [f"stale data from {data['time']}"])

    if options.flip_humid and 'humidity' in data:
        if data['humidity'] == 100: data['humidity'] = 0
        elif data['humidity'] == 0: data['humidity'] = 100

    if options.zero99_humid and 'humidity' in data:
        if data['humidity'] == 99: data['humidity'] = 0

    if 'temperature_C' not in data and 'temperature_F' in data:
        data['temperature_C'] = (data['temperature_F']-32)*5/9

    rc = 0
    statusline = [data['model']]
    perfdata = []
    statusline_t = []

    if 'battery_ok' in data and data['battery_ok'] == 0:
        statusline += ["battery not reliable"]
        rc = 1

    if 'temperature_C' in data:
        statusline_t += ["{:.2f}C".format(data['temperature_C'])]
        perfdata += ["'temperature'={:.3f};{};{};;".format(
            data['temperature_C'],
            (options.temphi_warn,"")[options.temphi_warn is None],
            (options.temphi_crit,"")[options.temphi_crit is None]
        )]
        if options.temphi_crit is not None and options.temphi_warn is not None:
            t_rc = check(data['temperature_C'], options.temphi_warn, options.temphi_crit)
            if t_rc > rc:
                rc = t_rc
                statusline += ["temperature too high"]
        if options.templo_crit is not None and options.templo_warn is not None:
            t_rc = check(data['temperature_C'], options.templo_warn, options.templo_crit)
            if t_rc > rc:
                rc = t_rc
                statusline += ["temperature too low"]

    if 'humidity' in data:
        statusline_t += [f"{data['humidity']}%"]
        perfdata += ["'humidity'={:.1f};{};{};;".format(
            data['humidity'],
            (options.humid_warn,"")[options.humid_warn is None],
            (options.humid_crit,"")[options.humid_crit is None]
        )]
        if options.humid_crit is not None and options.humid_warn is not None:
            t_rc = check(data['humidity'], options.humid_warn, options.humid_crit)
            if t_rc > rc:
                rc = t_rc
                statusline += ["humidity too high"]

    statusline += ["@".join(statusline_t), f"(data from {data['time']})"]

    nagexit(rc, statusline, perfdata)


if __name__ == "__main__":
    parser = ArgumentParser(
        description = "%(prog)s checks for sensor data"
    )

    gen_opts = parser.add_argument_group("Generic options")
    gen_opts.add_argument("-f", "--file", dest="file",
        action="store", help="path to json file",
        required=True
    )
    gen_opts.add_argument("-a", "--maxage", type=int, dest="maxage",
        action="store", help="max age of sensor data in seconds"
    )
    # does nothing atm
    gen_opts.add_argument("-s", "--since", type=int, dest="since",
        action="store", help="ignore data older than this date"
    )

    sensor_opts = parser.add_argument_group("Sensor options")
    sensor_opts.add_argument("-m", "--model", dest="model",
        action="store", help="model/brand name",
        required=True
    )
    sensor_opts.add_argument("-i", "--id", type=int, dest="id",
        action="store", help="sensor id"
    )
    sensor_opts.add_argument("-r", "--rid", type=int, dest="rid",
        action="store", help="sensor rid"
    )
    sensor_opts.add_argument("-x", "--channel", type=int, dest="channel",
        action="store", help="channel number"
    )

    temp_opts = parser.add_argument_group("Temperature options")
    temp_opts.add_argument("-W", "--temp-high-warn", type=float, dest="temphi_warn",
        action="store", help="temperature high warn"
    )
    temp_opts.add_argument("-C", "--temp-high-crit", type=float, dest="temphi_crit",
        action="store", help="temperature high critical"
    )
    temp_opts.add_argument("-t", "--temp-low-warn", type=float, dest="templo_warn",
        action="store", help="temperature low warn"
    )
    temp_opts.add_argument("-T", "--temp-low-crit", type=float, dest="templo_crit",
        action="store", help="temperature low critical"
    )

    humid_opts = parser.add_argument_group("Humidity options")
    humid_opts.add_argument("-w", "--humid-warn", type=float, dest="humid_warn",
        action="store", help="humidity high warn"
    )
    humid_opts.add_argument("-c", "--humid-crit", type=float, dest="humid_crit",
        action="store", help="humidity high critical"
    )
    humid_opts.add_argument("-z", "--switch-zero", dest="flip_humid",
        action="store_true", help="switch humidity values of 0%% and 100%%"
    )
    humid_opts.add_argument("--zero99", dest="zero99_humid",
        action="store_true", help="assume 99%% as 0%%"
    )

    options = parser.parse_args()

    if options.since is not None:
        options.since = datetime.fromtimestamp(options.since)

    check_rtl433()
