Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copy this file to .env and fill in your values.
# .env is listed in .gitignore and will NOT be committed.

# ----- MQTT settings -----
MQTT_SERVER=192.168.1.1
MQTT_PORT=1883
MQTT_PATH=homeassistant/webhook

# Optional: MQTT broker credentials
# MQTT_USER=myuser
# MQTT_PASSWORD=mypassword

# Optional: Enable TLS for MQTT connection (1 / true / yes)
# MQTT_TLS=false

# ----- Webhook authentication -----
# If set, every incoming POST must include:
# Authorization: Bearer <token>
# Leave empty to disable authentication (not recommended in production).
# WEBHOOK_TOKEN=change-me-to-a-strong-random-secret

# ----- Timezone -----
TZ=Europe/Berlin

# ----- Traefik labels (adapt to your domain) -----
WEBHOOK_HOST=webhook.example.com
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
12 changes: 6 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
FROM tiangolo/meinheld-gunicorn-flask:latest
FROM python:3.12-slim

RUN apt-get update && apt-get install -y \
python3-pip
WORKDIR /app

RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip3 install --upgrade Flask
RUN pip3 install paho-mqtt
COPY requirements.txt .
RUN python -m pip install --no-cache-dir -r requirements.txt

COPY ./app /app

CMD ["gunicorn", "--bind", "0.0.0.0:80", "main:app"]
Binary file added app/__pycache__/main.cpython-312.pyc
Binary file not shown.
56 changes: 41 additions & 15 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,19 @@
import paho.mqtt.client as mqtt
import json
import datetime

import os
#$env:MQTT_SERVER='192.168.176.6'
import sys

#-------------Output Logger
# create logger
logger = logging.getLogger("Webhook2MQTT")
#logger.setLevel(logging.INFO)
logger.setLevel(logging.INFO)
# create console handler with a higher log level
ch = logging.StreamHandler()
#ch.setLevel(logging.INFO)
ch.setLevel(logging.INFO)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
#formatter = logging.Formatter('%(levelname)s - %(message)s')
ch.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(ch)
Expand All @@ -29,7 +26,7 @@
mqtt_server = os.environ['MQTT_SERVER']
else:
logger.error("Please set environment-Variable for MQTT_SERVER")
os.exit()
sys.exit(1)
logger.info("set MQTT_SERVER to {}".format(mqtt_server))

if 'MQTT_PORT' in os.environ:
Expand All @@ -44,25 +41,54 @@
mqtt_path = 'webhook'
logger.info("set mqtt-path to '{}'".format(mqtt_path))

mqtt_user = os.environ.get('MQTT_USER')
mqtt_password = os.environ.get('MQTT_PASSWORD')
mqtt_tls = os.environ.get('MQTT_TLS', '').lower() in ('1', 'true', 'yes')
if mqtt_user:
logger.info("MQTT authentication enabled")
if mqtt_tls:
logger.info("MQTT TLS enabled")

webhook_token = os.environ.get('WEBHOOK_TOKEN')
if webhook_token:
logger.info("Webhook token authentication enabled")
else:
logger.warning("WEBHOOK_TOKEN is not set — webhook endpoint is unauthenticated")

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 # 1 MB limit


def workit(params):
logger.info("workit:")
logger.info(params)
params['timestamp'] = datetime.datetime.now().isoformat()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
client.connect(mqtt_server, mqtt_port, 60)
client.publish(mqtt_path, json.dumps(params),qos=0,retain=True)
client.disconnect()
logger.info(params)
try:
params['timestamp'] = datetime.datetime.now().isoformat()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
if mqtt_user:
client.username_pw_set(mqtt_user, mqtt_password)
if mqtt_tls:
client.tls_set()
client.connect(mqtt_server, mqtt_port, 60)
client.publish(mqtt_path, json.dumps(params), qos=0, retain=True)
client.disconnect()
except Exception as e:
logger.error("Failed to publish MQTT message: {}".format(e))
Comment on lines +75 to +76


@app.route('/', methods=['POST'])
def respond():
logger.info(request)
myparams=request.get_json()
if webhook_token:
auth_header = request.headers.get('Authorization', '')
if auth_header != 'Bearer {}'.format(webhook_token):
logger.warning("Unauthorized request from {}".format(request.remote_addr))
return Response(status=401)
Comment on lines +82 to +86
myparams = request.get_json()
if myparams is None:
logger.warning("Received non-JSON or empty request body")
return Response(status=400)
Comment on lines +87 to +90
x = threading.Thread(target=workit, args=(myparams,))
x.start()
return Response(status=200)

#app.run (host = "localhost", port = 5050)

18 changes: 10 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: "3.6"
services:
webhook2mqtt:
build: .
Expand All @@ -11,24 +10,27 @@ services:
- "/etc/localtime:/etc/localtime:ro"
- "/etc/timezone:/etc/timezone:ro"
environment:
TZ: "Europe/Berlin"
MQTT_SERVER: 'home.jru.me'
MQTT_PORT: 1883
MQTT_PATH: 'homeassistant/webhook'
TZ: ${TZ:-Europe/Berlin}
MQTT_SERVER: ${MQTT_SERVER}
MQTT_PORT: ${MQTT_PORT:-1883}
MQTT_PATH: ${MQTT_PATH:-webhook}
# MQTT_USER: ${MQTT_USER}
# MQTT_PASSWORD: ${MQTT_PASSWORD}
# MQTT_TLS: ${MQTT_TLS:-false}
# WEBHOOK_TOKEN: ${WEBHOOK_TOKEN}
# example config for https with traefik
# networks:
# - traefik_proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.webhook2mqtt_router_http.entrypoints=web"
- "traefik.http.routers.webhook2mqtt_router_http.rule=Host(`netatmo.jru.me`)"
- "traefik.http.routers.webhook2mqtt_router_http.rule=Host(`${WEBHOOK_HOST}`)"
# - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https"
# - "traefik.http.routers.webhook2mqtt_router_http.middlewares=https_redirect"
- "traefik.http.routers.webhook2mqtt_router.entrypoints=websecure"
- "traefik.http.routers.webhook2mqtt_router.rule=Host(`netatmo.jru.me`)"
- "traefik.http.routers.webhook2mqtt_router.rule=Host(`${WEBHOOK_HOST}`)"
- "traefik.http.routers.webhook2mqtt_router.tls=true"
- "traefik.http.routers.webhook2mqtt_router.tls.certresolver=my_certresolver"
restart: always
# log to syslog | optional
logging:
driver: syslog
Expand Down
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Flask==3.1.3
paho-mqtt==2.1.0
gunicorn==26.0.0