Merge branch 'develop' into 'main'

Develop

See merge request DasMoorhuhn/atc_mithermometer_gateway!2
This commit is contained in:
DasMoorhuhn 2024-07-03 00:08:56 +00:00
commit 303410d639
30 changed files with 747 additions and 112 deletions

8
.gitignore vendored
View File

@ -1 +1,7 @@
__pycache__
__pycache__
devices.yml
history.*
data/
*.json
*.iso
*.cow

View File

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/git_toolbox_blame.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

2
.idea/misc.xml generated
View File

@ -3,5 +3,5 @@
<component name="Black">
<option name="sdkName" value="Python 3.12 (autopicture-v3)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>

BIN
.media/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -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
ENTRYPOINT sh /docker_entrypoint.sh

View File

@ -1,18 +1,31 @@
<!-- TOC -->
* [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)
<!-- TOC -->
# 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/

View File

@ -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('')

40
build_docker.sh Normal file
View File

@ -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

View File

@ -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

View File

@ -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: .

View File

@ -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

View File

@ -1,2 +1,6 @@
bluepy
pyyaml
pyyaml
bs4
requests
flask
flask_cors

View File

@ -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/<path:path>')
@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)

128
python/src/chart.html Normal file
View File

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Messdaten Charts</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.chartContainer {
width: 100%;
max-width: 800px;
height: 500px;
margin-bottom: 40px;
}
canvas {
width: 100% !important;
height: 100% !important;
}
</style>
</head>
<body>
<h1>Messdaten Charts</h1>
<div id="charts"></div>
<script>
async function fetchJSONFiles() {
try {
const response = await fetch('http://localhost:8000/json');
const jsonFiles = await response.json();
return jsonFiles;
} catch (error) {
console.error('Fehler beim Abrufen der JSON-Dateien:', error);
return [];
}
}
async function fetchData(filename) {
try {
const response = await fetch(`http://localhost:8000/json/${filename}`);
const data = await response.json();
return data;
} catch (error) {
console.error(`Fehler beim Abrufen der Datei ${filename}:`, error);
return [];
}
}
function parseChartData(data) {
const timestamps = data.map(entry => new Date(entry.timestamp));
const temperatures = data.map(entry => entry.temperature);
const humidity = data.map(entry => entry.humidity);
const name = data.length > 0 ? data[0].name : 'Unbekannt';
const room = data.length > 0 ? data[0].room : 'Unbekannt';
return { timestamps, temperatures, humidity, name, room };
}
async function renderCharts() {
const jsonFiles = await fetchJSONFiles();
const chartsContainer = document.getElementById('charts');
for (const file of jsonFiles) {
const data = await fetchData(file);
const { timestamps, temperatures, humidity, name, room } = parseChartData(data);
const chartContainer = document.createElement('div');
chartContainer.className = 'chartContainer';
chartContainer.innerHTML = `<h2>Gerät: ${name}, Raum: ${room}</h2><canvas></canvas>`;
chartsContainer.appendChild(chartContainer);
const ctx = chartContainer.querySelector('canvas').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: timestamps,
datasets: [
{
label: 'Temperatur (°C)',
data: temperatures,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
fill: false,
},
{
label: 'Luftfeuchtigkeit (%)',
data: humidity,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
fill: false,
}
]
},
options: {
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'minute'
}
},
y: {
beginAtZero: true
}
},
plugins: {
title: {
display: true,
text: `Gerät: ${name}, Raum: ${room}`
}
}
}
});
}
}
renderCharts();
</script>
</body>
</html>

View File

@ -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,
}

View File

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

View File

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

View File

@ -1,8 +0,0 @@
devices:
- "A4:C1:38:83:05:E8":
name: "My Sensor"
room: "My Room"
- "...":
name: "..."
room: "..."

View File

@ -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

49
python/src/log_data.py Normal file
View File

@ -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

View File

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

View File

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

1
python/src/mqtt.py Normal file
View File

@ -0,0 +1 @@
# TODO

View File

@ -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

159
run_gateway.sh Normal file
View File

@ -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

3
stop_gateway.sh Normal file
View File

@ -0,0 +1,3 @@
echo Stopping container gracefully...
docker stop ATC_MiThermometer_Gateway
echo Done