Add MQTT and gateway discovery
This commit is contained in:
parent
e162948cb8
commit
d7b3303e50
@ -45,6 +45,9 @@ RUN apk add --no-cache sudo bluez tzdata
|
||||
ENV TZ=Europe/Berlin
|
||||
ENV DOCKER=true
|
||||
ENV API=false
|
||||
ENV NAME=ATC_MiThermometer_Gateway
|
||||
ENV VERSION=24-07-03
|
||||
ENV MODE=1
|
||||
|
||||
# Copy pips from the pip build stage
|
||||
COPY --from=pip_build_stage /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||
|
||||
@ -5,7 +5,6 @@ env > .env
|
||||
if [ "$API" = true ]; then
|
||||
python3.12 api_endpoints.py &
|
||||
sleep 1
|
||||
python3.12 find_gateways.py &
|
||||
fi
|
||||
|
||||
python3.12 main.py
|
||||
@ -5,3 +5,4 @@ requests
|
||||
flask
|
||||
flask_cors
|
||||
FindMyIP
|
||||
paho-mqtt
|
||||
@ -4,6 +4,7 @@ from flask import jsonify
|
||||
from flask import send_from_directory
|
||||
from flask_cors import CORS
|
||||
from flask_cors import cross_origin
|
||||
from check_update import check_for_update
|
||||
|
||||
|
||||
class API:
|
||||
@ -17,43 +18,44 @@ class API:
|
||||
self.app.config['CORS_HEADERS'] = 'Content-Type'
|
||||
|
||||
# --------Static Routes-------
|
||||
@self.app.route('/')
|
||||
@self.app.route('/api')
|
||||
@cross_origin()
|
||||
def serve_root():
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
update = check_for_update()
|
||||
root_dict = {
|
||||
"version": "24-07-03",
|
||||
"mode": 1,
|
||||
"version": {
|
||||
"version": os.getenv('VERSION'),
|
||||
"update_available": update.update_available if update is not None else None,
|
||||
"up_to_date": update.up_to_date if update is not None else None,
|
||||
"develop_version": update.development if update is not None else None,
|
||||
},
|
||||
"mode": os.getenv('MODE'),
|
||||
"name": os.getenv('NAME'),
|
||||
"info": {
|
||||
"files_size_sum": self.get_file_size()
|
||||
"files_size_sum": self.get_file_size(),
|
||||
"files": os.listdir(f'{workdir}/data')
|
||||
}
|
||||
}
|
||||
return jsonify(root_dict)
|
||||
|
||||
@self.app.route('/api/json/<path:path>')
|
||||
@cross_origin()
|
||||
def serve_json(path):
|
||||
return send_from_directory(f'{workdir}/data', path)
|
||||
|
||||
@self.app.route('/charts')
|
||||
@cross_origin()
|
||||
def serve_index():
|
||||
return send_from_directory('/src', 'chart.html')
|
||||
|
||||
@self.app.route('/json')
|
||||
@cross_origin()
|
||||
def serve_get_list_of_json():
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
return jsonify(os.listdir(f'{workdir}/data'))
|
||||
|
||||
@self.app.route('/json/<path:path>')
|
||||
@cross_origin()
|
||||
def serve_json(path):
|
||||
|
||||
return send_from_directory(f'{workdir}/data', path)
|
||||
|
||||
# --------Helpers-------
|
||||
def get_file_size(self):
|
||||
workdir, filename = os.path.split(os.path.abspath(__file__))
|
||||
files = os.listdir(f'{workdir}/data')
|
||||
sizes = 0.0
|
||||
sizes = 0
|
||||
for file in files:
|
||||
if file.endswith('.json'):
|
||||
sizes += round(int(os.path.getsize(f'{workdir}/data/{file}')), 2)
|
||||
|
||||
if file.endswith('.json'): sizes += os.path.getsize(f'{workdir}/data/{file}')
|
||||
return sizes
|
||||
|
||||
|
||||
|
||||
74
python/src/check_update.py
Normal file
74
python/src/check_update.py
Normal file
@ -0,0 +1,74 @@
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
|
||||
DEBUG = True if os.environ.get('DEBUG') is not None else False
|
||||
|
||||
|
||||
class State:
|
||||
def __init__(self, version_int:int):
|
||||
self.version_int:int = version_int
|
||||
self.version_str:str = f"{str(version_int)[0]}{str(version_int)[1]}-{str(version_int)[2]}{str(version_int)[3]}-{str(version_int)[4]}{str(version_int)[5]}"
|
||||
self.update_available:bool = False
|
||||
self.up_to_date:bool = False
|
||||
self.development:bool = False
|
||||
|
||||
|
||||
class Release:
|
||||
def __init__(self, data:dict):
|
||||
self.name = data['name']
|
||||
self.tag_name = data['tag_name']
|
||||
self.description = data['description']
|
||||
self.created_at = data['created_at']
|
||||
self.released_at = data['released_at']
|
||||
self.upcoming_release = data['upcoming_release']
|
||||
self.version_int = int(self.tag_name.replace("-", ""))
|
||||
|
||||
|
||||
def check_for_update():
|
||||
try: version_current = int(os.getenv('VERSION').replace("-", ""))
|
||||
except:
|
||||
print("Error getting current version")
|
||||
return
|
||||
project_id = 58341398
|
||||
request = f"https://gitlab.com/api/v4/projects/{project_id}/releases"
|
||||
response = requests.get(url=request, timeout=1)
|
||||
if not response.ok: return
|
||||
|
||||
releases_json = json.loads(response.text)
|
||||
# Date, Object
|
||||
latest = [0, None]
|
||||
for release_json in releases_json:
|
||||
release = Release(release_json)
|
||||
if release.version_int > latest[0]:
|
||||
latest[0] = release.version_int
|
||||
latest[1] = release
|
||||
print(repr(latest)) if DEBUG else {}
|
||||
|
||||
release = latest[1]
|
||||
if release.version_int > version_current:
|
||||
state = State(release.version_int)
|
||||
state.update_available = True
|
||||
return state
|
||||
|
||||
elif release.version_int == version_current:
|
||||
state = State(release.version_int)
|
||||
state.up_to_date = True
|
||||
return state
|
||||
|
||||
else:
|
||||
state = State(release.version_int)
|
||||
state.development = True
|
||||
return state
|
||||
|
||||
|
||||
def print_state(state:State):
|
||||
if state is None: return
|
||||
print(f"Current version: {os.getenv('VERSION')}")
|
||||
if state.update_available:
|
||||
print(f"Update available: {state.version_str}")
|
||||
if state.up_to_date:
|
||||
print(f"Up to date")
|
||||
if state.development:
|
||||
print(f"Development Version")
|
||||
print("")
|
||||
@ -4,8 +4,11 @@ import FindMyIP
|
||||
import ipaddress
|
||||
import threading
|
||||
import requests
|
||||
import os
|
||||
|
||||
DEBUG = True if os.environ.get('DEBUG') is not None else False
|
||||
max_threads = 50
|
||||
port = 8000
|
||||
final = {}
|
||||
|
||||
|
||||
@ -15,41 +18,37 @@ def check_port(ip, port):
|
||||
# sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
|
||||
socket.setdefaulttimeout(2.0) # seconds (float)
|
||||
result = sock.connect_ex((ip, port))
|
||||
if result == 0:
|
||||
# print("Port is open")
|
||||
final[ip] = "OPEN"
|
||||
else:
|
||||
# print("Port is closed/filtered")
|
||||
final[ip] = "CLOSED"
|
||||
if result == 0: final[ip] = True
|
||||
else: final[ip] = False
|
||||
sock.close()
|
||||
|
||||
except:
|
||||
final[ip] = "EXCEPTION"
|
||||
pass
|
||||
|
||||
|
||||
port = 8000
|
||||
def scan_for_gateways():
|
||||
local_ip = FindMyIP.internal()
|
||||
local_ip = local_ip.split('.')[:-1]
|
||||
local_ip.append("0")
|
||||
local_ip = '.'.join(local_ip)
|
||||
|
||||
print(f"Scan on {local_ip}/24 for port {port}")
|
||||
print(f"Scan on {local_ip}/24 for port {port}") if DEBUG else {}
|
||||
|
||||
for ip in ipaddress.IPv4Network(f'{local_ip}/24'):
|
||||
threading.Thread(target=check_port, args=[str(ip), port]).start()
|
||||
# sleep(0.1)
|
||||
|
||||
# limit the number of threads.
|
||||
while threading.active_count() > max_threads:
|
||||
sleep(1)
|
||||
while threading.active_count() > max_threads: sleep(1)
|
||||
|
||||
sorted_ips = dict(sorted(final.items(), key=lambda item: tuple(map(int, item[0].split('.')))))
|
||||
|
||||
gateways = []
|
||||
for ip, state in sorted_ips.items():
|
||||
if state == "OPEN":
|
||||
if state:
|
||||
try:
|
||||
response = requests.get(f'http://{ip}:{port}/')
|
||||
response = requests.get(f'http://{ip}:{port}/api')
|
||||
# TODO: Check if the API is for real a gateway
|
||||
if response.status_code == 200:
|
||||
print(ip, state)
|
||||
print(response.text)
|
||||
print(ip, response.json()) if DEBUG else {}
|
||||
gateways.append([ip, response.json()])
|
||||
except:pass
|
||||
return gateways
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ import os
|
||||
from discovery import start_discovery
|
||||
from log_data import log_to_json
|
||||
from loop import start_loop
|
||||
from check_update import check_for_update
|
||||
from check_update import print_state
|
||||
|
||||
INTERVAL = 40
|
||||
TIMEOUT = 20
|
||||
@ -17,8 +19,13 @@ if DEBUG:
|
||||
print(f"timeout: {timeout}")
|
||||
print(f"DOCKER: {DOCKER}")
|
||||
print(f"DEBUG: {DEBUG}")
|
||||
print(f"VERSION: {os.getenv('VERSION')}")
|
||||
print("")
|
||||
|
||||
update_state = check_for_update()
|
||||
print_state(update_state)
|
||||
|
||||
|
||||
if DOCKER:
|
||||
print("Running in docker")
|
||||
|
||||
|
||||
@ -1 +1,15 @@
|
||||
# TODO
|
||||
import paho.mqtt.client as MQTT
|
||||
|
||||
topic_root = "/atc_mithermometer_gateway"
|
||||
|
||||
mqtt = MQTT.Client(mqtt.CallbackAPIVersion.VERSION2)
|
||||
mqtt.connect("192.168.178.140", 1883, 60)
|
||||
|
||||
|
||||
def publish_measurement(mac):
|
||||
topic = f"{topic_root}/measurements/{mac}"
|
||||
topic_temp = f"{topic}/temperature"
|
||||
topic_humid = f"{topic}/humidity"
|
||||
topic_battery = f"{topic}/battery"
|
||||
|
||||
mqtt.publish(topic_temp, )
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
TAG="latest"
|
||||
CONTAINER="dasmoorhuhn/atc-mithermometer-gateway"
|
||||
CONTAINER_NAME="ATC_MiThermometer_Gateway"
|
||||
VOLUME=data
|
||||
VOLUME=$(pwd)/data
|
||||
|
||||
BACKGROUND=""
|
||||
TIME_ZONE=""
|
||||
NAME=""
|
||||
INTERACTIVE=false
|
||||
BUILD=false
|
||||
API=false
|
||||
DEBUG=false
|
||||
MODE=1
|
||||
LOOP="0"
|
||||
TIMEOUT="0"
|
||||
|
||||
@ -20,6 +22,8 @@ HELP="USAGE: sh run_docker.sh [OPTIONS] \n
|
||||
[ -i | --interactive ] Start the container in interactive mode. That means, you can read the console output in real time and abort via STRG+C \n
|
||||
[ -a | --api ] Start with the API \n
|
||||
[ -v | --volume ] Set the volume, where the data from the gateway will be stored. Use relative path like /home/user/gateway/data \n
|
||||
[ -n | --name ] Set a custom name for this gateway \n
|
||||
[ -m2 | --mesh-gateway ] Set the mode to mesh gateway. This gateway is meant to expand the bluetooth radius. Default mode is main gateway \n
|
||||
[ -tz | --timezone ] Set the timezone. Default is Europe/Berlin \n
|
||||
[ -to | --timeout ] Set the timeout for the bluetooth scan. default is 20s \n
|
||||
[ -h | --help ] Get this dialog \n
|
||||
@ -73,6 +77,14 @@ docker_run() {
|
||||
COMMAND="$COMMAND --env API=$API"
|
||||
fi
|
||||
|
||||
if [ "$MODE" != 1 ]; then
|
||||
COMMAND="$COMMAND --env MODE=$MODE"
|
||||
fi
|
||||
|
||||
if [ "$NAME" != "" ]; then
|
||||
COMMAND="$COMMAND --env NAME=$NAME"
|
||||
fi
|
||||
|
||||
if [ "$DEBUG" = true ]; then
|
||||
COMMAND="$COMMAND --env DEBUG=$DEBUG"
|
||||
COMMAND="$COMMAND $CONTAINER:$TAG"
|
||||
@ -114,6 +126,15 @@ while [ "$1" != "" ]; do
|
||||
VOLUME=$1
|
||||
shift
|
||||
;;
|
||||
-n | --name )
|
||||
shift
|
||||
NAME=$1
|
||||
shift
|
||||
;;
|
||||
-m2 | --mesh-gateway)
|
||||
MODE=2
|
||||
shift
|
||||
;;
|
||||
-tz | --timezone )
|
||||
shift
|
||||
TIME_ZONE=$1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user