diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..70e816d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: ci + +on: + push: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64,linux/arm64,linux/arm/v6 + push: true + tags: gbeine/fronius2mqtt:latest diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c53e7ff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3-alpine + +WORKDIR /fronius2mqtt + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY fronius2mqtt . + +CMD [ "python", "./fronius2mqtt" ] diff --git a/README.md b/README.md index aca690f..7daa6dd 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ It forwards the data directly to MQTT. ## Install +### Installation using Docker + +docker run -it --rm --name fronius2mqtt -v fronius2mqtt.conf:/etc/fronius2mqtt.conf docker.io/gbeine/fronius2mqtt + +### Native installation with Python venv + - clone the git repository - ensure to have Python 3 with venv installed - run the ```install``` script in the local directory @@ -21,16 +27,18 @@ Each configuration option is also available as command line argument. - copy ```fronius2mqtt.conf.example``` - configure as you like -| option | default | arguments | comment | -|----------------|--------------------------|---------------------|--------------------------------------------------------------------| -| mqtt_host | 'localhost' | -m, --mqtt_host | The hostname of the MQTT server. | -| mqtt_port | 1883 | --mqtt_port | The port of the MQTT server. | -| mqtt_keepalive | 30 | --mqtt_keepalive | The keep alive interval for the MQTT server connection in seconds. | -| mqtt_clientid | 'fronius2mqtt' | --mqtt_clientid | The clientid to send to the MQTT server. | -| mqtt_user | - | -u, --mqtt_user | The username for the MQTT server connection. | -| mqtt_password | - | -p, --mqtt_password | The password for the MQTT server connection. | -| mqtt_topic | 'fronius' | -t, --mqtt_topic | The topic to publish MQTT message. | -| http_host | 'localhost' | --http_host | The address of the HTTP server. | -| http_port | 8080 | --http_port | The port of the HTTP server. | -| verbose | - | -v, --verbose | Be verbose while running. | -| - | '/etc/fronius2mqtt.conf' | -c, --config | The path to the config file. | +| option | default | arguments | comment | +|------------------|--------------------------|---------------------|----------------------------------------------------------------------------------------| +| mqtt_host | 'localhost' | -m, --mqtt_host | The hostname of the MQTT server. | +| mqtt_port | 1883 | --mqtt_port | The port of the MQTT server. | +| mqtt_keepalive | 30 | --mqtt_keepalive | The keep alive interval for the MQTT server connection in seconds. | +| mqtt_clientid | 'fronius2mqtt' | --mqtt_clientid | The clientid to send to the MQTT server. | +| mqtt_user | - | -u, --mqtt_user | The username for the MQTT server connection. | +| mqtt_password | - | -p, --mqtt_password | The password for the MQTT server connection. | +| mqtt_topic | 'fronius' | -t, --mqtt_topic | The topic to publish MQTT message. | +| mqtt_tls_version | 'TLSv1.2' | --mqtt_tls_version | The TLS version to use for MQTT. One of TLSv1, TLSv1.1, TLSv1.2. | +| mqtt_verify_mode | 'CERT_REQUIRED' | --mqtt_verify_mode | The SSL certificate verification mode. One of CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED. | +| http_host | 'localhost' | --http_host | The address of the HTTP server. | +| http_port | 8080 | --http_port | The port of the HTTP server. | +| verbose | - | -v, --verbose | Be verbose while running. | +| - | '/etc/fronius2mqtt.conf' | -c, --config | The path to the config file. | diff --git a/fronius2mqtt b/fronius2mqtt index 28c529f..07f2b24 100755 --- a/fronius2mqtt +++ b/fronius2mqtt @@ -2,7 +2,9 @@ import argparse import json +import logging import os +import ssl import paho.mqtt.client as mqtt from bottle import request, route, post, run @@ -11,6 +13,18 @@ from bottle import request, route, post, run mqtt_client = None daemon_args = None +verify_mode = { + 'CERT_NONE': ssl.CERT_NONE, + 'CERT_OPTIONAL': ssl.CERT_OPTIONAL, + 'CERT_REQUIRED': ssl.CERT_REQUIRED +} + +tls_versions = { + 'TLSv1': ssl.PROTOCOL_TLSv1, + 'TLSv1.1': ssl.PROTOCOL_TLSv1_1, + 'TLSv1.2': ssl.PROTOCOL_TLSv1_2 +} + def extract_request_body(): body = request.body @@ -26,7 +40,7 @@ def extract_request_data(): @route('/') def index(): - return "Hello World!" + return "Hello World!
This is the fronius2mqtt daemon by Gerrit Beine" @post('/current_data_inverter/') @@ -162,8 +176,12 @@ def logdata_data(device): def start_mqtt(): global daemon_args - mqtt_client = mqtt.Client(daemon_args.mqtt_clientid) + mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) + cert_reqs = verify_mode[daemon_args.mqtt_verify_mode] if daemon_args.mqtt_verify_mode in verify_mode else None + tls_version = tls_versions[daemon_args.mqtt_tls_version] if daemon_args.mqtt_tls_version in tls_versions else None + mqtt_client.tls_set(cert_reqs=cert_reqs,tls_version=tls_version) if daemon_args.verbose: + logging.basicConfig(level=logging.DEBUG) mqtt_client.enable_logger() if daemon_args.mqtt_user is not None and daemon_args.mqtt_password is not None: mqtt_client.username_pw_set(daemon_args.mqtt_user, daemon_args.mqtt_password) @@ -201,6 +219,21 @@ def parse_args(): parser.add_argument('-t', '--mqtt_topic', type=str, default='fronius', help='The topic to publish MQTT message. Default is fronius') + parser.add_argument('--mqtt_tls_version', type=str, + default='TLSv1.2', + help='The TLS version to use for MQTT. One of TLSv1, TLSv1.1, TLSv1.2. Default is TLSv1.2') + parser.add_argument('--mqtt_verify_mode', type=str, + default='CERT_REQUIRED', + help='The SSL certificate verification mode. One of CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED. Default is CERT_REQUIRED') + # TODO + # parser.add_argument('--mqtt_ca_cert', type=str, + # help='The path to the SSL CA certificate. Optional, no default') + # parser.add_argument('--mqtt_certfile', type=str, + # help='The path to the SSL certificate file. Optional, no default') + # parser.add_argument('--mqtt_keyfile', type=str, + # help='The path to the SSL key file. Optional, no default') + # parser.add_argument('--mqtt_keyfile_password', type=str, + # help='The path to the SSL key password. Optional, no default') parser.add_argument('--http_host', type=str, default='localhost', help='The address of the HTTP server. Default is localhost') @@ -240,6 +273,19 @@ def parse_config(): daemon_args.mqtt_password = data['mqtt_password'] if 'mqtt_topic' in data: daemon_args.mqtt_topic = data['mqtt_topic'] + if 'mqtt_tls_version' in data: + daemon_args.mqtt_tls_version = data['mqtt_tls_version'] + if 'mqtt_verify_mode' in data: + daemon_args.mqtt_verify_mode = data['mqtt_verify_mode'] + # TODO + # if 'mqtt_ca_cert' in data: + # daemon_args.mqtt_ca_cert = data['mqtt_ca_cert'] + # if 'mqtt_certfile' in data: + # daemon_args.mqtt_certfile = data['mqtt_certfile'] + # if 'mqtt_keyfile' in data: + # daemon_args.mqtt_keyfile = data['mqtt_keyfile'] + # if 'mqtt_keyfile_password' in data: + # daemon_args.mqtt_keyfile_password = data['mqtt_keyfile_password'] if 'http_host' in data: daemon_args.http_host = data['http_host'] if 'http_port' in data: