Skip to content

Commit

Permalink
Consolidating updates
Browse files Browse the repository at this point in the history
- unified MQTT configuration
- updated README
- add .gitignore
- add .dockerignore
  • Loading branch information
Gerrit Beine committed Sep 10, 2024
1 parent 63387ea commit a9f3278
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*
!knx2mqtt
!requirements.txt
164 changes: 164 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

# Local configurations
*.conf
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ Each configuration option is also available as command line argument.
| `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` | 'bus/knx' | `-t`, `--mqtt_topic` | The topic to publish MQTT message. |
| `mqtt_tls` | - | `--mqtt_tls` | Use SSL/TLS encryption for MQTT connection. |
| `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. |
| `mqtt_ssl_ca_path` | - | `--mqtt_ssl_ca_path` | The SSL certificate authority file to verify the MQTT server. |
| `mqtt_tls_no_verify` | - | `--mqtt_tls_no_verify` | Do not verify SSL/TLS constraints like hostname. |
| `knx_host` | 'localhost' | `--knx_host` | The address of the KNX tunnel device. |
| `knx_port` | 3671 | `--knx_port` | The port of the KNX tunnel device. |
| `knx_local_ip` | - | `--knx_local_ip` | The ip address of the system that connects to KNX. |
| `knx_individual_address` | - | `--knx_individual_address` | The group address of the system that send telegrams to KNX. |
| `knx_no_queue` | - | `--knx_no_queue` | Workaround for scheduling problems of XKNX telegram queue. |
| `timestamp` | - | `-z`, `--timestamp` | Publish timestamps for all topics, e.g. for monitoring purposes. |
| `verbose` | - | `-v`, `--verbose` | Be verbose while running. |
| - | '/etc/knx2mqtt.conf' | `-c`, `--config` | The path to the config file. |
| `items` | see below | - | The configuration for the items on the KNX bus. |
Expand Down
67 changes: 49 additions & 18 deletions knx2mqtt
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ from xknx.telegram import Telegram, TelegramDirection

XKNX_DPT_MODULE_STR = "xknx.dpt"

knx_tunnel = None
mqtt_client = None
daemon_args = None
item_states = None

verify_mode = {
'CERT_NONE': ssl.CERT_NONE,
'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
Expand All @@ -42,6 +37,12 @@ tls_versions = {
}


knx_tunnel = None
mqtt_client = None
daemon_args = None
item_states = None


def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)

Expand Down Expand Up @@ -129,7 +130,7 @@ def publish_to_mqtt(address, value):
item_states[address] = str(value)

print("Topic: {}, Payload: {}".format(topic, value))
mqtt_client.publish(topic, value)
mqtt_client.publish(topic, str(value))
if daemon_args.timestamp:
mqtt_client.publish("{}/timestamp".format(topic), time.time(), retain=True)

Expand Down Expand Up @@ -173,22 +174,29 @@ def on_mqtt_received(client, userdata, message):
eprint(traceback.format_exc())


def start_mqtt():
def init_mqtt():
logging.debug('Starting MQTT')
global daemon_args
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
if cert_reqs is not None and tls_version is not None:
mqtt_client.tls_set(cert_reqs=cert_reqs, tls_version=tls_version)
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, daemon_args.mqtt_clientid)
if daemon_args.mqtt_tls:
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
if 'mqtt_ssl_ca_path' in daemon_args:
mqtt_client.tls_set(daemon_args.mqtt_ssl_ca_path)#, cert_reqs=cert_reqs, tls_version=tls_version)
elif cert_reqs is not None and tls_version is not None:
print(cert_reqs)
mqtt_client.tls_set(cert_reqs=cert_reqs, tls_version=tls_version)
else:
logging.error("Invalid TLS configuration.")
exit(255)
mqtt_client.tls_insecure_set(daemon_args.mqtt_tls_no_verify)
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)
mqtt_client.on_connect = on_mqtt_connect
mqtt_client.on_message = on_mqtt_received
mqtt_client.connect(daemon_args.mqtt_host, daemon_args.mqtt_port, daemon_args.mqtt_keepalive)
mqtt_client.loop_start()
return mqtt_client


Expand Down Expand Up @@ -239,12 +247,22 @@ def parse_args():
parser.add_argument('-t', '--mqtt_topic', type=str,
default='bus/knx',
help='The topic to publish MQTT message. Default is bus/knx')
parser.add_argument('--mqtt_tls',
default=False,
action='store_true',
help='Use SSL/TLS encryption for MQTT connection.')
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')
parser.add_argument('--mqtt_ssl_ca_path', type=str,
help='The SSL certificate authority file to verify the MQTT server.')
parser.add_argument('--mqtt_tls_no_verify',
default=False,
action='store_true',
help='Do not verify SSL/TLS constraints like hostname.')
parser.add_argument('-k', '--knx_host', type=str,
default='localhost',
help='The hostname of the KNX gateway. Default is localhost')
Expand Down Expand Up @@ -299,10 +317,16 @@ 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' in data:
daemon_args.mqtt_tls = bool(data['mqtt_tls'])
if 'mqtt_tls_version' in data:
daemon_args.mqtt_tls_version = data['mqtt_tls_version']
daemon_args.mqtt_tls = data['mqtt_tls_version']
if 'mqtt_verify_mode' in data:
daemon_args.mqtt_verify_mode = data['mqtt_verify_mode']
daemon_args.mqtt_tls = data['mqtt_verify_mode']
if 'mqtt_ssl_ca_path' in data:
daemon_args.mqtt_ssl_ca_path = data['mqtt_ssl_ca_path']
if 'mqtt_tls_no_verify' in data:
daemon_args.mqtt_tls_no_verify = bool(data['mqtt_tls_no_verify'])
if 'knx_host' in data:
daemon_args.knx_host = data['knx_host']
if 'knx_port' in data:
Expand All @@ -315,6 +339,8 @@ def parse_config():
daemon_args.knx_no_queue = data['knx_no_queue']
if 'timestamp' in data:
daemon_args.timestamp = data['timestamp']
if 'verbose' in data:
daemon_args.verbose = data['verbose']
if 'items' in data:
daemon_args.items = data['items']

Expand Down Expand Up @@ -348,8 +374,13 @@ def main():
global daemon_args, mqtt_client
daemon_args = parse_args()
parse_config()
init_items()
mqtt_client = start_mqtt()
# Verbosity
if daemon_args.verbose:
logging.basicConfig(level=logging.DEBUG)
# MQTT connection
mqtt_client = init_mqtt()
mqtt_client.loop_start()
# KNX connection
start_knx()


Expand Down
38 changes: 37 additions & 1 deletion knx2mqtt.conf.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
{
"mqtt_host": "localhost",
"mqtt_port": "1883"
"mqtt_port": "keepalive"
"mqtt_clientid": "knx2mqtt"
"mqtt_user": "knx2mqtt",
"mqtt_password": "t0p_s3cr3t",
"mqtt_topic": "bus/knx",
"mqtt_tls": "false",
"mqtt_tls_version": "TLSv1.2",
"mqtt_verify_mode": "CERT_NONE",
"mqtt_ssl_ca_path": "/etc/ssl/myca.pem",
"mqtt_tls_no_verify": "false",
"knx_host": "10.0.0.11",
"knx_local_ip": "10.0.7.12"
"knx_local_ip": "10.0.7.12",
"knx_individual_address": "15.15.250",
"knx_no_queue": "true",
"verbose": "false",
"items": [
{
"address": "4/0/11",
"type": "DPTBinary",
"mqtt_subscribe": "true"
},
{
"address": "4/0/21",
"type": "DPTUpDown"
},
{
"address": "5/0/11",
"type": "DPTTemperature"
},
{
"address": "5/0/17",
"type": "DPTHumidity"
},
{
"address": "5/0/18",
"type": "DPTPartsPerMillion"
}
]
}

0 comments on commit a9f3278

Please sign in to comment.