2020-04-30 18:23:23 +00:00
|
|
|
import paho.mqtt.client as mqtt
|
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import logging
|
2020-05-02 12:43:30 +00:00
|
|
|
import signal
|
2020-05-09 12:21:18 +00:00
|
|
|
import requests
|
2020-04-30 18:23:23 +00:00
|
|
|
from datetime import datetime
|
|
|
|
from influxdb import InfluxDBClient
|
|
|
|
from dotenv import find_dotenv, load_dotenv
|
|
|
|
|
|
|
|
load_dotenv(find_dotenv())
|
|
|
|
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_INFLUX_HOST = os.getenv("DST_INFLUX_HOST")
|
|
|
|
DST_INFLUX_USER = os.getenv("DST_INFLUX_USER")
|
|
|
|
DST_INFLUX_PASS = os.getenv("DST_INFLUX_PASS")
|
|
|
|
DST_INFLUX_DB = os.getenv("DST_INFLUX_DB")
|
2021-10-19 19:02:48 +00:00
|
|
|
DST_TRACCAR_URL = os.getenv("DST_TRACCAR_URL")
|
2020-05-09 12:21:18 +00:00
|
|
|
HC_PING_URL = os.getenv("HC_PING_URL")
|
2021-10-19 19:02:48 +00:00
|
|
|
VERSION = "v3.1"
|
2020-04-30 18:23:23 +00:00
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
OT_TOPIC = "owntracks/tobru/dragino"
|
|
|
|
OT_TID = "dragino"
|
|
|
|
|
2020-04-30 18:23:23 +00:00
|
|
|
|
|
|
|
def on_connect_ttn(client, userdata, flags, rc):
|
|
|
|
logging.info("connected to ttn %s - %s", SRC_MQTT_HOST, str(rc))
|
2021-10-16 12:55:00 +00:00
|
|
|
client.subscribe("v3/+/devices/+/up")
|
2020-04-30 18:23:23 +00:00
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-04-30 18:23:23 +00:00
|
|
|
def on_connect_ot(client, userdata, flags, rc):
|
|
|
|
logging.info("connected to ot %s - %s", DST_MQTT_HOST, str(rc))
|
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-04-30 18:23:23 +00:00
|
|
|
def on_publish_ot(client, userdata, rc):
|
2020-05-01 18:05:50 +00:00
|
|
|
logging.info("published data to ot")
|
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-05-01 18:05:50 +00:00
|
|
|
def on_log(client, userdata, level, buf):
|
|
|
|
logging_level = mqtt.LOGGING_LEVEL[level]
|
|
|
|
logging.log(logging_level, buf)
|
2021-10-18 05:47:23 +00:00
|
|
|
# logging.info("got a log message level %s: %s", level, str(buf))
|
2020-05-09 12:21:18 +00:00
|
|
|
if "PINGRESP" in str(buf):
|
|
|
|
# report to https://healthchecks.io to tell that the connection is alive
|
|
|
|
requests.get(HC_PING_URL)
|
2020-04-30 18:23:23 +00:00
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-04-30 18:23:23 +00:00
|
|
|
# The callback for when a PUBLISH message is received from the server.
|
|
|
|
def on_message_ttn(client, userdata, msg):
|
|
|
|
data = json.loads(msg.payload)
|
2021-10-18 05:47:23 +00:00
|
|
|
logging.info(
|
2021-10-19 18:13:35 +00:00
|
|
|
"message from ttn received for %s via %s",
|
|
|
|
data["end_device_ids"]["device_id"],
|
|
|
|
data["uplink_message"]["network_ids"]["cluster_id"],
|
2021-10-18 05:47:23 +00:00
|
|
|
)
|
2020-05-10 19:52:01 +00:00
|
|
|
|
|
|
|
# retrieve info about gateway
|
2021-10-18 06:00:44 +00:00
|
|
|
gtw_id = data["uplink_message"]["rx_metadata"][0]["gateway_ids"]["gateway_id"]
|
2020-05-17 16:08:46 +00:00
|
|
|
try:
|
2021-10-18 05:47:23 +00:00
|
|
|
gtw_info = requests.get(
|
|
|
|
"https://www.thethingsnetwork.org/gateway-data/gateway/" + gtw_id
|
|
|
|
).json()
|
|
|
|
logging.info(
|
|
|
|
"received via gw %s, %s, owned by %s",
|
2020-05-17 16:08:46 +00:00
|
|
|
data["metadata"]["gateways"][0]["gtw_id"],
|
|
|
|
gtw_info[gtw_id]["description"],
|
|
|
|
gtw_info[gtw_id]["owner"],
|
|
|
|
)
|
|
|
|
except:
|
|
|
|
logging.info("received via gw %s", gtw_id)
|
2020-05-01 18:05:50 +00:00
|
|
|
|
2021-10-19 18:13:35 +00:00
|
|
|
# the decoded data
|
|
|
|
bat_v = data["uplink_message"]["decoded_payload"]["BatV"]
|
|
|
|
motion_detection = data["uplink_message"]["decoded_payload"]["MD"]
|
|
|
|
led_status = data["uplink_message"]["decoded_payload"]["LON"]
|
|
|
|
fw_version = data["uplink_message"]["decoded_payload"]["FW"]
|
|
|
|
pitch = data["uplink_message"]["decoded_payload"]["Pitch"]
|
|
|
|
roll = data["uplink_message"]["decoded_payload"]["Roll"]
|
|
|
|
latitude = data["uplink_message"]["decoded_payload"]["Latitude"]
|
|
|
|
longitude = data["uplink_message"]["decoded_payload"]["Longitude"]
|
2021-10-19 19:02:48 +00:00
|
|
|
altitude = data["uplink_message"]["decoded_payload"]["Altitude"]
|
|
|
|
if data["uplink_message"]["decoded_payload"]["HDOP"]:
|
|
|
|
hdop = data["uplink_message"]["decoded_payload"]["HDOP"]
|
|
|
|
else:
|
|
|
|
hdop = 0
|
2021-10-19 18:13:35 +00:00
|
|
|
rssi = data["uplink_message"]["rx_metadata"][0]["rssi"]
|
|
|
|
airtime = data["uplink_message"]["consumed_airtime"][:-1]
|
2021-10-19 19:02:48 +00:00
|
|
|
timestamp = int(datetime.timestamp(datetime.now()))
|
2021-10-19 18:13:35 +00:00
|
|
|
|
2020-05-09 12:05:19 +00:00
|
|
|
# max is 4 volts, 3 volts is considered empty
|
2021-10-19 18:13:35 +00:00
|
|
|
batpercent = round((bat_v - 3) * 100)
|
2020-04-30 18:23:23 +00:00
|
|
|
|
2021-10-19 18:13:35 +00:00
|
|
|
alarm_status = False
|
|
|
|
if data["uplink_message"]["decoded_payload"]["ALARM_status"] == "TRUE":
|
|
|
|
alarm_status = True
|
2020-06-06 14:04:59 +00:00
|
|
|
logging.info("Red button pushed!")
|
|
|
|
|
2021-10-19 18:13:35 +00:00
|
|
|
logging.info("Motion detection: %s", motion_detection)
|
|
|
|
logging.info("LED status for position: %s", led_status)
|
|
|
|
logging.info("Firmware version: %s", fw_version)
|
2020-05-09 12:21:18 +00:00
|
|
|
|
2020-05-17 16:08:46 +00:00
|
|
|
got_fix = False
|
2021-10-19 18:13:35 +00:00
|
|
|
if latitude == 0:
|
2020-06-06 13:50:49 +00:00
|
|
|
logging.info("no GPS data (Latitude) present")
|
|
|
|
# set GPS data to 0 for InfluxDB
|
2021-10-19 18:13:35 +00:00
|
|
|
latitude = 0.0
|
|
|
|
longitude = 0.0
|
2020-06-06 13:50:49 +00:00
|
|
|
else:
|
2021-10-18 05:47:23 +00:00
|
|
|
logging.info(
|
|
|
|
"GPS data (Latitude) present: lat %s, lon %s",
|
2021-10-19 18:13:35 +00:00
|
|
|
latitude,
|
|
|
|
longitude,
|
2020-06-06 14:04:59 +00:00
|
|
|
)
|
2020-05-17 16:08:46 +00:00
|
|
|
got_fix = True
|
2020-04-30 18:23:23 +00:00
|
|
|
# transform received data into OwnTracks format
|
2021-10-18 05:47:23 +00:00
|
|
|
ot_data = json.dumps(
|
|
|
|
{
|
|
|
|
"_type": "location",
|
2021-10-19 18:13:35 +00:00
|
|
|
"lat": latitude,
|
|
|
|
"lon": longitude,
|
2021-10-18 05:47:23 +00:00
|
|
|
"batt": batpercent,
|
|
|
|
"t": "p",
|
|
|
|
"tid": OT_TID,
|
2021-10-19 19:02:48 +00:00
|
|
|
"tst": timestamp,
|
2021-10-18 05:47:23 +00:00
|
|
|
"conn": "m",
|
|
|
|
}
|
|
|
|
)
|
2020-04-30 18:23:23 +00:00
|
|
|
|
|
|
|
# publish to owntracks
|
2020-06-06 14:04:59 +00:00
|
|
|
logging.info("publishing data to owntracks via mqtt to topic %s", OT_TOPIC)
|
2020-05-10 19:52:01 +00:00
|
|
|
client_ot.publish(OT_TOPIC, payload=ot_data, retain=True, qos=1)
|
2020-05-09 12:05:19 +00:00
|
|
|
|
2021-10-19 19:02:48 +00:00
|
|
|
# send to traccar
|
|
|
|
logging.info("publishing data to traccar")
|
|
|
|
traccar_url = f"{DST_TRACCAR_URL}/?id={OT_TID}&lat={latitude}&lon={longitude}×tamp={timestamp}&hdop={hdop}&altitude={altitude}&speed=0"
|
|
|
|
requests.get(traccar_url)
|
|
|
|
|
2020-05-09 12:05:19 +00:00
|
|
|
# write to influxdb
|
|
|
|
logging.info("writing data to influxdb")
|
2021-10-19 18:13:35 +00:00
|
|
|
influxdb_points = [
|
|
|
|
{
|
|
|
|
"measurement": "dragino",
|
|
|
|
"tags": {
|
|
|
|
"device": "lgt92",
|
|
|
|
},
|
|
|
|
"fields": {
|
|
|
|
"bat": float(bat_v),
|
|
|
|
"pitch": float(pitch),
|
|
|
|
"roll": float(roll),
|
|
|
|
"lat": float(latitude),
|
|
|
|
"lon": float(longitude),
|
|
|
|
"alarm": int(alarm_status),
|
|
|
|
"airtime": float(airtime),
|
|
|
|
"rssi": rssi,
|
|
|
|
"fix": got_fix,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
]
|
|
|
|
influxdb.write_points(influxdb_points)
|
2020-04-30 18:23:23 +00:00
|
|
|
|
|
|
|
logging.info("data processing done")
|
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-05-02 12:43:30 +00:00
|
|
|
def shutdown():
|
|
|
|
logging.info("disconnecting from mqtt")
|
2020-04-30 18:23:23 +00:00
|
|
|
client_ot.disconnect()
|
|
|
|
client_ot.loop_stop()
|
|
|
|
client_ttn.disconnect()
|
|
|
|
client_ttn.loop_stop()
|
2020-05-02 12:43:30 +00:00
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
2020-05-02 12:43:30 +00:00
|
|
|
def handleSIGTERM(signalNumber, frame):
|
|
|
|
logging.info("got SIGTERM")
|
|
|
|
shutdown()
|
|
|
|
return
|
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2020-05-02 12:43:30 +00:00
|
|
|
|
|
|
|
signal.signal(signal.SIGTERM, handleSIGTERM)
|
|
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
level=logging.INFO,
|
2021-10-18 05:47:23 +00:00
|
|
|
format="%(asctime)s - %(message)s",
|
|
|
|
datefmt="%Y-%m-%d %H:%M:%S %Z",
|
2020-05-02 12:43:30 +00:00
|
|
|
)
|
|
|
|
|
2021-10-18 05:47:23 +00:00
|
|
|
logging.info("Starting ioteer lgt92. " + VERSION)
|
2020-05-09 12:21:18 +00:00
|
|
|
|
2020-05-02 12:43:30 +00:00
|
|
|
# Prepare InfluxDB
|
|
|
|
influxdb = InfluxDBClient(
|
2021-10-18 05:47:23 +00:00
|
|
|
host=DST_INFLUX_HOST,
|
|
|
|
port=443,
|
|
|
|
database=DST_INFLUX_DB,
|
|
|
|
username=DST_INFLUX_USER,
|
|
|
|
password=DST_INFLUX_PASS,
|
|
|
|
ssl=True,
|
|
|
|
verify_ssl=True,
|
2020-05-02 12:43:30 +00:00
|
|
|
)
|
|
|
|
|
2020-05-10 19:52:01 +00:00
|
|
|
# Prepare MQTT for The Things Network
|
2020-05-02 12:43:30 +00:00
|
|
|
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
|
2021-10-18 05:47:23 +00:00
|
|
|
client_ttn.username_pw_set(SRC_MQTT_USER, SRC_MQTT_PASS)
|
2020-05-02 12:43:30 +00:00
|
|
|
client_ttn.tls_set()
|
|
|
|
client_ttn.connect(SRC_MQTT_HOST, 8883, 60)
|
|
|
|
|
2020-05-10 19:52:01 +00:00
|
|
|
# Prepare MQTT for OwnTracks
|
2021-10-18 05:47:23 +00:00
|
|
|
ot_lwt = json.dumps(
|
|
|
|
{
|
|
|
|
"_type": "lwt",
|
|
|
|
"tst": int(datetime.timestamp(datetime.now())),
|
|
|
|
}
|
|
|
|
)
|
2020-05-02 12:43:30 +00:00
|
|
|
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
|
2021-10-18 05:47:23 +00:00
|
|
|
client_ot.username_pw_set(DST_MQTT_USER, DST_MQTT_PASS)
|
2020-05-02 12:43:30 +00:00
|
|
|
client_ot.tls_set()
|
2020-05-10 19:52:01 +00:00
|
|
|
client_ot.will_set(OT_TOPIC, payload=ot_lwt, qos=1, retain=True)
|
2020-05-02 12:43:30 +00:00
|
|
|
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()
|
2020-05-31 19:55:32 +00:00
|
|
|
logging.info("tschuess")
|