199 lines
6.2 KiB
Python
199 lines
6.2 KiB
Python
import paho.mqtt.client as mqtt
|
|
import json
|
|
import os
|
|
import logging
|
|
import signal
|
|
import requests
|
|
from datetime import datetime
|
|
from dotenv import find_dotenv, load_dotenv
|
|
from pprint import pprint
|
|
|
|
load_dotenv(find_dotenv(filename=".env_rak2171"))
|
|
SRC_MQTT_HOST = os.getenv("SRC_MQTT_HOST")
|
|
SRC_MQTT_USER = os.getenv("SRC_MQTT_USER")
|
|
SRC_MQTT_PASS = os.getenv("SRC_MQTT_PASS")
|
|
DST_MQTT_HOST = os.getenv("DST_MQTT_HOST")
|
|
DST_MQTT_USER = os.getenv("DST_MQTT_USER")
|
|
DST_MQTT_PASS = os.getenv("DST_MQTT_PASS")
|
|
DST_TRACCAR_URL = os.getenv("DST_TRACCAR_URL")
|
|
PUSHOVER_TOKEN = os.getenv("PUSHOVER_TOKEN")
|
|
PUSHOVER_USER_KEY = os.getenv("PUSHOVER_USER_KEY")
|
|
VERSION = "v1.2"
|
|
|
|
OT_TOPIC_PREFIX = "owntracks/things/"
|
|
|
|
|
|
def on_connect_ttn(client, userdata, flags, rc):
|
|
logging.info("connected to ttn %s - %s", SRC_MQTT_HOST, str(rc))
|
|
client.subscribe("v3/+/devices/+/up")
|
|
|
|
|
|
def on_connect_ot(client, userdata, flags, rc):
|
|
logging.info("connected to ot %s - %s", DST_MQTT_HOST, str(rc))
|
|
|
|
|
|
def on_publish_ot(client, userdata, rc):
|
|
logging.info("published data to ot")
|
|
|
|
|
|
def on_log(client, userdata, level, buf):
|
|
logging_level = mqtt.LOGGING_LEVEL[level]
|
|
logging.log(logging_level, buf)
|
|
# logging.info("got a log message level %s: %s", level, str(buf))
|
|
|
|
|
|
def push_msg(msg, device_id):
|
|
requests.post(
|
|
"https://api.pushover.net/1/messages.json",
|
|
data={
|
|
"token": PUSHOVER_TOKEN,
|
|
"user": PUSHOVER_USER_KEY,
|
|
"message": msg,
|
|
"title": f"Nachricht von {device_id}",
|
|
},
|
|
)
|
|
|
|
|
|
def check_batt(batt, device_id):
|
|
msg = f"Batterie von {device_id} ist unter 20%: {batt}%"
|
|
if batt <= 20 and batt >= 1:
|
|
logging.info(msg)
|
|
push_msg(msg=msg, device_id=device_id)
|
|
|
|
|
|
# The callback for when a PUBLISH message is received from the server.
|
|
def on_message_ttn(client, userdata, msg):
|
|
data = json.loads(msg.payload)
|
|
device_id = data["end_device_ids"]["device_id"]
|
|
logging.info(
|
|
"message from ttn received for %s via %s",
|
|
device_id,
|
|
data["uplink_message"]["network_ids"]["cluster_id"],
|
|
)
|
|
|
|
# retrieve info about gateway
|
|
gtw_id = []
|
|
for gtw in data["uplink_message"]["rx_metadata"]:
|
|
gtw_id.append(gtw["gateway_ids"]["gateway_id"])
|
|
|
|
gws = ",".join(gtw_id)
|
|
logging.info(f"received via ttn gateway: {gws}")
|
|
|
|
# the decoded data
|
|
fix = data["uplink_message"]["decoded_payload"].get("fix", 0)
|
|
sos = data["uplink_message"]["decoded_payload"].get("sos", 0)
|
|
alarm = data["uplink_message"]["decoded_payload"].get("alarm", 0)
|
|
batt = data["uplink_message"]["decoded_payload"].get("batt", 0)
|
|
if fix == 1:
|
|
logging.info("got payload with a fix")
|
|
latitude = data["uplink_message"]["decoded_payload"].get("lat", 0.0)
|
|
longitude = data["uplink_message"]["decoded_payload"].get("lng", 0.0)
|
|
timestamp = data["uplink_message"]["decoded_payload"].get("time", 0)
|
|
accuracy = data["uplink_message"]["decoded_payload"].get("acc", 0)
|
|
battery = data["uplink_message"]["decoded_payload"].get("batt", 0)
|
|
ot_data = json.dumps(
|
|
{
|
|
"_type": "location",
|
|
"acc": accuracy,
|
|
"lat": latitude,
|
|
"lon": longitude,
|
|
"batt": battery,
|
|
"t": "p",
|
|
"tid": device_id,
|
|
"tst": timestamp,
|
|
"conn": "m",
|
|
}
|
|
)
|
|
|
|
# publish to owntracks via MQTT
|
|
logging.info(
|
|
"publishing data to owntracks via mqtt to topic %s",
|
|
OT_TOPIC_PREFIX + device_id,
|
|
)
|
|
client_ot.publish(
|
|
OT_TOPIC_PREFIX + device_id, payload=ot_data, retain=True, qos=1
|
|
)
|
|
|
|
# check battery
|
|
check_batt(batt=batt, device_id=device_id)
|
|
|
|
# send to traccar
|
|
logging.info("publishing data to traccar")
|
|
traccar_url = f"{DST_TRACCAR_URL}/?id={device_id}&lat={latitude}&lon={longitude}×tamp={timestamp}&accuracy={accuracy}&batt={battery}"
|
|
requests.get(traccar_url)
|
|
# elif sos == 0:
|
|
# logging.info("got payload with a sos")
|
|
# push_msg(msg=f"Knopf 5 mal gedrückt - deaktiviert", device_id=device_id)
|
|
elif sos == 1:
|
|
logging.info("got payload with a sos")
|
|
push_msg(msg=f"Knopf 5 mal gedrückt - aktiviert", device_id=device_id)
|
|
# TODO publish geo:// URL
|
|
elif alarm == 1:
|
|
logging.info("got payload with an alarm")
|
|
else:
|
|
logging.info("no usable payload - skipping")
|
|
# check battery
|
|
check_batt(batt=batt, device_id=device_id)
|
|
|
|
|
|
def shutdown():
|
|
logging.info("disconnecting from mqtt")
|
|
client_ot.disconnect()
|
|
client_ot.loop_stop()
|
|
client_ttn.disconnect()
|
|
client_ttn.loop_stop()
|
|
|
|
|
|
def handleSIGTERM(signalNumber, frame):
|
|
logging.info("got SIGTERM")
|
|
shutdown()
|
|
return
|
|
|
|
|
|
if __name__ == "__main__":
|
|
signal.signal(signal.SIGTERM, handleSIGTERM)
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s - %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S %Z",
|
|
)
|
|
|
|
logging.info("Starting ioteer rak2171 - " + VERSION)
|
|
|
|
# Prepare MQTT for The Things Network
|
|
client_ttn = mqtt.Client()
|
|
client_ttn.enable_logger()
|
|
client_ttn.on_connect = on_connect_ttn
|
|
client_ttn.on_message = on_message_ttn
|
|
client_ttn.on_log = on_log
|
|
client_ttn.username_pw_set(SRC_MQTT_USER, SRC_MQTT_PASS)
|
|
client_ttn.tls_set()
|
|
client_ttn.connect(SRC_MQTT_HOST, 8883, 60)
|
|
|
|
# Prepare MQTT for OwnTracks
|
|
ot_lwt = json.dumps(
|
|
{
|
|
"_type": "lwt",
|
|
"tst": int(datetime.timestamp(datetime.now())),
|
|
}
|
|
)
|
|
client_ot = mqtt.Client()
|
|
client_ot.enable_logger()
|
|
client_ot.on_connect = on_connect_ot
|
|
client_ot.on_publish = on_publish_ot
|
|
client_ot.on_log = on_log
|
|
client_ot.username_pw_set(DST_MQTT_USER, DST_MQTT_PASS)
|
|
client_ot.tls_set()
|
|
# todo: this is wrong
|
|
# client_ot.will_set(OT_TOPIC_PREFIX + device_id, payload=ot_lwt, qos=1, retain=True)
|
|
client_ot.connect(DST_MQTT_HOST, 8883, 60)
|
|
|
|
try:
|
|
# Connect to MQTT and react to messages
|
|
client_ot.loop_start()
|
|
client_ttn.loop_forever()
|
|
except KeyboardInterrupt:
|
|
shutdown()
|
|
logging.info("tschuess")
|