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/). ![](.media/41N1IH9jwoL._AC_SL1024_.jpg) -**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** -![](.media/demo_001.gif) +![](.media/demo.gif) ## 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