inital commit
This commit is contained in:
parent
2e6347214a
commit
ec29ab24f0
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 localhorst
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
14
README.md
14
README.md
@ -1,2 +1,14 @@
|
||||
# VEGAPULS-Air-exporter
|
||||
# The Things Network Exporter for VEGAPULS Air
|
||||
|
||||
Export metrics of a VEGAPULS Air connected via TTN as a prometheus service.
|
||||
|
||||
## Install ##
|
||||
|
||||
- `mkdir /opt/ttn-vegapulsair-exporter/`
|
||||
- `cd /opt/ttn-vegapulsair-exporter/`
|
||||
- import `tttn-vegapulsair-exporter.py` and `config.py`
|
||||
- Set the constants in `config.py`
|
||||
- `chmod +x /opt/ttn-vegapulsair-exporter/ttn-vegapulsair-exporter.py`
|
||||
- `chown -R prometheus /opt/ttn-vegapulsair-exporter/`
|
||||
- `nano /etc/systemd/system/ttn-vegapulsair-exporter.service`
|
||||
- `systemctl daemon-reload && systemctl enable --now ttn-vegapulsair-exporter.service`
|
||||
|
12
config.py
Normal file
12
config.py
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Author: Hendrik Schutter, mail@hendrikschutter.com
|
||||
"""
|
||||
|
||||
hostName = "127.0.0.1"
|
||||
serverPort = 9106
|
||||
exporter_prefix = "vegapulsair_"
|
||||
|
||||
ttn_user = "appid@ttn"
|
||||
ttn_key = "THE APP API KEY FROM TTN CONSOLE"
|
||||
ttn_region = "EU1"
|
181
ttn-vegapulsair-exporter.py
Normal file
181
ttn-vegapulsair-exporter.py
Normal file
@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
""" Author: Hendrik Schutter, mail@hendrikschutter.com
|
||||
"""
|
||||
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
import paho.mqtt.client as mqtt
|
||||
from datetime import datetime
|
||||
import threading
|
||||
import time
|
||||
import json
|
||||
import sys
|
||||
import config
|
||||
|
||||
scrape_healthy = True
|
||||
startTime = datetime.now()
|
||||
node_metrics = list()
|
||||
mutex = threading.Lock()
|
||||
request_count = 0
|
||||
|
||||
class RequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def get_metrics(self):
|
||||
global request_count
|
||||
global node_metrics
|
||||
global mutex
|
||||
mutex.acquire()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
self.wfile.write(bytes(config.exporter_prefix + "expoter_duration_seconds_sum " + str(int((datetime.now() - startTime).total_seconds())) + "\n", "utf-8"))
|
||||
self.wfile.write(bytes(config.exporter_prefix + "exporter_request_count " + str(request_count) + "\n", "utf-8"))
|
||||
self.wfile.write(bytes(config.exporter_prefix + "exporter_scrape_healthy " + str(int(scrape_healthy)) + "\n", "utf-8"))
|
||||
|
||||
for metric in node_metrics:
|
||||
#print(metric)
|
||||
self.wfile.write(bytes(config.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("<html>", "utf-8"))
|
||||
self.wfile.write(bytes("<head><title>VEGAPULS Air exporter</title></head>", "utf-8"))
|
||||
self.wfile.write(bytes("<body>", "utf-8"))
|
||||
self.wfile.write(bytes('<h1>ttn-vegapulsair exporter based on data from LoRaWAN TTN node.</h1>', "utf-8"))
|
||||
self.wfile.write(bytes('<p><a href="/metrics">Metrics</a></p>', "utf-8"))
|
||||
self.wfile.write(bytes("</body>", "utf-8"))
|
||||
self.wfile.write(bytes("</html>", "utf-8"))
|
||||
|
||||
def update_metrics(payload, metadata):
|
||||
#print("Payload: "+ str(payload))
|
||||
#print("Metadata: "+ str(metadata))
|
||||
|
||||
global node_metrics
|
||||
global mutex
|
||||
global scrape_healthy
|
||||
mutex.acquire()
|
||||
scrape_healthy = True
|
||||
node_metrics.clear()
|
||||
|
||||
if "Distance" in payload:
|
||||
print("set Distance: " + str(float(payload["Distance"])))
|
||||
node_metrics.append("distance " + str(float(payload["Distance"])))
|
||||
|
||||
if "Inclination_degree" in payload:
|
||||
print("set Inclination_degree: " + str(int(payload["Inclination_degree"])))
|
||||
node_metrics.append("inclination_degree " + str(int(payload["Inclination_degree"])))
|
||||
|
||||
if "MvLinProcent" in payload:
|
||||
print("set MvLinProcent: " + str(int(payload["MvLinProcent"])))
|
||||
node_metrics.append("linprocent " + str(int(payload["MvLinProcent"])))
|
||||
|
||||
if "MvProcent" in payload:
|
||||
print("set MvProcent: " + str(int(payload["MvProcent"])))
|
||||
node_metrics.append("procent " + str(int(payload["MvProcent"])))
|
||||
|
||||
if "MvScaled" in payload:
|
||||
print("set MvScaled: " + str(float(payload["MvScaled"])))
|
||||
node_metrics.append("scaled " + str(float(payload["MvScaled"])))
|
||||
|
||||
if "MvScaledUnit" in payload:
|
||||
print("set MvScaledUnit: " + str(int(payload["MvScaledUnit"])))
|
||||
node_metrics.append("scaled_unit " + str(int(payload["MvScaledUnit"])))
|
||||
|
||||
if "PacketIdentifier" in payload:
|
||||
print("set PacketIdentifier: " + str(int(payload["PacketIdentifier"])))
|
||||
node_metrics.append("packet_identifier " + str(int(payload["PacketIdentifier"])))
|
||||
|
||||
if "RemainingPower" in payload:
|
||||
print("set RemainingPower: " + str(int(payload["RemainingPower"])))
|
||||
node_metrics.append("remaining_power " + str(int(payload["RemainingPower"])))
|
||||
|
||||
if "Temperature" in payload:
|
||||
print("set Temperature: " + str(int(payload["Temperature"])))
|
||||
node_metrics.append("temperature " + str(int(payload["Temperature"])))
|
||||
|
||||
if "Unit" in payload:
|
||||
print("set Unit: " + str(int(payload["Unit"])))
|
||||
node_metrics.append("unit " + str(int(payload["Unit"])))
|
||||
|
||||
if "UnitTemperature" in payload:
|
||||
print("set UnitTemperature: " + str(int(payload["UnitTemperature"])))
|
||||
node_metrics.append("temperature_unit " + str(int(payload["UnitTemperature"])))
|
||||
|
||||
if "rssi" in metadata[0]:
|
||||
print("set rssi: " + str(int(metadata[0]["rssi"])))
|
||||
node_metrics.append("rssi " + str(int(metadata[0]["rssi"])))
|
||||
|
||||
if "channel_rssi" in metadata[0]:
|
||||
print("set channel_rssi: " + str(int(metadata[0]["channel_rssi"])))
|
||||
node_metrics.append("channel_rssi " + str(int(metadata[0]["channel_rssi"])))
|
||||
|
||||
if "snr" in metadata[0]:
|
||||
print("set snr: " + str(float(metadata[0]["snr"])))
|
||||
node_metrics.append("snr " + str(float(metadata[0]["snr"])))
|
||||
|
||||
mutex.release()
|
||||
|
||||
def on_connect(mqttc, obj, flags, rc):
|
||||
print("\nConnected to MQTT: rc = " + str(rc))
|
||||
|
||||
def on_message(mqttc, obj, msg):
|
||||
#print("\nMessage: " + msg.topic + " " + str(msg.qos))
|
||||
parsedJSON = json.loads(msg.payload)
|
||||
#print(json.dumps(parsedJSON, indent=4))
|
||||
|
||||
try:
|
||||
uplink_message = parsedJSON["uplink_message"]
|
||||
update_metrics(uplink_message["decoded_payload"], uplink_message["rx_metadata"])
|
||||
except:
|
||||
mutex.acquire()
|
||||
scrape_healthy = False
|
||||
mutex.release()
|
||||
print("Unable to parse uplink")
|
||||
|
||||
def on_subscribe(mqttc, obj, mid, granted_qos):
|
||||
print("\nSubscribed to MQTT: " + str(mid) + " " + str(granted_qos))
|
||||
|
||||
def poll_mqtt(mqttc):
|
||||
while True:
|
||||
mqttc.loop(10) # seconds timeout
|
||||
|
||||
def main():
|
||||
print("starting ...")
|
||||
|
||||
mqttc = mqtt.Client()
|
||||
mqttc.on_connect = on_connect
|
||||
mqttc.on_subscribe = on_subscribe
|
||||
mqttc.on_message = on_message
|
||||
mqttc.username_pw_set(config.ttn_user, config.ttn_key)
|
||||
mqttc.tls_set()
|
||||
mqttc.connect(config.ttn_region.lower() + ".cloud.thethings.network", 8883, 60)
|
||||
mqttc.subscribe("#", 0) # all device uplinks
|
||||
|
||||
# run mqtt in thread forever
|
||||
poll_mqtt_thread = threading.Thread(target=poll_mqtt, args=((mqttc,)))
|
||||
poll_mqtt_thread.start()
|
||||
|
||||
webServer = HTTPServer((config.hostName, config.serverPort), RequestHandler)
|
||||
print("Server started http://%s:%s" % (config.hostName, config.serverPort))
|
||||
|
||||
try:
|
||||
webServer.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(-1)
|
||||
|
||||
webServer.server_close()
|
||||
print("Server stopped.")
|
||||
poll_mqtt_thread.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
15
ttn-vegapulsair-exporter.service
Normal file
15
ttn-vegapulsair-exporter.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=TTN Exporter for VEGAPULS Air
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
RestartSec=2s
|
||||
Type=oneshot
|
||||
User=prometheus
|
||||
Group=prometheus
|
||||
WorkingDirectory=/opt/ttn-vegapulsair-exporter/
|
||||
ExecStart=/usr/bin/python3 /opt/ttn-vegapulsair-exporter/ttn-vegapulsair-exporter.py
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Reference in New Issue
Block a user