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")