diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..16d1793
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,8 @@
+[run]
+ omit =
+ test.py
+ */site-packages/
+ tests/*
+ /home/travis/virtualenv/*
+ /usr/*
+ /home/buildbot/.local/lib/*
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cfccfde
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# prom433
+
+![Docker Image Version (latest semver)](https://img.shields.io/docker/v/andrewjw/prom433)
+[![Coverage Status](https://coveralls.io/repos/github/andrewjw/prom433/badge.svg?branch=master)](https://coveralls.io/github/andrewjw/prom433?branch=master)
+
+Exposes Prometheus metrics based on data received by rtl_433
diff --git a/args.txt b/args.txt
new file mode 100644
index 0000000..d80d904
--- /dev/null
+++ b/args.txt
@@ -0,0 +1 @@
+rtl_433 -f 434000000 -s 300000 -F json
diff --git a/bin/prom433 b/bin/prom433
new file mode 100644
index 0000000..aaeafa9
--- /dev/null
+++ b/bin/prom433
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3.8
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import threading
+import subprocess
+import sys
+
+from prom433 import get_arguments, connect, serve, update_stats
+
+def run_subprocess(args):
+ rtl = subprocess.run(f"rtl_433 {args.rtl} -F json", stdout=subprocess.PIPE)
+
+ for line in rtl.stdout:
+ update_stats(line)
+
+def main():
+ args = get_arguments(sys.argv[1:])
+
+ threading.Thread(target=run_subprocess,
+ args=(args, ),
+ daemon=True).start()
+
+ serve(args)
+
+if __name__ == "__main__":
+ main()
diff --git a/buildbot.sh b/buildbot.sh
new file mode 100644
index 0000000..a135648
--- /dev/null
+++ b/buildbot.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+set -e
+
+pip3 install -r requirements.txt
+
+./code_style.sh
+./run_tests.sh
+
+BRANCH=$(git rev-parse --abbrev-ref HEAD)
+echo "Building branch $BRANCH"
+if [[ "$BRANCH" == "master" ]]; then
+ COVERALLS_REPO_TOKEN=$GLOWPROM_COVERALLS_REPO_TOKEN coveralls
+ semantic-release publish
+fi
+if [[ ${BRANCH:0:7} == "heads/v" ]]; then
+ ./docker_push.sh
+fi
diff --git a/code_style.sh b/code_style.sh
new file mode 100644
index 0000000..d1a91d9
--- /dev/null
+++ b/code_style.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -e
+
+pycodestyle bin/ prom433/ tests/
diff --git a/docker_push.sh b/docker_push.sh
new file mode 100644
index 0000000..3cc2643
--- /dev/null
+++ b/docker_push.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -e
+
+# £TRAVIS_TAG begins with a v
+TAG=`echo $TRAVIS_TAG | sed 's/^.//'`
+
+docker login --username andrewjw --password $DOCKER_TOKEN
+
+docker build --build-arg VERSION=$TAG -t andrewjw/prom433:$TAG .
+
+docker push andrewjw/prom433:$TAG
diff --git a/prom433/__init__.py b/prom433/__init__.py
new file mode 100644
index 0000000..353652e
--- /dev/null
+++ b/prom433/__init__.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3.8
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from .arguments import get_arguments
+from .exceptions import InvalidArguments
+from .prometheus import prometheus, get_metrics
+from .server import serve, update_stats
+
+__version__ = "0.0.1"
diff --git a/prom433/arguments.py b/prom433/arguments.py
new file mode 100644
index 0000000..1077633
--- /dev/null
+++ b/prom433/arguments.py
@@ -0,0 +1,39 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import argparse
+import os
+
+parser = argparse.ArgumentParser(
+ description='Listens to meter reports from Glow (glowmarkt.com) MQTT and'
+ + ' exposes them as prometheus metrics')
+parser.add_argument('--rtl', type=str, nargs='?',
+ help='Arguments to pass to rtl_433')
+parser.add_argument('--bind', type=str, nargs='?', default="0.0.0.0:9100",
+ help='the ip address and port to bind to')
+
+
+def get_arguments(args):
+ args = parser.parse_args(args)
+ if "RTL_ARGS" in os.environ:
+ args.rtl = os.environ["RTL_ARGS"]
+
+ if ":" not in args.bind:
+ args.bind = (args.bind, 9100)
+ else:
+ args.bind = (args.bind.split(":")[0], int(args.bind.split(":")[1]))
+
+ return args
diff --git a/prom433/child_process.py b/prom433/child_process.py
new file mode 100644
index 0000000..0b9ae1d
--- /dev/null
+++ b/prom433/child_process.py
@@ -0,0 +1,24 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import subprocess
+
+def rtl433(args, callback, _popen=subprocess.Popen):
+ process = _popen(["rtl_433", "-f", "json"] + args, stdout=subprocess.PIPE)
+ for line in process.stdout.readlines():
+ if not line.startswith("{"):
+ continue
+ callback(line)
diff --git a/prom433/exceptions.py b/prom433/exceptions.py
new file mode 100644
index 0000000..9db21a6
--- /dev/null
+++ b/prom433/exceptions.py
@@ -0,0 +1,18 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+class InvalidArguments(Exception):
+ pass
diff --git a/prom433/prometheus.py b/prom433/prometheus.py
new file mode 100644
index 0000000..f193467
--- /dev/null
+++ b/prom433/prometheus.py
@@ -0,0 +1,93 @@
+# rtl_433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import json
+import logging
+
+METRICS = {}
+
+WEATHER_METRIC = "%s{id=\"%i\"} %f"
+
+WEATHER_TEMP_HELP = "# HELP weather_temperature The temperature in degrees celcius."
+WEATHER_TEMP_TYPE = "# TYPE weather_temperature gauge"
+
+WEATHER_HUM_HELP = "# HELP weather_humidity The humidity in %."
+WEATHER_HUM_TYPE = "# TYPE weather_humidity gauge"
+
+WEATHER_WIND_AVG_HELP = "# HELP weather_wind_avg The average windspeed in km/h."
+WEATHER_WIND_AVG_TYPE = "# TYPE weather_wind_avg gauge"
+WEATHER_WIND_MAX_HELP = "# HELP weather_wind_max The maximum windspeed in km/h."
+WEATHER_WIND_MAX_TYPE = "# TYPE weather_wind_max gauge"
+WEATHER_WIND_DIR_HELP = "# HELP weather_wind_dir The wind direction in degrees."
+WEATHER_WIND_DIR_TYPE = "# TYPE weather_wind_dir gauge"
+
+WEATHER_RAIN_HELP = "# HELP weather_rain The total rainfall in mm."
+WEATHER_RAIN_TYPE = "# TYPE weather_rain counter"
+
+WEATHER_BATTERY_HELP = "# HELP weather_rain The battery status."
+WEATHER_BATTERY_TYPE = "# TYPE weather_battery gauge"
+
+NEXUS_METRIC = "%s{id=\"%i\", channel=\"%i\"} %f"
+
+NEXUS_TEMP_HELP = "# HELP nexus_temperature The temperature in degrees celcius."
+NEXUS_TEMP_TYPE = "# TYPE nexus_temperature gauge"
+NEXUS_HUM_HELP = "# HELP nexus_humidity The humidity in %."
+NEXUS_HUM_TYPE = "# TYPE nexus_humidity gauge"
+NEXUS_BATTERY_HELP = "# HELP nexus_battery The battery status."
+NEXUS_BATTERY_TYPE = "# TYPE nexus_battery gauge"
+
+METRICS_PREFIXES = {
+ "weather_temperature": [WEATHER_TEMP_HELP, WEATHER_TEMP_TYPE],
+ "nexus_temperature": [NEXUS_TEMP_HELP, NEXUS_TEMP_TYPE],
+}
+
+METRIC_FORMATS = {
+ "weather_temperature": WEATHER_METRIC,
+ "nexus_temperature": NEXUS_METRIC
+}
+
+#{"time" : "2021-05-08 15:27:58", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 202,
+# "battery_ok" : 0, "temperature_C" : 6.900, "humidity" : 63, "wind_dir_deg" : 158,
+# "wind_avg_km_h" : 4.896, "wind_max_km_h" : 8.568, "rain_mm" : 2.400, "mic" : "CRC"}
+#{"time" : "2021-05-08 15:28:02", "model" : "Nexus-TH", "id" : 177, "channel" : 3,
+# "battery_ok" : 0, "temperature_C" : 21.300, "humidity" : 39}
+
+def prometheus(line):
+ payload = json.loads(line)
+
+ if payload["model"] == "Fineoffset-WHx080":
+ weather(payload)
+ elif payload["model"] == "Nexus-TH":
+ nexus(payload)
+ else:
+ model = payload["model"]
+ logging.warn(f"Unknown message model {model}")
+
+def get_metrics():
+ lines = []
+ for metric_name in sorted(set([m[0] for m in METRICS])):
+ lines.extend(METRICS_PREFIXES[metric_name])
+ for metric_key, value in METRICS.items():
+ if metric_key[0] == metric_name:
+ lines.append(METRIC_FORMATS[metric_name] % (metric_key + (value, )))
+
+ return "\n".join(lines)
+
+def weather(payload):
+ METRICS[("weather_temperature", payload["id"])] = payload["temperature_C"]
+
+def nexus(payload):
+ METRICS[("nexus_temperature", payload["id"], payload["channel"])] = payload["temperature_C"]
diff --git a/prom433/server.py b/prom433/server.py
new file mode 100644
index 0000000..09be186
--- /dev/null
+++ b/prom433/server.py
@@ -0,0 +1,67 @@
+# glowprom
+# Copyright (C) 2020 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from datetime import datetime
+import http.server
+
+from .prometheus import prometheus
+
+
+STATS = None
+
+
+class Handler(http.server.BaseHTTPRequestHandler):
+ def do_GET(self):
+ if self.path == "/":
+ self.send_index()
+ elif self.path == "/metrics":
+ self.send_metrics()
+ else:
+ self.send_error(404)
+
+ def send_index(self):
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("""
+
+
RTL433 Prometheus
+
+RTL433 Prometheus
+Metrics
+
+""".encode("utf8"))
+
+ def send_metrics(self):
+ if STATS is None:
+ self.send_response(404)
+ self.end_headers()
+ else:
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(STATS.encode("utf8"))
+
+
+def serve(args): # pragma: no cover
+ server = http.server.HTTPServer(args.bind, Handler)
+ server.serve_forever()
+
+
+def update_stats(client, userdata, msg):
+ global STATS
+ if msg is None:
+ STATS = None
+ else:
+ STATS = prometheus(msg)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..1d027de
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+pycodestyle==2.7.0
+coverage==4.5.4
+python-coveralls==2.9.3
+python-semantic-release==7.15.1
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..86a9070
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+coverage run test.py
+
+let R=$?
+
+coverage report
+
+coverage html
+
+exit $R
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..6d5719b
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[semantic_release]
+version_variable = prom433/__init__.py:__version__
+build_command = python3 setup.py sdist bdist_wheel
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..3265bcb
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,45 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import setuptools
+
+from glowprom import __version__
+
+with open("README.md", "r") as fh:
+ long_description = fh.read()
+
+with open('requirements.txt') as f:
+ requirements = f.read().splitlines()
+
+setuptools.setup(
+ name="prom433",
+ version=__version__,
+ author="Andrew Wilkinson",
+ author_email="andrewjwilkinson@gmail.com",
+ description="Exposes Prometheus metrics based on data received by rtl_433",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ url="https://github.com/andrewjw/prom433",
+ packages=setuptools.find_packages(),
+ scripts=["bin/prom433"],
+ classifiers=[
+ "Programming Language :: Python :: 3.8",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+ "Operating System :: OS Independent",
+ ],
+ python_requires='>=3.8',
+ install_requires=requirements,
+)
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..0b734cf
--- /dev/null
+++ b/test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3.8
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import os
+import unittest
+import sys
+
+import tests
+
+def suite():
+ """Test suite"""
+
+ return unittest.defaultTestLoader.loadTestsFromModule(tests)
+
+if __name__ == "__main__":
+ RESULT = unittest.TextTestRunner(verbosity=2).run(suite())
+
+ sys.exit(0 if RESULT.wasSuccessful() else 1)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..ad5fc54
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,18 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from .test_arguments import TestArguments
+from .test_prometheus import TestPrometheus
diff --git a/tests/output_sample.txt b/tests/output_sample.txt
new file mode 100644
index 0000000..dae1649
--- /dev/null
+++ b/tests/output_sample.txt
@@ -0,0 +1,29 @@
+rtl_433 version 21.05-44-ga4009b6f branch master at 202107121256 inputs file rtl_tcp RTL-SDR
+Use -h for usage help and see https://triq.org/ for documentation.
+Trying conf file at "rtl_433.conf"...
+Trying conf file at "/root/.config/rtl_433/rtl_433.conf"...
+Trying conf file at "/usr/local/etc/rtl_433/rtl_433.conf"...
+Trying conf file at "/etc/rtl_433/rtl_433.conf"...
+Registered 161 out of 190 device decoding protocols [ 1-4 8 11-12 15-17 19-23 25-26 29-36 38-60 63 67-71 73-100 102-105 108-116 119 121 124-128 130-149 151-161 163-168 170-175 177-190 ]
+Found Rafael Micro R820T tuner
+[R82XX] PLL not locked!
+Sample rate set to 300000 S/s.
+Tuner gain set to Auto.
+Tuned to 434.000MHz.
+Allocating 15 zero-copy buffers
+Failed to allocate zero-copy buffer for transfer 0
+Falling back to buffers in userspace
+baseband_demod_FM: low pass filter for 300000 Hz at cutoff 30000 Hz, 33.3 us
+{"time" : "2021-07-12 20:46:23", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 15.900, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:46:23", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 15.900, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:46:56", "model" : "Nexus-TH", "id" : 147, "channel" : 1, "battery_ok" : 0, "temperature_C" : 23.100, "humidity" : 62}
+{"time" : "2021-07-12 20:47:11", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 15.900, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:47:22", "model" : "Nexus-TH", "id" : 137, "channel" : 2, "battery_ok" : 0, "temperature_C" : 23.800, "humidity" : 66}
+{"time" : "2021-07-12 20:47:30", "model" : "Nexus-TH", "id" : 132, "channel" : 3, "battery_ok" : 0, "temperature_C" : 23.100, "humidity" : 65}
+{"time" : "2021-07-12 20:47:53", "model" : "Nexus-TH", "id" : 147, "channel" : 1, "battery_ok" : 0, "temperature_C" : 23.100, "humidity" : 62}
+{"time" : "2021-07-12 20:47:59", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 16.000, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:47:59", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 16.000, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:48:29", "model" : "Nexus-TH", "id" : 137, "channel" : 2, "battery_ok" : 0, "temperature_C" : 23.800, "humidity" : 66}
+{"time" : "2021-07-12 20:48:47", "model" : "Fineoffset-WHx080", "subtype" : 0, "id" : 250, "battery_ok" : 1, "temperature_C" : 16.000, "humidity" : 99, "wind_dir_deg" : 180, "wind_avg_km_h" : 0.000, "wind_max_km_h" : 0.000, "rain_mm" : 95.400, "mic" : "CRC"}
+{"time" : "2021-07-12 20:48:49", "model" : "Nexus-TH", "id" : 132, "channel" : 3, "battery_ok" : 0, "temperature_C" : 23.100, "humidity" : 65}
+{"time" : "2021-07-12 20:48:50", "model" : "Nexus-TH", "id" : 147, "channel" : 1, "battery_ok" : 0, "temperature_C" : 23.100, "humidity" : 62}
diff --git a/tests/test_arguments.py b/tests/test_arguments.py
new file mode 100644
index 0000000..751d131
--- /dev/null
+++ b/tests/test_arguments.py
@@ -0,0 +1,65 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import os
+import unittest
+
+from prom433 import get_arguments, InvalidArguments
+
+
+class TestArguments(unittest.TestCase):
+ def setUp(self):
+ os.environ = {}
+
+ def test_user_environ(self):
+ os.environ["GLOWPROM_USER"] = "testuser"
+ args = get_arguments(["--passwd", "testpassword", "--topic", "topic"])
+ self.assertEqual("testuser", args.user)
+ self.assertEqual("testpassword", args.passwd)
+
+ def test_passwd_environ(self):
+ os.environ["GLOWPROM_PASSWD"] = "testpassword"
+ args = get_arguments(["--user", "testuser", "--topic", "topic"])
+ self.assertEqual("testpassword", args.passwd)
+
+ def test_passwd_environ(self):
+ os.environ["GLOWPROM_TOPIC"] = "topic"
+ args = get_arguments(["--user", "testuser",
+ "--passwd", "testpassword"])
+ self.assertEqual("topic", args.topic)
+
+ def test_bind_without_port(self):
+ os.environ["GLOWPROM_USER"] = "testuser"
+ os.environ["GLOWPROM_PASSWD"] = "testpassword"
+ os.environ["GLOWPROM_TOPIC"] = "topic"
+ args = get_arguments(["--bind", "192.168.1.2"])
+ self.assertEqual("192.168.1.2", args.bind[0])
+ self.assertEqual(9100, args.bind[1])
+
+ def test_no_user(self):
+ os.environ["GLOWPROM_PASSWD"] = "testpasswd"
+ os.environ["GLOWPROM_TOPIC"] = "topic"
+ self.assertRaises(InvalidArguments, get_arguments, [])
+
+ def test_no_passwd(self):
+ os.environ["GLOWPROM_USER"] = "testuser"
+ os.environ["GLOWPROM_TOPIC"] = "topic"
+ self.assertRaises(InvalidArguments, get_arguments, [])
+
+ def test_no_topic(self):
+ os.environ["GLOWPROM_USER"] = "testuser"
+ os.environ["GLOWPROM_PASSWD"] = "testpasswd"
+ self.assertRaises(InvalidArguments, get_arguments, [])
diff --git a/tests/test_prometheus.py b/tests/test_prometheus.py
new file mode 100644
index 0000000..baa2f10
--- /dev/null
+++ b/tests/test_prometheus.py
@@ -0,0 +1,43 @@
+# prom433
+# Copyright (C) 2021 Andrew Wilkinson
+#
+# This program 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import io
+import os
+import unittest
+
+from prom433 import prometheus, get_metrics, child_process
+from prom433.prometheus import METRICS
+
+MESSAGE_TEXT = open("tests/output_sample.txt", "rb").read().decode("utf8")
+
+def mock_popen(args, stdout):
+ return MockPopenReturn(io.StringIO(MESSAGE_TEXT))
+
+class MockPopenReturn:
+ def __init__(self, buffer):
+ self.stdout = buffer
+
+class TestPrometheus(unittest.TestCase):
+ def test_prometheus(self):
+ child_process.rtl433([], prometheus, _popen=mock_popen)
+
+ prom = get_metrics()
+ print(prom)
+
+ self.assertIn(
+ "consumption{type=\"electricity\",period=\"daily\"} 7.971", prom)
+ self.assertIn(
+ "consumption{type=\"gas\",period=\"daily\"} 39.452", prom)