diff --git a/.gitignore b/.gitignore
index ed8ebf5..5078187 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,7 @@
-__pycache__
\ No newline at end of file
+__pycache__
+devices.yml
+history.*
+data/
+*.json
+*.iso
+*.cow
\ No newline at end of file
diff --git a/.idea/ATC_Sensor_Gateway.iml b/.idea/ATC_Sensor_Gateway.iml
index f571432..d0876a7 100644
--- a/.idea/ATC_Sensor_Gateway.iml
+++ b/.idea/ATC_Sensor_Gateway.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/git_toolbox_blame.xml b/.idea/git_toolbox_blame.xml
new file mode 100644
index 0000000..7dc1249
--- /dev/null
+++ b/.idea/git_toolbox_blame.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 5a5d9ae..12391f3 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/.media/demo.gif b/.media/demo.gif
new file mode 100644
index 0000000..5a550bc
Binary files /dev/null and b/.media/demo.gif differ
diff --git a/vector.py b/.media/vector.py
similarity index 100%
rename from vector.py
rename to .media/vector.py
diff --git a/Dockerfile b/Dockerfile
index a474eea..bf958e5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,16 +1,53 @@
-FROM python:3.12
+# This is the build stage for getting all python libs. Mainly it's for reducing the final image size because for building and installing the pips, many tools are needed which are just bloat to the final image.
+FROM python:3.12-alpine3.20 AS pip_build_stage
-WORKDIR = /src
+COPY ./python/requierements.txt /
-COPY python/src/ .
-COPY python/requierements.txt .
-COPY python/docker_entrypoint.sh /
+RUN apk add \
+ make \
+ git \
+ glib-dev \
+ gcc \
+ build-base \
+ freetype-dev \
+ libpng-dev \
+ openblas-dev \
+ libxml2-dev \
+ libxslt-dev
+# BluePy needs to be build here... idk why but pip install fails on alpine somehow
+RUN git clone https://github.com/IanHarvey/bluepy.git && \
+ cd bluepy && \
+ python3.12 setup.py build && \
+ python3.12 setup.py install && \
+ cd /
+
+# Normal pip install for pyyaml failed on RPI4, so I build it here
+RUN git clone https://github.com/yaml/pyyaml.git && \
+ cd pyyaml && \
+ python3.12 setup.py build && \
+ python3.12 setup.py install && \
+ cd /
+
+RUN pip3.12 install -r requierements.txt
+
+
+# This is the stage for the final image
+FROM python:3.12-alpine3.20
+
+WORKDIR /src
+COPY ./python/src/ .
+COPY ./python/docker_entrypoint.sh /
RUN mkdir data
+VOLUME /src/data
-RUN apt-get update && \
- apt-get install -y bluez sudo
+RUN apk add --no-cache sudo bluez tzdata
+ENV TZ=Europe/Berlin
+ENV DOCKER=true
+ENV API=false
-RUN pip3.12 install -r requierements.txt && rm -f requierements.txt
+# 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
+COPY --from=pip_build_stage /usr/local/bin /usr/local/bin
-ENTRYPOINT sh /docker_entrypoint.sh
\ No newline at end of file
+ENTRYPOINT sh /docker_entrypoint.sh
diff --git a/README.md b/README.md
index dd2f5bf..b90e968 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,31 @@
+
+* [ATC_MiThermometer_Gateway](#atc_mithermometer_gateway)
+ * [Roadmap](#roadmap)
+ * [Getting started](#getting-started)
+ * [Preconditions](#preconditions)
+ * [Shell Scripts](#shell-scripts)
+ * [Start Gateway](#start-gateway)
+ * [Build your own docker container](#build-your-own-docker-container)
+ * [MicroPython for MicroController](#micropython-for-microcontroller)
+* [Resources](#resources)
+
+
# ATC_MiThermometer_Gateway
Python gateway for the [custom firmware](https://github.com/atc1441/ATC_MiThermometer) for the [Xiaomi Thermometer LYWSD03MMC](https://www.mi.com/de/product/mi-temperature-and-humidity-monitor-2/).

-**Features:**
-- [DONE] Make in runnable in a docker container (because only cool people are using docker)
+## Roadmap
+
+**Done:**
+- Make in runnable in a docker container (because only cool people are using docker)
+- Make docker image smaller. I mean shiiit 1GB D: should be possible to be under 500MB (It's now around 100MB)
+- Implement a loop for fetching the data every X seconds
**TODOs:**
- [WIP] Can run on Raspberry Pi (3, 4, zero w) or any other Linux driven hardware which has BLE and WiFi support
- [WIP] Storing temperature, humidity and battery state as json in a text file
-- [WIP] Implement a loop for fetching the data every x minute
-- [WIP] Make discoveries async
-- [TODO] Make docker image smaller. I mean shiiit 1GB D: should be possible to be under 500MB
- [TODO] Make a microPython version for using the raspberry pico w or any other microcontroller with BLE and WiFi support
- [TODO] Collect data from multiple devices/gateways
- [TODO] Command line tool for managing the devices
@@ -24,41 +37,77 @@ Python gateway for the [custom firmware](https://github.com/atc1441/ATC_MiThermo
**Current State**
-
+
## Getting started
First of all, you need to flash the [custom firmware](https://github.com/atc1441/ATC_MiThermometer) on your LYWSD03MMC device. A step-by-step guid is in his youtube channel, the video is linked on his GitHub repo. It's straight forward and does not require any special hardware.
-## Run Gateway
-
-The libraries are needed to be installed as root, because the gateway itself needs to be executed as root, else it is not able to use the bluetooth adapter. In the future, I try to do it better. Also in the future it will be much easier to start and install.
+## Preconditions
Install `bluez`. It's needed for bluepy to communicate with the bluetooth adapter.
```bash
sudo apt-get install -y bluez
```
-Install PIP Libraries
-```bash
-sudo pip3 install -r python/requirements.txt
-```
+
+## Shell Scripts
+
+**build_docker.sh**
+
+| Arg | Meaning | Default |
+|---------------|----------------------------|---------------------------------------|
+| -t \| --tag | Set a tag for build | latest |
+| -i \| --image | Set a image name for build | dasmoorhuhn/atc-mithermometer-gateway |
+| -h \| --help | Get this help in the CLI | |
+
+**run_docker.sh**
+
+| Arg | Meaning | Default |
+|---------------------|------------------------------------------------------------------------------------------------------------------------|----------------------|
+| -d | Run in Backgrund | false |
+| -t \| --tag | Set a docker tag | latest |
+| -b \| --build | Build the image before running the container | false |
+| -l \| --loop | Start the gateway in looping mode | false (40s fallback) |
+| -i \| --interactive | Start the container in interactive mode. That means, you can read the console output in real time and abort via STRG+C | false |
+| -a \| --api | Start with the API | false |
+| -v \| --volume | Set the volume, where the data from the gateway will be stored. Use relative path like /home/user/gateway/data | data |
+| -tz \| --timezone | Set the timezone | Europe/Berlin |
+| -to \| --timeout | Set the timeout for the bluetooth scan | 20 |
+| -h \| --help | Get this dialog in CLI | |
+| --debug | Activate debug mode. Meant for fixing errors or development. | false |
+
+## Start Gateway
+
+For getting started, you need to download the `run_gateway.sh` or build the docker run commands by your own.
+
Run Gateway
```bash
-cd python/src
-sudo python3 main.py
+sh run_gateway.sh
```
-### Docker
-
-Build docker container
+Run Gateway with specified volume for persistence data, loop interval of 40 seconds and interactive mode
```bash
-docker-compose build
-```
-Run docker container. Killing the hosts bluetooth service is needed to access it from the docker container.
-```bash
-sudo sh run_docker.sh
+sh run_gateway.sh --volume /home/username/data --loop 40 --interactive
```
-### MicroPython for MicroController
+## Build your own docker container
+
+Build for your current platform (Without parameters, default values will be chosen)
+```bash
+sh build_docker.sh --image you/your-image-name you/your-image-name
+```
+
+For building the docker container for multiple platforms at once, you need to have [docker buildx](https://github.com/docker/buildx) installed. (Without parameters, default values will be chosen)
+```bash
+sh build_docker_multi_platforn.sh --platforms linux/amd64,linux/arm64 --image you/your-image-name you/your-image-name
+```
+
+
+## MicroPython for MicroController
Coming when I develop it...
+
+
+# Resources
+- https://pythonspeed.com/articles/alpine-docker-python this article is nuts :D
+- https://docs.docker.com/build/building/multi-stage/
\ No newline at end of file
diff --git a/bluetooth tools/search_for_ble.py b/bluetooth tools/search_for_ble.py
deleted file mode 100644
index e69de29..0000000
diff --git a/bluetooth tools/search_for_blc.py b/bluetooth_tools/search_for_blc.py
similarity index 100%
rename from bluetooth tools/search_for_blc.py
rename to bluetooth_tools/search_for_blc.py
diff --git a/bluetooth_tools/search_for_ble.py b/bluetooth_tools/search_for_ble.py
new file mode 100644
index 0000000..8703b7a
--- /dev/null
+++ b/bluetooth_tools/search_for_ble.py
@@ -0,0 +1,8 @@
+from bluepy.btle import Scanner
+scanner = Scanner(0)
+print('Start scan...')
+devices = scanner.scan(10)
+for device in devices:
+ print('address : %s' % device.addr.upper())
+ print(device.getScanData())
+ print('')
diff --git a/build_docker.sh b/build_docker.sh
new file mode 100644
index 0000000..94981ed
--- /dev/null
+++ b/build_docker.sh
@@ -0,0 +1,40 @@
+TAG=latest
+IMAGE=dasmoorhuhn/atc-mithermometer-gateway
+HELP="USAGE: sh build_docker.sh \n
+[ -t | --tag ] Select a tag for building. Default is latest \n
+[ -i | --image ] Select image tag for building. Default is dasmoorhuhn/atc-mithermometer-gateway \n
+[ -h | --help ] Get this dialog"
+
+docker version > /dev/null 2>&1
+if [ "$?" != 0 ]; then
+ echo Missing docker. Please install docker and try build again.
+ exit 1
+fi
+
+docker_build(){
+ docker build --tag $IMAGE:$TAG .
+}
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -t | --tag )
+ shift
+ TAG=$1
+ shift
+ ;;
+ -i | --image )
+ shift
+ IMAGE=$1
+ shift
+ ;;
+ -h | --help )
+ echo $HELP
+ exit
+ ;;
+ * )
+ echo $HELP
+ exit 1
+ esac
+done
+
+docker_build
\ No newline at end of file
diff --git a/build_docker_multi_platforn.sh b/build_docker_multi_platforn.sh
new file mode 100644
index 0000000..b16cbd8
--- /dev/null
+++ b/build_docker_multi_platforn.sh
@@ -0,0 +1,55 @@
+TAG=latest
+IMAGE=dasmoorhuhn/atc-mithermometer-gateway
+PLATFORMS=linux/amd64,linux/arm64,linux/arm
+HELP="USAGE: sh build_docker.sh \n
+[ -t | --tag ] Select a tag for building. Default: latest \n
+[ -i | --image ] Select image tag for building. Default: dasmoorhuhn/atc-mithermometer-gateway \n
+[ -p | --platforms ] Select the platforms, for which the image should build. Default: linux/amd64,linux/arm64,linux/arm \n
+[ -h | --help ] Get this dialog"
+
+docker buildx version
+if [ "$?" != 0 ]; then
+ echo Missing docker buildx. Please install docker buildx from https://github.com/docker/buildx and try build again.
+ exit 1
+fi
+
+create_builder() {
+ docker buildx create --name builder
+ docker buildx use builder
+}
+
+build_docker() {
+ create_builder
+ docker login
+ docker buildx build --tag $IMAGE:$TAG --platform=$PLATFORMS --push .
+}
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -t | --tag )
+ shift
+ TAG=$1
+ shift
+ ;;
+ -i | --image )
+ shift
+ IMAGE=$1
+ shift
+ ;;
+ -p | --platforms )
+ shift
+ PLATFORMS=$1
+ shift
+ ;;
+ -h | --help )
+ echo $HELP
+ exit
+ ;;
+ * )
+ echo $HELP
+ echo $1
+ exit 1
+ esac
+done
+
+build_docker
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 2f47bf8..0a3b958 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,6 +2,6 @@ version: '3'
services:
atc_mithermometer_gateway:
- image: atc-mithermometer-gateway:develop
- container_name: ATC_MiThermometer_Gateway
+ image: dasmoorhuhn/atc-mithermometer-gateway:develop-alpine
+ container_name: ATC_MiThermometer_Gateway_Build
build: .
\ No newline at end of file
diff --git a/python/docker_entrypoint.sh b/python/docker_entrypoint.sh
index b4a9ea2..aeb780b 100644
--- a/python/docker_entrypoint.sh
+++ b/python/docker_entrypoint.sh
@@ -1,8 +1,10 @@
-#!/bin/bash
+#!/bin/sh
-service dbus start
-bluetoothd &
+env > .env
-/bin/bash
+if [ "$API" = true ]; then
+ python3.12 api_endpoints.py &
+ sleep 1
+fi
-sudo python3 main.py
+python3.12 main.py
\ No newline at end of file
diff --git a/python/requierements.txt b/python/requierements.txt
index d99d203..b7ed443 100644
--- a/python/requierements.txt
+++ b/python/requierements.txt
@@ -1,2 +1,6 @@
bluepy
-pyyaml
\ No newline at end of file
+pyyaml
+bs4
+requests
+flask
+flask_cors
\ No newline at end of file
diff --git a/python/src/api_endpoints.py b/python/src/api_endpoints.py
new file mode 100644
index 0000000..6448f3b
--- /dev/null
+++ b/python/src/api_endpoints.py
@@ -0,0 +1,39 @@
+import os
+from flask import Flask
+from flask import jsonify
+from flask import send_from_directory
+from flask_cors import CORS
+from flask_cors import cross_origin
+
+
+class API:
+ """
+ API endpoints
+ """
+
+ def __init__(self):
+ self.app = Flask(import_name='backend', static_folder='/data', static_url_path='')
+ CORS(self.app)
+ self.app.config['CORS_HEADERS'] = 'Content-Type'
+
+ # --------Static Routes-------
+ @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/')
+ @cross_origin()
+ def serve_json(path):
+ workdir, filename = os.path.split(os.path.abspath(__file__))
+ return send_from_directory(f'{workdir}/data', path)
+
+
+api = API()
+api.app.run(host='0.0.0.0', port=8000)
diff --git a/python/src/chart.html b/python/src/chart.html
new file mode 100644
index 0000000..1f6bc71
--- /dev/null
+++ b/python/src/chart.html
@@ -0,0 +1,128 @@
+
+
+
+
+
+ Messdaten Charts
+
+
+
+
+
+
Messdaten Charts
+
+
+
+
+
diff --git a/python/src/data_class.py b/python/src/data_class.py
index 5a05404..490c059 100644
--- a/python/src/data_class.py
+++ b/python/src/data_class.py
@@ -1,23 +1,39 @@
+from datetime import datetime
+
+
class Data:
- def __init__(self, data:dict):
- self.timestamp = data['timestamp']
- self.mac = data['mac']
- self.temperature = data['temperature']
- self.humidity = data['humidity']
- self.battery_percent = data['battery_percent']
- self.battery_volt = data['battery_volt']
- self.count = data['count']
+ def __init__(self, data):
+ self.timestamp = None
+ self.mac = None
+ self.temperature = None
+ self.humidity = None
+ self.battery_percent = None
+ self.battery_volt = None
+ self.count = None
+ self.parse_data(data)
+
+ def parse_data(self, val):
+ data_bytes = [int(val[i:i + 2], 16) for i in range(0, len(val), 2)]
+ if data_bytes[8] > 127: data_bytes[8] -= 256
+
+ self.timestamp = datetime.now().astimezone().replace(microsecond=0).isoformat()
+ self.mac = ":".join(["{:02X}".format(data_bytes[i]) for i in range(2, 8)]).upper()
+ self.temperature = (data_bytes[8] * 256 + data_bytes[9]) / 10
+ self.humidity = data_bytes[10]
+ self.battery_percent = data_bytes[11]
+ self.battery_volt = (data_bytes[12] * 256 + data_bytes[13]) / 1000
+ self.count = data_bytes[14]
def print_data(self):
print(self.to_json())
def to_json(self):
return {
- 'timestamp': self.timestamp,
- 'mac': self.mac,
- 'temperature': self.temperature,
- 'humidity': self.humidity,
- 'battery_percent': self.battery_percent,
- 'battery_volt': self.battery_volt,
- 'count': self.count,
+ "timestamp": self.timestamp,
+ "mac": self.mac,
+ "temperature": self.temperature,
+ "humidity": self.humidity,
+ "battery_percent": self.battery_percent,
+ "battery_volt": self.battery_volt,
+ "count": self.count,
}
diff --git a/python/src/devices.example.yml b/python/src/devices.example.yml
new file mode 100644
index 0000000..30041aa
--- /dev/null
+++ b/python/src/devices.example.yml
@@ -0,0 +1,8 @@
+devices:
+ - mac: A4:C1:38:00:00:00
+ name: "my_device_1"
+ room: "my_room_1"
+
+ - mac: A4:C1:38:00:00:00
+ name: "my_device_2"
+ room: "my_room_2"
diff --git a/python/src/devices.py b/python/src/devices.py
index 31e56f0..603b611 100644
--- a/python/src/devices.py
+++ b/python/src/devices.py
@@ -1,2 +1,31 @@
import yaml
+import os
+workdir, filename = os.path.split(os.path.abspath(__file__))
+config_file = f"{workdir}{os.sep}devices.yml"
+
+
+class Device:
+ def __init__(self, data):
+ self.mac = data['mac']
+ self.name = data['name']
+ self.room = data['room']
+
+ def to_json(self):
+ return {
+ "mac": self.mac,
+ "name": self.name,
+ "room": self.room
+ }
+
+
+def get_devices():
+ devices_list = []
+ with open(file=config_file, mode='r') as file:
+ devices = yaml.safe_load(file)
+ for device in devices['devices']: devices_list.append(Device(data=device))
+ return devices_list
+
+
+def get_device(dev):
+ return next((d for d in get_devices() if d.mac == dev.addr.upper()), None)
diff --git a/python/src/devices.yml b/python/src/devices.yml
deleted file mode 100644
index ce23077..0000000
--- a/python/src/devices.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-devices:
- - "A4:C1:38:83:05:E8":
- name: "My Sensor"
- room: "My Room"
-
- - "...":
- name: "..."
- room: "..."
diff --git a/python/src/discovery.py b/python/src/discovery.py
index d01ff82..7d8b17f 100644
--- a/python/src/discovery.py
+++ b/python/src/discovery.py
@@ -1,10 +1,8 @@
-import asyncio
-from threading import Thread
from bluepy.btle import DefaultDelegate
from bluepy.btle import Scanner
-from datetime import datetime
from data_class import Data
+from devices import get_device
# This is the list, where the responses will be stored from the `handleDiscovery`
devices = []
@@ -19,9 +17,11 @@ class ScanDelegate(DefaultDelegate):
for (sdid, desc, val) in dev.getScanData():
if self.is_temperature(sdid, val):
- data_obj = Data(self.parse_data(val))
+ data_obj = Data(val)
+
if self.is_atc_device(dev, data_obj):
- devices.append([dev, data_obj])
+ device_from_config = get_device(dev)
+ devices.append([dev, data_obj, device_from_config])
@staticmethod
def is_temperature(sdid, val):
@@ -35,31 +35,26 @@ class ScanDelegate(DefaultDelegate):
if 'A4:C1:38' not in dev.addr.upper(): return False
for device in devices:
if str(device[0].addr) == str(dev.addr): return False
- print("Device %s (%s), RSSI=%d dB" % (dev.addr.upper(), dev.addrType, dev.rssi))
+
+ device_from_config = get_device(dev)
+
+ try:print(f"Device: {dev.addr.upper()} ({dev.addrType}), RSSI: {dev.rssi}dB, Room: {device_from_config.room}")
+ except:print(f"Device: {dev.addr.upper()} ({dev.addrType}), RSSI: {dev.rssi}dB, Room: ?")
print(f'\tTemp: {data_obj.temperature}°C, Humid: {data_obj.humidity}%, Batt: {data_obj.battery_percent}%\n')
return True
- @staticmethod
- def parse_data(val):
- bytes = [int(val[i:i + 2], 16) for i in range(0, len(val), 2)]
- if bytes[8] > 127:
- bytes[8] -= 256
- return {
- 'timestamp': datetime.now().astimezone().replace(microsecond=0).isoformat(),
- 'mac': ":".join(["{:02X}".format(bytes[i]) for i in range(2, 8)]),
- 'temperature': (bytes[8] * 256 + bytes[9]) / 10,
- 'humidity': bytes[10],
- 'battery_percent': bytes[11],
- 'battery_volt': (bytes[12] * 256 + bytes[13]) / 1000,
- 'count': bytes[14],
- }
+
+def cleanup():
+ global devices
+ devices = []
-def start_discovery(timeout=10.0):
+def start_discovery(timeout=20):
+ cleanup()
global devices
print(f'Start discovery with timout {timeout}s...')
scanner = Scanner().withDelegate(ScanDelegate())
- scanner.scan(timeout=timeout, passive=True)
+ scanner.scan(timeout=timeout, passive=False)
return devices
diff --git a/python/src/log_data.py b/python/src/log_data.py
new file mode 100644
index 0000000..179a296
--- /dev/null
+++ b/python/src/log_data.py
@@ -0,0 +1,49 @@
+import os
+import sys
+import json
+from data_class import Data
+from devices import Device
+
+DEBUG = True if os.getenv('DEBUG') == 'true' else False
+
+
+def log_to_json(devices):
+ workdir, filename = os.path.split(os.path.abspath(__file__))
+
+ for device in devices:
+ dev, data_obj, from_config = device
+ data_obj: Data
+ from_config: Device
+ file_name = f'{workdir}/data/{str(data_obj.mac).replace(":", "-")}.json'
+ print(file_name) if DEBUG else {}
+
+ try:
+ with open(file_name, 'r') as file: data = json.load(file)
+ except:
+ with open(file_name, 'w') as file: file.write("[]")
+ data = []
+
+ measurements = {
+ "timestamp": data_obj.timestamp,
+ "temperature": data_obj.temperature,
+ "humidity": data_obj.humidity,
+ "battery_percent": data_obj.battery_percent,
+ "battery_volt": data_obj.battery_volt,
+ "rssi": dev.rssi,
+ "name": from_config.name,
+ "room": from_config.room
+ }
+ data.append(measurements)
+
+ print(measurements) if DEBUG else {}
+
+ with open(file_name, 'w') as file: file.write(json.dumps(data, indent=2))
+
+
+def log_to_mongodb(data):
+ pass
+
+
+def log_to_mqtt(data):
+ pass
+
diff --git a/python/src/loop.py b/python/src/loop.py
index e50ca33..561a624 100644
--- a/python/src/loop.py
+++ b/python/src/loop.py
@@ -1,9 +1,11 @@
from time import sleep
-
+from log_data import log_to_json
from discovery import start_discovery
-def start_loop(interval=60):
+def start_loop(interval=40, timeout=20):
+ print(f"Starting loop with interval {interval}s")
while True:
- start_discovery()
+ devices = start_discovery(timeout=timeout)
+ log_to_json(devices)
sleep(interval)
diff --git a/python/src/main.py b/python/src/main.py
index 8f018a1..2fd502a 100644
--- a/python/src/main.py
+++ b/python/src/main.py
@@ -1,19 +1,35 @@
+import os
from discovery import start_discovery
-from data_class import Data
+from log_data import log_to_json
+from loop import start_loop
-devices = start_discovery()
+INTERVAL = 40
+TIMEOUT = 20
+DOCKER = True if os.getenv('DOCKER') == 'true' else False
+DEBUG = True if os.getenv('DEBUG') == 'true' else False
+interval = os.getenv('LOOP')
+timeout = os.getenv('TIMEOUT')
+if DEBUG:
+ print(f"INTERVAL: {INTERVAL}")
+ print(f"TIMEOUT: {TIMEOUT}")
+ print(f"interval: {interval}")
+ print(f"timeout: {timeout}")
+ print(f"DOCKER: {DOCKER}")
+ print(f"DEBUG: {DEBUG}")
+ print("")
-if len(devices) > 0:
- for device_list in devices:
- data = device_list[1]
- device = device_list[0]
+if DOCKER:
+ print("Running in docker")
- data:Data
- # print(f'Temp: {data.temperature}°C, Humid: {data.humidity}%, Batt: {data.battery_percent}%')
+ try:INTERVAL = int(interval)
+ except:pass
+ try:TIMEOUT = int(timeout)
+ except:pass
+
+ if interval is None: log_to_json(start_discovery(timeout=TIMEOUT))
+ else:start_loop(INTERVAL, TIMEOUT)
else:
- print('No devices found')
-
-
+ start_loop(interval=40)
diff --git a/python/src/mqtt.py b/python/src/mqtt.py
new file mode 100644
index 0000000..4640904
--- /dev/null
+++ b/python/src/mqtt.py
@@ -0,0 +1 @@
+# TODO
diff --git a/run_docker.sh b/run_docker.sh
deleted file mode 100644
index 20f4df3..0000000
--- a/run_docker.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-sudo killall -9 bluetoothd
-docker stop atc-mithermometer-gateway:develop
-docker run \
- --cap-add=SYS_ADMIN \
- --cap-add=NET_ADMIN \
- --net=host \
- -v /var/run/dbus/:/var/run/dbus/ \
- -v /path/to/data:/src/data \
- atc-mithermometer-gateway:develop
\ No newline at end of file
diff --git a/run_gateway.sh b/run_gateway.sh
new file mode 100644
index 0000000..4e7f3ff
--- /dev/null
+++ b/run_gateway.sh
@@ -0,0 +1,159 @@
+TAG="latest"
+CONTAINER="dasmoorhuhn/atc-mithermometer-gateway"
+CONTAINER_NAME="ATC_MiThermometer_Gateway"
+VOLUME=data
+
+BACKGROUND=""
+TIME_ZONE=""
+INTERACTIVE=false
+BUILD=false
+API=false
+DEBUG=false
+LOOP="0"
+TIMEOUT="0"
+
+HELP="USAGE: sh run_docker.sh [OPTIONS] \n
+[ -d ] Run in Backgrund \n
+[ -t | --tag ] Set a docker tag. Default: latest \n
+[ -b | --build ] Build the image before running the container \n
+[ -l | --loop ] Start the gateway in looping mode. e.g.: --loop 40 will set the interval of the loop to 40s. Default is single run mode \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
+[ -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
+[ --debug ] Set into debug mode"
+
+docker version > /dev/null 2>&1
+if [ "$?" != 0 ]; then
+ echo Missing docker. Please install docker and try build again.
+ exit 1
+fi
+
+docker_run() {
+ sudo killall -9 bluetoothd > /dev/null 2>&1
+ echo Killing old container...
+ docker stop $CONTAINER_NAME > /dev/null 2>&1
+ docker container rm $CONTAINER_NAME > /dev/null 2>&1
+
+ COMMAND="docker run $BACKGROUND"
+ COMMAND="$COMMAND --cap-add=SYS_ADMIN"
+ COMMAND="$COMMAND --cap-add=NET_ADMIN"
+ COMMAND="$COMMAND --net=host"
+ COMMAND="$COMMAND --name=$CONTAINER_NAME"
+ COMMAND="$COMMAND --restart=on-failure"
+ COMMAND="$COMMAND --volume=/var/run/dbus/:/var/run/dbus/"
+ COMMAND="$COMMAND --volume=$VOLUME:/src/data"
+
+ if [ "$INTERACTIVE" = true ]; then
+ COMMAND="$COMMAND --interactive"
+ COMMAND="$COMMAND --tty"
+ COMMAND="$COMMAND --attach=stdout"
+ COMMAND="$COMMAND --attach=stdin"
+ fi
+
+ if [ "$LOOP" != "0" ]; then
+ COMMAND="$COMMAND --env LOOP=$LOOP"
+ fi
+
+ if [ "$TIMEOUT" != "0" ]; then
+ COMMAND="$COMMAND --env TIMEOUT=$TIMEOUT"
+ fi
+
+ if [ "$BUILD" = true ]; then
+ sh build_docker.sh --tag $TAG
+ fi
+
+ if [ "$TIME_ZONE" != "" ]; then
+ COMMAND="$COMMAND --env TZ=$TIME_ZONE"
+ fi
+
+ if [ "$API" != false ]; then
+ COMMAND="$COMMAND --env API=$API"
+ fi
+
+ if [ "$DEBUG" = true ]; then
+ COMMAND="$COMMAND --env DEBUG=$DEBUG"
+ COMMAND="$COMMAND $CONTAINER:$TAG"
+ echo
+ echo $COMMAND
+ echo
+ echo DEBUG MODE
+ else
+ COMMAND="$COMMAND $CONTAINER:$TAG"
+ fi
+
+ echo Start container...
+ echo
+ $COMMAND
+
+ docker container rm $CONTAINER_NAME > /dev/null 2>&1
+}
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -d )
+ BACKGROUND="-d"
+ shift
+ ;;
+ --debug )
+ DEBUG=true
+ shift
+ ;;
+ -a | --api)
+ API=true
+ shift
+ ;;
+ -b | --build )
+ BUILD=true
+ shift
+ ;;
+ -v | --volume )
+ shift
+ VOLUME=$1
+ shift
+ ;;
+ -tz | --timezone )
+ shift
+ TIME_ZONE=$1
+ shift
+ ;;
+ -to | --timeout )
+ shift
+ TIMEOUT=$1
+ shift
+ ;;
+ -t | --tag )
+ shift
+ TAG=$1
+ shift
+ ;;
+ -l | --loop )
+ shift
+ firstchar=`echo $1 | cut -c1-1`
+ if [ "$firstchar" = "-" ]; then
+ LOOP=0
+ elif [ "$firstchar" = "" ]; then
+ LOOP=0
+ else
+ LOOP=$1
+ shift
+ fi
+ ;;
+ -i | --interactive )
+ INTERACTIVE=true
+ shift
+ ;;
+ -h | --help )
+ echo $HELP
+ exit
+ ;;
+ * )
+ echo $HELP
+ exit 1
+ esac
+done
+
+docker_run
+
diff --git a/stop_gateway.sh b/stop_gateway.sh
new file mode 100644
index 0000000..120d2ca
--- /dev/null
+++ b/stop_gateway.sh
@@ -0,0 +1,3 @@
+echo Stopping container gracefully...
+docker stop ATC_MiThermometer_Gateway
+echo Done
\ No newline at end of file