From df9447eee4d07dde4eaab4661cd488e378f18ccf Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 17 Dec 2022 20:23:09 +0100 Subject: [PATCH] first working state with dummy data --- README.md | 56 ++++++++++++++++++++- eq3bt-exporter.py | 109 +++++++++++++++++++++++++++++++++++++++++ eq3bt-exporter.service | 18 +++++++ 3 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 eq3bt-exporter.py create mode 100644 eq3bt-exporter.service diff --git a/README.md b/README.md index d78f54b..103b687 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,57 @@ # eq3bt-exporter -scrape EQ3 bluetooth device and export as prometheus metric \ No newline at end of file +Scrapes [EQ3](https://www.eq-3.com/products/eqiva/detail/bluetooth-smart-radiator-thermostat.html) bluetooth device and export as prometheus metric. + +## Requirements + +- `git clone https://github.com/rytilahti/python-eq3bt` +- `cd python-eq3bt` +- `pip install .` + +## Features and Limitations +- Polls data ever 5 minutes from the EQ3 +- Caches the data for faster prometheus crapes +- Only suppoting **one** eq3bt device at this dev stage + +## Config + +Set your MAC in `eq3bt-exporter.py`. + +**Hint** DonĀ“t scrape with higher intervalls due to blocking based on requests! + +## Usage + +to start: `python3 q3bt-exporter.py` + +to get metrics: `curl http://127.0.0.1:9100/metrics` + +Example metrics: + +``` +eq3bt_expoter_duration_seconds_sum 3 +eq3bt_exporter_request_count 9 +eq3bt_exporter_scrape_healthy 1 +eq3bt_target_temperature 12 +eq3bt_valve 12 +eq3bt_low_battery 0 +eq3bt_window_open 0 +``` + +## Permanent Install + + +`cd /usr/bin/` + +`git clone https://git.mosad.xyz/localhorst/eq3bt-exporter` + +`cd eq3bt-exporter` + +Change host/port in `q3bt-exporter.py` + +`mv eq3bt-exporter.service /usr/lib/systemd/system/` + +`systemctl daemon-reload` + +`systemctl enable --now /usr/lib/systemd/system/eq3bt-exporter.service` + +`systemctl status eq3bt-exporter.service` diff --git a/eq3bt-exporter.py b/eq3bt-exporter.py new file mode 100644 index 0000000..d15a9ed --- /dev/null +++ b/eq3bt-exporter.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" Author: Hendrik Schutter, mail@hendrikschutter.com + Date of creation: 2022/12/17 + Date of last modification: 2022/12/17 +""" + +from http.server import BaseHTTPRequestHandler, HTTPServer +import time +import threading +from datetime import datetime +from urllib.parse import urlsplit, parse_qs +from random import randrange + +hostName = "127.0.0.1" +serverPort = 9100 +exporter_prefix = "eq3bt_" +eq3bt_max = "00:1A:22:0F:20:CA" + +request_count = 0 +scrape_healthy = True +startTime = datetime.now() +eq3bt_metrics = list() +mutex = threading.Lock() + +class RequestHandler(BaseHTTPRequestHandler): + + def get_metrics(self): + global request_count + global eq3bt_metrics + global exporter_prefix + global mutex + mutex.acquire() + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes(exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8")) + self.wfile.write(bytes(exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8")) + self.wfile.write(bytes(exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8")) + + for metric in eq3bt_metrics: + #print(metric) + self.wfile.write(bytes(exporter_prefix + metric + "\n", "utf-8")) + + mutex.release() + + def do_GET(self): + global request_count + request_count = request_count + 1 + print("Request: " + self.path) + if (self.path.startswith("/metrics")): + self.get_metrics() + else: + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("eq3bt exporter", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes('

eq3bt exporter based on data from https://github.com/rytilahti/python-eq3bt/

', "utf-8")) + self.wfile.write(bytes('

Metrics

', "utf-8")) + self.wfile.write(bytes("", "utf-8")) + self.wfile.write(bytes("", "utf-8")) + + +def update_metrics(): + while True: + print("poll data from eq3bt") + global eq3bt_metrics + global mutex + global scrape_healthy + mutex.acquire() + scrape_healthy = True + eq3bt_metrics.clear() + + try: + eq3bt_metrics.append("target_temperature " + str(int(12.2))) + eq3bt_metrics.append("valve " + str(int(12.2))) + eq3bt_metrics.append("low_battery " + str(int(False))) + eq3bt_metrics.append("window_open " + str(int(False))) + except Exception as ex: + print("unable to poll data from eq3bt! error: " + str(ex)) + scrape_healthy = False + pass + mutex.release() + time.sleep(300) + + +def main(): + print("start") + + webServer = HTTPServer((hostName, serverPort), RequestHandler) + + print("Server started http://%s:%s" % (hostName, serverPort)) + + update_metrics_thread = threading.Thread(target=update_metrics, args=()) + update_metrics_thread.start() + + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + print("Server stopped.") + update_metrics_thread.join() + +if __name__ == "__main__": + main() diff --git a/eq3bt-exporter.service b/eq3bt-exporter.service new file mode 100644 index 0000000..c43b2a0 --- /dev/null +++ b/eq3bt-exporter.service @@ -0,0 +1,18 @@ +[Unit] +Description=Prometheus gas station exporter for fuel prices +Documentation=https://git.mosad.xyz/localhorst/gas-station-exporter +Wants=network-online.target +After=network-online.target +After=wg-quick@wg0.service + +[Service] +Restart=always +User=prometheus +WorkingDirectory=/usr/bin/gas-station-exporter/ +ExecStart=/usr/bin/python3 gas-station-exporter.py +ExecReload=/bin/kill -HUP $MAINPID +TimeoutStopSec=20s +SendSIGKILL=no + +[Install] +WantedBy=multi-user.target