#!/usr/bin/env python3

"""RRDtool monitoring relay for rtl_433."""

# Start rtl_433 (rtl_433 -C si -F syslog:127.0.0.1:1433), then this script

from __future__ import print_function
from __future__ import with_statement

import sys
import socket
import time
import json
import rrdtool

# Option: PEP 3143 - Standard daemon process library
# (pip install python-daemon)
try:
    import daemon
except ImportError:
    daemon = None

UDP_IP = "127.0.0.1"
UDP_PORT = 1433
RRD_PATH = "" # e.g. "/var/lib/rtl_433/rrd/"
GRAPH_PATH = ""  # e.g. "/var/www/rrd/html/"
GRAPH_INTERVAL = 30 * 60  # in seconds, i.e. 30 minutes


def create_rrd(rrdfile):
    # print("Creating", rrdfile)
    return rrdtool.create(rrdfile,
                          "--step", "1800", "--start", '0',
                          "DS:temperature:GAUGE:2000:U:U",
                          "DS:humidity:GAUGE:2000:U:U",
                          "RRA:AVERAGE:0.5:1:600",
                          "RRA:AVERAGE:0.5:6:700",
                          "RRA:AVERAGE:0.5:24:775",
                          "RRA:AVERAGE:0.5:288:797",
                          "RRA:MAX:0.5:1:600",
                          "RRA:MAX:0.5:6:700",
                          "RRA:MAX:0.5:24:775",
                          "RRA:MAX:0.5:444:797")


def update_rrd(rrdfile, temperature, humidity):
    # print("Updating", rrdfile, temperature, humidity)
    return rrdtool.update(rrdfile, "N:%s:%s" % (temperature, humidity))


def graph_rrd(rrdfile, label, path=""):
    for sched in ['daily' , 'weekly', 'monthly', 'hourly']:
        period = sched[0] # 'w', 'd', 'm', 'h'
        # print("Graphing", sched, label, rrdfile)
        ret = rrdtool.graph("%smetrics-%s.%s.png" % (path, sched, label),
                            "--start", "-1%s" % (period),
                            "--title", label,
                            "--vertical-label=C",
                            "--right-axis-label=%",
                            '--watermark=rtl_433',
                            "-w 800", "-h 200",
                            "DEF:t=%s:temperature:AVERAGE" % (rrdfile),
                            "DEF:h=%s:humidity:AVERAGE" % (rrdfile),
                            "LINE1:t#00FF00:temperature\r",
                            "LINE2:h#0000FF:humidity\r",
                            "GPRINT:t:AVERAGE:Temp avg %6.1lf C",
                            "GPRINT:t:MAX:Temp max %6.1lf C\r",
                            "GPRINT:h:AVERAGE:Hum avg %6.0lf %%",
                            "GPRINT:h:MAX:Hum max %6.0lf %%\r")
    return ret


def sanitize(text):
    return text.replace(" ", "_").replace("/", "_").replace(".", "_").replace("&", "")


def parse_syslog(line):
    """Try to extract the payload from a syslog line."""
    line = line.decode("ascii")  # also UTF-8 if BOM
    if line.startswith("<"):
        # fields should be "<PRI>VER", timestamp, hostname, command, pid, mid, sdata, payload
        fields = line.split(None, 7)
        line = fields[-1]
    return line


def rtl_433_probe(sock):
    next_graph = {}

    while True:
        line, _addr = sock.recvfrom(1024)

        try:
            line = parse_syslog(line)
            data = json.loads(line)
            now = int(time.time())

            label = sanitize(data["model"])
            if "channel" in data:
                label += ".CH" + str(data["channel"])
            elif "id" in data:
                label += ".ID" + str(data["id"])
            rrd_file = RRD_PATH + label + ".rrd"

            if "type" in data and data["type"] == "TPMS":
                continue

            if "temperature_C" not in data:
                continue
            temperature = data["temperature_C"]

            humidity = "U"
            if "humidity" in data:
                humidity = data["humidity"]

            try:
                rrdtool.info(rrd_file)
            except rrdtool.OperationalError:
                create_rrd(rrd_file)

            update_rrd(rrd_file, temperature, humidity)

            if label not in next_graph or next_graph[label] < now:
                graph_rrd(rrd_file, label, GRAPH_PATH)
                next_graph[label] = now + GRAPH_INTERVAL

        except KeyError:
            pass

        except ValueError:
            pass


def run(run_as_daemon=False):
    # check optional python imports
    if run_as_daemon and not daemon:
        print("Error: pip install python-daemon")
        return

    # setup input and output
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.bind((UDP_IP, UDP_PORT))

    # run, as daemon if requested
    if run_as_daemon:
        with daemon.DaemonContext(files_preserve=[sock]):
            rtl_433_probe(sock)
    else:
        rtl_433_probe(sock)


if __name__ == "__main__":
    # simple argument parsing
    run_as_daemon = len(sys.argv) > 1 and sys.argv[1] == "-d"
    try:
        run(run_as_daemon)
    except KeyboardInterrupt:
        pass
