├── .env ├── examples ├── theengsgateway.service ├── docker-compose.yml └── README.md ├── Dockerfile ├── .github └── workflows │ ├── build_docker_on_commit.yml │ └── build_docker.yml ├── README.md └── chroot └── opt └── venv └── start.sh /.env: -------------------------------------------------------------------------------- 1 | VERSION=1.5.0.2 2 | NON_INTERACTIVE=false 3 | BUILDX=true 4 | BUILDX_PURGE=false 5 | -------------------------------------------------------------------------------- /examples/theengsgateway.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=TheengsGateway BLE Gateway Service 3 | After=network.target 4 | 5 | [Service] 6 | WorkingDirectory=/opt/theengsgateway 7 | ExecStartPre=docker compose down 8 | ExecStartPre=docker compose rm -f 9 | ExecStartPre=docker compose pull 10 | ExecStart=docker compose up 11 | ExecStop=docker compose down 12 | Restart=on-failure 13 | RestartSec=30s 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | SHELL ["/bin/bash", "-ec"] 4 | 5 | RUN apt update && apt install --no-install-recommends -y bluez build-essential 6 | RUN python3 -m venv /opt/venv && \ 7 | source /opt/venv/bin/activate && \ 8 | pip install --upgrade pip setuptools==70.0.0 && \ 9 | pip install --upgrade --extra-index-url=https://www.piwheels.org/simple pip TheengsGateway==1.5.0 10 | 11 | COPY chroot / 12 | CMD source /opt/venv/bin/activate && exec /opt/venv/start.sh 13 | -------------------------------------------------------------------------------- /.github/workflows/build_docker_on_commit.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | IMAGE_TAG: "theengs/gateway:test" 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up QEMU 18 | uses: docker/setup-qemu-action@v3 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v3 21 | - name: Build multi-arch Docker image 22 | uses: docker/build-push-action@v6 23 | with: 24 | context: . 25 | platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6 26 | push: false 27 | outputs: "type=tar,dest=image.tar" 28 | - name: Upload Docker image artifact 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: docker-image 32 | path: image.tar 33 | retention-days: 1 -------------------------------------------------------------------------------- /.github/workflows/build_docker.yml: -------------------------------------------------------------------------------- 1 | name: docker push 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | env: 9 | TEST_TAG: theengs/gateway:test 10 | 11 | jobs: 12 | docker: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - 16 | name: Checkout 17 | uses: actions/checkout@v4 18 | - 19 | name: Set up QEMU 20 | uses: docker/setup-qemu-action@v3 21 | - 22 | name: Set up Docker Buildx 23 | uses: docker/setup-buildx-action@v3 24 | - 25 | name: Login to DockerHub 26 | uses: docker/login-action@v3 27 | with: 28 | username: ${{ secrets.DOCKERHUB_USERNAME }} 29 | password: ${{ secrets.DOCKERHUB_TOKEN }} 30 | - 31 | name: Build and push 32 | uses: docker/build-push-action@v6 33 | with: 34 | context: . 35 | platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/arm/v6 36 | push: true 37 | tags: | 38 | theengs/gateway:${{ github.event.release.tag_name }} 39 | theengs/gateway:latest -------------------------------------------------------------------------------- /examples/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | theengsgateway: 3 | image: theengs/gateway:latest 4 | network_mode: host 5 | environment: 6 | MQTT_HOST: 192.168.0.200 7 | MQTT_USERNAME: username 8 | MQTT_PASSWORD: ubersecretpassword 9 | MQTT_PUB_TOPIC: home/TheengsGateway/BTtoMQTT 10 | MQTT_SUB_TOPIC: home/+/BTtoMQTT/undecoded 11 | MQTT_PRE_TOPIC: home/presence/TheengsGateway 12 | PRESENCE: false 13 | GENERAL_PRESENCE: false 14 | PUBLISH_ALL: true 15 | PUBLISH_ADVDATA: false 16 | TIME_FORMAT: false 17 | TIME_SYNC: "[]" 18 | TIME_BETWEEN: 60 19 | TRACKER_TIMEOUT: 120 20 | SCAN_TIME: 5 21 | LOG_LEVEL: DEBUG 22 | DISCOVERY: true 23 | HASS_DISCOVERY: true 24 | DISCOVERY_TOPIC: homeassistant 25 | DISCOVERY_DEVICE_NAME: TheengsGateway 26 | DISCOVERY_FILTER: "[IBEACON]" 27 | SCANNING_MODE: active 28 | ADAPTER: hci0 29 | BINDKEYS: "{}" 30 | ENABLE_TLS: false 31 | ENABLE_WEBSOCKET: false 32 | BLE: true 33 | IDENTITIES: "{}" 34 | WHITELIST: "[]" 35 | BLACKLIST: "[]" 36 | 37 | volumes: 38 | - /var/run/dbus:/var/run/dbus 39 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Install as Systemd Service 2 | 3 | - Create directory /opt/theengsgateway 4 | - Copy docker-compose.yml from this directory to /opt/theengsgateway 5 | - Make nececarry changes to docker-compose.yml, make sure `image` matches the architecture where you want to install TheengsGateway 6 | - Copy theengsgateway.service from this directory to /etc/systemd/system/theengsgateway.service 7 | - Make changes to WorkingDirectory in theengsgateway.service if you haven't copied docker-compose.yml to /opt/theengsgateway, use other directory instead 8 | - Run `systemctl daemon-reload` 9 | - Run systemctl start theengsgateway to start the service 10 | - Run journalctl -xefu theengsgateway to tail logs 11 | - Run journalctl -xeu theengsgateway to view snapshot of logs 12 | - Run systemctl stop theengsgateway to stop the service 13 | - Run systemctl status theengsgateway to check the status of a service 14 | 15 | ## Install as Systemd Service on LibreElec 16 | - Create directory /storage/.kodi/userdata/custom/dockerfiles/theengsgateway 17 | - Copy docker-compose.yml from this directory to /storage/.kodi/userdata/custom/dockerfiles/theengsgateway 18 | - Make nececarry changes to docker-compose.yml, make sure `image` matches the architecture where you want to install TheengsGateway 19 | - Copy theengsgateway.service from this directory to /storage/.config/system.d/theengsgateway.service 20 | - Make changes to WorkingDirectory in theengsgateway.service to point to /storage/.config/system.d/theengsgateway.service 21 | - Run `systemctl daemon-reload` 22 | - Run systemctl start theengsgateway to start the service 23 | - Run journalctl -xefu theengsgateway to tail logs 24 | - Run journalctl -xeu theengsgateway to view snapshot of logs 25 | - Run systemctl stop theengsgateway to stop the service 26 | - Run systemctl status theengsgateway to check the status of a service 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Theengs Gateway - Docker Version 2 | 3 | | Arch | Docker Image | 4 | | ----------------- | ---------------------------------------- | 5 | | ![aarch64-shield] | theengs/gateway-arm64:latest | 6 | | ![amd64-shield] | theengs/gateway-amd64:latest | 7 | | ![armv6-shield] | theengs/gateway-arm-v6:latest | 8 | | ![armv7-shield] | theengs/gateway-arm-v7:latest | 9 | | ![i386-shield] | theengs/gateway-i386:latest | 10 | 11 | ## So why the Docker Version? 12 | It happened just so that in my home, somehow Raspberry Pi 3 (where my Home Assistance Instance is hosted) Integrated Bluetooth adapter died and all my BLE sensors are now unavailable. Theengs Gateway seemed like a perfect thing to install on my Raspberry Pi 4 which is used as a media server. 13 | However, this is where the problem happened. 14 | I have LibreElec installed on RPi 4, therefore i could not use any of methods above to install Theengs Gateway, because: 15 | - pip was not installed, installing it on LibreElec would not keep it there since it uses overlay image. On next boot it would be overwritten 16 | - snap is not part of LibreElec and could not be installed 17 | - I want to keep RPi 4 as a media server, so overwriting it with Home Assistant installation is not an option 18 | 19 | ## Birth of Docker Version 20 | LibreElec natively comes with Docker. So building a docker image and running it inside LibreElec is a great way to run Theengs Gateway there. 21 | Also, this may help other users who prefer to use Docker to install Theengs Gateway. 22 | 23 | ## Usage 24 | Here are two ways to run Theengs Gateway in Docker. 25 | 26 | ### docker-compose.yml 27 | You can write a docker-compose.yml and use docker compose plugin to run. This is a preferred method as it's more humanly readable. Following docker-compose.yml can be used: 28 | ``` 29 | # docker-compose.yml 30 | version: '3.1' 31 | 32 | services: 33 | theengsgateway: 34 | image: theengs/gateway-ARCH:latest 35 | network_mode: host 36 | environment: 37 | MQTT_HOST: 38 | MQTT_USERNAME: 39 | MQTT_PASSWORD: 40 | MQTT_PUB_TOPIC: home/TheengsGateway/BTtoMQTT 41 | MQTT_SUB_TOPIC: home/+/BTtoMQTT/undecoded 42 | MQTT_PRE_TOPIC: home/presence/TheengsGateway 43 | PRESENCE: false 44 | PUBLISH_ALL: true 45 | PUBLISH_ADVDATA: false 46 | TIME_BETWEEN: 60 47 | SCAN_TIME: 60 48 | LOG_LEVEL: DEBUG 49 | DISCOVERY: true 50 | HASS_DISCOVERY: true 51 | DISCOVERY_TOPIC: homeassistant 52 | DISCOVERY_DEVICE_NAME: TheengsGateway 53 | DISCOVERY_FILTER: "[IBEACON,GAEN,MS-CDP]" 54 | SCANNING_MODE: active 55 | ADAPTER: hci0 56 | TIME_SYNC: "[]" 57 | TIME_FORMAT: 0 58 | BINDKEYS: "[]" 59 | ENABLE_TLS: false 60 | ENABLE_WEBSOCKET: false 61 | IDENTITIES: "[]" 62 | WHITELIST: "[11:22:33:44:55:66,AA:BB:CC:DD:EE:FF]" 63 | BLACKLIST: "[11:22:33:44:55:66,AA:BB:CC:DD:EE:FF]" 64 | 65 | volumes: 66 | - /var/run/dbus:/var/run/dbus 67 | ``` 68 | 69 | *MQTT_HOST* is mandatory field, ofcourse. 70 | *MQTT_USERNAME* and *MQTT_PASSWORD* you use if you require authentication with MQTT Host. 71 | 72 | Other variables are not mandatory and you can even leave them out, default values will be used, so you can shorten your docker-compose.yml to this one (if username/password is not used): 73 | 74 | ``` 75 | # docker-compose.yml 76 | version: '3.1' 77 | 78 | services: 79 | theengsgateway: 80 | image: theengs/gateway-ARCH:latest 81 | network_mode: host 82 | environment: 83 | MQTT_HOST: 84 | volumes: 85 | - /var/run/dbus:/var/run/dbus 86 | ``` 87 | 88 | After you have the file created, run `docker-compose up` or `docker compose up`. It will start the process where you can monitor the progress. 89 | This is not ideal way to run but great for first run and for testing to see what's going on and for checking messages. 90 | If you wish to run in background, append --detach after "up" command. 91 | 92 | To bring it down, use `docker-compose down; docker-compose rm -f` or `docker compose down; docker compose rm -f`. 93 | 94 | All of these docker compose commands need to be ran from same directory as where docker-compose.yml file is created. 95 | 96 | ### Using docker run command 97 | You can use following command to run if you don't have docker compose plugin installed: 98 | ``` 99 | docker run --rm \ 100 | --network host \ 101 | --privileged \ 102 | --restart always \ 103 | -e MQTT_HOST= \ 104 | -e MQTT_USERNAME= \ 105 | -e MQTT_PASSWORD= \ 106 | -e MQTT_PUB_TOPIC=home/TheengsGateway/BTtoMQTT \ 107 | -e MQTT_SUB_TOPIC=home/+/BTtoMQTT/undecoded \ 108 | -e MQTT_PRE_TOPIC=home/presence/TheengsGateway \ 109 | -e PRESENCE=false \ 110 | -e GENERAL_PRESENCE=false \ 111 | -e PUBLISH_ALL=true \ 112 | -e PUBLISH_ADVDATA=false \ 113 | -e TIME_BETWEEN=60 \ 114 | -e TRACKER_TIMEOUT=120 \ 115 | -e SCAN_TIME=60 \ 116 | -e LOG_LEVEL=DEBUG \ 117 | -e DISCOVERY=true \ 118 | -e HASS_DISCOVERY=true \ 119 | -e DISCOVERY_TOPIC=homeassistant \ 120 | -e DISCOVERY_DEVICE_NAME=TheengsGateway \ 121 | -e DISCOVERY_FILTER="[IBEACON]" \ 122 | -e SCANNING_MODE=active 123 | -e ADAPTER=hci0 \ 124 | -e TIME_SYNC="[]" \ 125 | -e TIME_FORMAT=0 \ 126 | -e BLE=true \ 127 | -e IDENTITIES="{\"00:11:22:33:44:55:66\":\"0dc540f3025b474b9ef1085e051b1add\",\"AA:BB:CC:DD:EE:FF\":\"6385424e1b0341109942ad2a6bb42e58\"}" \ 128 | -e BINDKEYS="{\"00:11:22:33:44:55:66\":\"0dc540f3025b474b9ef1085e051b1add\",\"AA:BB:CC:DD:EE:FF\":\"6385424e1b0341109942ad2a6bb42e58\"}" \ 129 | -e WHITELIST='"[11:22:33:44:55:66,AA:BB:CC:DD:EE:FF]"' \ 130 | -e BLACKLIST='"[11:22:33:44:55:66,AA:BB:CC:DD:EE:FF]"' \ 131 | -v /var/run/dbus:/var/run/dbus \ 132 | --name theengsgateway \ 133 | theengs/gateway-ARCH:latest 134 | ``` 135 | 136 | And again, shorten it like this if you wish to use recommended defaults (without user/pass): 137 | 138 | ``` 139 | docker run --rm \ 140 | --network host \ 141 | --privileged \ 142 | -e MQTT_HOST= \ 143 | -v /var/run/dbus:/var/run/dbus \ 144 | --name gateway \ 145 | theengs/gateway-ARCH:latest 146 | ``` 147 | 148 | These commands will run the container in foreground, it's not ideal, but if you wish to see what's happening it's great - for testing. 149 | If you want to run it in background, add "--detach" between *docker run* and *--rm* 150 | 151 | ## Configuration 152 | 153 | Once again, MQTT_HOST is the *ONLY* required option, all others can be ommited and defaults will be used. 154 | If used without MQTT_USERNAME and MQTT_PASSWORD, it will try to login annonymously, otherwise, please provide MQTT_USERNAME and MQTT_PASSWORD too. 155 | 156 | As for other configuration options for Theengs Gateway, please review any and all on [Details options](https://gateway.theengs.io/use/use.html#details-options) page on Theengs Gateway official site. 157 | 158 | [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg 159 | [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg 160 | [armv6-shield]: https://img.shields.io/badge/armv6-yes-green.svg 161 | [armv7-shield]: https://img.shields.io/badge/armv7-yes-green.svg 162 | [i386-shield]: https://img.shields.io/badge/i386-yes-green.svg 163 | [current-version]: https://img.shields.io/badge/Current%20Version-0.5.0.1-blue 164 | -------------------------------------------------------------------------------- /chroot/opt/venv/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONFIG="$HOME/theengsgw.conf" 4 | 5 | isempty() { 6 | if [ x"$1" == "x" ]; then 7 | return 0 8 | else 9 | return 1 10 | fi 11 | } 12 | 13 | hasvalue() { 14 | if [ x"$1" == "x" ]; then 15 | return 1 16 | else 17 | return 0 18 | fi 19 | } 20 | 21 | # Exit if MQTT host not specified 22 | if isempty $MQTT_HOST; then 23 | echo "MQTT Host not defined, exiting" 24 | exit 1 25 | fi 26 | 27 | # If we enter username... 28 | if hasvalue $MQTT_USERNAME; then 29 | # ...we must check for password too 30 | if isempty $MQTT_PASSWORD; then 31 | echo "MQTT_USERNAME specified without MQTT_PASSWORD" 32 | exit 1 33 | fi 34 | fi 35 | 36 | ### Syntax checks - START 37 | if hasvalue $MQTT_PORT; then 38 | if ! [[ $MQTT_PORT =~ ^[0-9]+$ ]]; then 39 | echo "WARNING : Wrong value for MQTT_PORT environment variable, will use default - 1883" 40 | MQTT_PORT=1883 41 | fi 42 | fi 43 | 44 | if hasvalue $PUBLISH_ALL; then 45 | if ! [[ $PUBLISH_ALL =~ (true|false) ]]; then 46 | echo "WARNING : Wrong value for PUBLISH_ALL environment variable, will use default - true" 47 | PUBLISH_ALL=true 48 | fi 49 | fi 50 | 51 | if hasvalue $PUBLISH_ADVDATA; then 52 | if ! [[ $PUBLISH_ADVDATA =~ (true|false) ]]; then 53 | echo "WARNING : Wrong value for PUBLISH_ADVDATA environment variable, will use default - false" 54 | PUBLISH_ADVDATA=false 55 | fi 56 | fi 57 | 58 | if hasvalue $PRESENCE; then 59 | if ! [[ $PRESENCE =~ (true|false) ]]; then 60 | echo "WARNING : Wrong value for PRESENCE environment variable, will use default - false" 61 | PRESENCE=false 62 | fi 63 | fi 64 | 65 | if hasvalue $GENERAL_PRESENCE; then 66 | if ! [[ $GENERAL_PRESENCE =~ (true|false) ]]; then 67 | echo "WARNING : Wrong value for GENERAL_PRESENCE environment variable, will use default - false" 68 | GENERAL_PRESENCE=false 69 | fi 70 | fi 71 | 72 | if hasvalue $BLE; then 73 | if ! [[ $BLE =~ (true|false) ]]; then 74 | echo "WARNING : Wrong value for BLE environment variable, will use default - true" 75 | BLE=true 76 | fi 77 | fi 78 | 79 | if hasvalue $SCAN_TIME; then 80 | if ! [[ $SCAN_TIME =~ ^[0-9]+$ ]]; then 81 | echo "WARNING : Wrong value for SCAN_TIME environment variable, will use default - 60" 82 | SCAN_TIME=60 83 | fi 84 | fi 85 | 86 | if hasvalue $TIME_BETWEEN; then 87 | if ! [[ $TIME_BETWEEN =~ ^[0-9]+$ ]]; then 88 | echo "WARNING : Wrong value for TIME_BETWEEN environment variable, will use default - 60" 89 | TIME_BETWEEN=60 90 | fi 91 | fi 92 | 93 | if hasvalue $TRACKER_TIMEOUT; then 94 | if ! [[ $TRACKER_TIMEOUT =~ ^[0-9]+$ ]]; then 95 | echo "WARNING : Wrong value for TRACKER_TIMEOUT environment variable, will use default - 120" 96 | TRACKER_TIMEOUT=120 97 | fi 98 | fi 99 | 100 | if hasvalue $LOG_LEVEL; then 101 | if ! [[ $LOG_LEVEL =~ (DEBUG|INFO|WARNING|ERROR|CRITICAL) ]]; then 102 | echo "WARNING : Wrong value for LOG_LEVEL environment variable, will use default - DEBUG" 103 | LOG_LEVEL=DEBUG 104 | fi 105 | fi 106 | 107 | if hasvalue $DISCOVERY; then 108 | if ! [[ $DISCOVERY =~ (true|false) ]]; then 109 | echo "WARNING : Wrong value for DISCOVERY environment variable, will use default - true" 110 | DISCOVERY=true 111 | fi 112 | fi 113 | 114 | if hasvalue $HASS_DISCOVERY; then 115 | if ! [[ $HASS_DISCOVERY =~ (true|false) ]]; then 116 | echo "WARNING : Wrong value for DISCOVERY environment variable, will use default - true" 117 | HASS_DISCOVERY=true 118 | fi 119 | fi 120 | 121 | if hasvalue $PASSIVE_SCAN; then 122 | # Deprecation warning, this was written before 0.5.0 was released , will use SCANNIN_MODE in future 123 | echo "PASSIVE_SCAN : Deprecated environment variable, this variable will be removed in future versions, please use SCANNING_MODE=active|passive" 124 | 125 | if [[ $PASSIVE_SCAN == true ]]; then 126 | echo "Enabling passive scanning mode" 127 | SCANNING_MODE="passive" 128 | elif [[ $PASSIVE_SCAN == false ]]; then 129 | echo "Disabling passive scanning mode" 130 | SCANNING_MODE="active" 131 | else 132 | echo "Incorrect value for PASSIVE_SCAN environment variable, will use default - active" 133 | fi 134 | fi 135 | 136 | if hasvalue $ADAPTER; then 137 | if ! [ -d /sys/class/bluetooth/$ADAPTER ]; then 138 | echo "WARNING : Adapter name $ADAPTER might not exist. Will accept the value but if you notice issues , please change it" 139 | fi 140 | fi 141 | 142 | if hasvalue $SCANNING_MODE; then 143 | if ! [[ $SCANNING_MODE =~ (active|passive) ]]; then 144 | echo "WARNING : Wrong value for SCANNING_MODE, must be one of: active, passive. Will use default - active" 145 | SCANNING_MODE=active 146 | fi 147 | fi 148 | 149 | if hasvalue $TIME_FORMAT; then 150 | if ! [[ $TIME_FORMAT =~ (true|false) ]]; then 151 | echo "WARNING : Wrong value for TIME_FORMAT environment variable, will use default - false" 152 | TIME_FORMAT=false 153 | fi 154 | fi 155 | 156 | if hasvalue $ENABLE_TLS; then 157 | if ! [[ $ENABLE_TLS =~ (true|false) ]]; then 158 | echo "WARNING : Wrong value for ENABLE_TLS environment variable, will use default - false" 159 | ENABLE_TLS=false 160 | fi 161 | fi 162 | 163 | if hasvalue $ENABLE_WEBSOCKET; then 164 | if ! [[ $ENABLE_WEBSOCKET =~ (true|false) ]]; then 165 | echo "WARNING : Wrong value for ENABLE_WEBSOCKET environment variable, will use default - false" 166 | ENABLE_WEBSOCKET=false 167 | fi 168 | fi 169 | 170 | ### Syntax checks - END 171 | 172 | cd $VIRTUAL_ENV 173 | 174 | echo "Creating config at $CONFIG ..." 175 | { 176 | cat < $CONFIG 229 | 230 | cat $CONFIG 231 | 232 | 233 | python3 -m TheengsGateway $PARAMS 234 | --------------------------------------------------------------------------------