This MQTT Relay enables seamless communication between your MQTT devices/services and Loxone Miniserver. It is heavily insipred and based upon the extraordinary work of Loxberry - especially the MQTT Gateway.
The MQTT has been created with several goals in mind:
- Follow best practices established by the Loxberry community
- Providing isolated MQTT bridging capabilites for scenarios, where a full Loxberry installation is not necessary
- Enabling provisioning over Docker
- Optimizing for low power scenarios
To achieve these goals the MQTT Relay for Loxone uses more opionionted and minimalist approach:
- Inbound communication with the MQTT Relay is done exclusively via UDP, with a reduced featureset compared to Loxberry
- Outbound communication with the Miniserver is done exclusively via HTTP/Websocket (no UDP capability)
- Interaction with the MQTT Relay is done primarily via MQTT, configuration is done exclusively via the
config.tomlfile, but no integrated functionality like MQTT-Finder, Incoming Message Overview - Except for boolean mapping and JSON flatteining, no transformers
- No provisioning of an integrated packaged MQTT broker
The general mindset is to use MQTT Relay for Loxone in conjunctin with other tools:
- Using MQTT Monitoring tools like MQTT Explorer to find MQTT-Topics and get information about the processed and forwarded topics from the MQTT Relay
- Using middleware like Node-Red, Homeassistant for transformation of topics
- Using an external MQTT Broker of your choice
⚠️ Disclaimer: This project is not affiliated with, endorsed by, or connected to Loxone Electronics GmbH in any way. Loxone and Miniserver are trademarks of Loxone Electronics GmbH.
# Create a configuration directory and copy the default config
mkdir -p config
curl -o config/config.toml https://raw.githubusercontent.com/Jakob-Gliwa/loxMqttRelay/main/config/default_config.toml
# Edit the config file with your settings
# nano config/config.toml
# Run the container
docker run -d \
--name loxmqttrelay \
--restart unless-stopped \
-v $(pwd)/config:/app/config \
-p 11884:11884/udp \
acidcliff/loxmqttrelay
Optionally set -e LOG_LEVEL=DEBUG for more detailed logging 1. Clone the repository:
```bash
git clone https://github.com/Jakob-Gliwa/loxMqttRelay.git
cd loxmqttrelay
```
2. Copy the default configuration:
```bash
cp config/default_config.toml config/config.toml
```
3. Edit config/config.toml with your settings:
```toml
[broker]
host = "your-mqtt-broker"
port = 1883
[miniserver]
miniserver_ip = "your-miniserver-ip"
miniserver_user = "your-user"
miniserver_pass = "your-password"
```
4. Run:
```bash
uv venv .venv
source .venv/
bin/activate
uv pip install .
python loxmqttrelay
```graph LR
MQTT[MQTT Broker] --> |MQTT|Relay[MQTT Relay]
Relay -->|HTTP/Websocket| Loxone[Loxone Miniserver]
graph LR
Relay[MQTT Relay] --> |MQTT| MQTT[MQTT Broker]
UPD[UDP Client] --> |UDP Message| Relay[MQTT Relay]
Loxone[Loxone Miniserver] -->|UDP Message| UPD[UDP Client]
- Bidirectional communication between MQTT and Loxone
- Dynamic topic filtering and whitelisting
- JSON payload expansion for complex data structures
- Automatic boolean value conversion
- Live configuration updates via MQTT
- Automatic synchronization of whitelisted topics using the Miniserver configuration
- Robust XML parsing with lxml recovery mode for malformed Loxone v16 configurations
- Topic monitoring and processing feedback
- Configuration via
config.toml
The Relay accepts UDP messages on the configured port (default: 11884). Messages should be space-separated in one of these formats:
publish topic message # Explicitly publish a message
retain topic message # Publish a retained message
topic message # Defaults to publish
Examples:
publish home/livingroom/light on
retain home/temperature 22.5
home/kitchen/light off # Will be published without retain
- Copy the provided
default_config.tomlas your starting point:cp config/default_config.toml config/config.toml
- Configure your MQTT broker settings and other preferences in
config.toml - Set up your Miniserver connection details
- Start the MQTT Relay
Start the MQTT Relay:
python main.pyYou can also set the logging level:
python main.py --log-level INFOYou can run the MQTT Relay using Docker, with configurable logging levels.
docker run -d \
-v /path/to/your/config:/app/config \
-e LOG_LEVEL=INFO \
-p 11884:11884/udp \
mqttrelay-
Volume Mapping: Mount your configuration directory to
/app/configin the container. This directory should contain yourconfig.tomlfile:-v /path/to/your/config:/app/config
-
Logging Level: Control the verbosity of logging:
- Set
LOG_LEVELto one of: DEBUG, INFO, WARNING, ERROR, CRITICAL - Default is INFO if not specified
- Example:
-e LOG_LEVEL=DEBUGfor detailed logging
- Set
-
Ports:
- UDP port (default 11884) for receiving UDP messages
version: '3'
services:
mqttrelay:
image: acidcliff/loxmqttrelay
volumes:
- ./config:/app/config
environment:
- LOG_LEVEL=INFO
ports:
- "11884:11884/udp"
restart: unless-stoppedThe MQTT Relay is configured through a config.toml file. A default configuration file (default_config.toml) is provided as a starting point with sensible defaults.
The logging level can be set in three ways, with the following priority (highest to lowest):
-
Command-line argument:
python main.py --log-level DEBUG
-
Environment variable (when using Docker):
docker run -e LOG_LEVEL=DEBUG ...
-
Configuration file (config.toml):
[general] log_level = "INFO"
Available log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
- DEBUG: Detailed information for debugging
- INFO: General operational information
- WARNING: Warning messages for potential issues
- ERROR: Error messages for serious problems
- CRITICAL: Critical issues that may prevent operation
If an invalid log level is provided, it will default to INFO with a warning message.
[broker]
host = "test.mosquitto.org"
port = 1884
user = "" # null becomes empty string in TOML
password = "" # null becomes empty string in TOML
client_id = "loxmqttrelay"
mqtt_version = "3.1" # "3.1" (default) for MQTT 3.1.x, "5" for MQTT 5The relay supports both MQTT 3.1.x and MQTT 5. The version is selected via the
mqtt_version option in the [broker] section:
mqtt_version = "3.1"(also accepts"3.1.1") - use MQTT 3.1.x. This is the default and is also used when the option is missing entirely.mqtt_version = "5"- use MQTT 5. This is required if you want to attach MQTT 5 user properties to messages coming from Loxone (see MQTT5 User Properties).
If an unknown value is provided, the relay logs a warning and falls back to MQTT 3.1.x.
Configure which MQTT topics to subscribe to:
[topics]
subscriptions = ["device/#","sensors/#",]
subscription_filters = ["device.*(data(?!.*(?:private|internal))"]
topic_whitelist = []
do_not_forward = []Subscriptions define to which topics the MQTT Relay will subscribe. Subscriptions follow the MQTT subcrition syntax. Subscription filters filter topics from further processing. These filters are defined by Regular Expressions. If you just wish to stop topics from being sent to the miniserver use the doNotForward-Option
When forwarding topics to Loxone, the MQTT Relay automatically normalizes topic names:
- Forward slashes (/) are replaced with underscores (_)
- Percent signs (%) are replaced with underscores (_)
For example:
device/statusbecomesdevice_statussensor%tempbecomessensor_temphome/living%room/tempbecomeshome_living_room_temp
This ensures compatibility with Loxone's naming restrictions while maintaining topic readability.
Alternatively to (or in combination with subscription filters) a topic whitelist can be defined. Only topics contained in the whitelist will be forwarded to the Miniserver. The topic whitelist is applied to the processed topics (so with boolean mapping and json flatteining applied if so selected) and with the normalization to send it to the Miniserver (so "device/status" becomes "device_status"):
[topics]
topic_whitelist = ["device_status","sensor_data"]Specify topics that should not be forwarded to the miniserver:
[topics]
do_not_forward = ["internal/topic","private/data"]Attention: If Whitelist is defined doNotForward will be ignored
[processing]
expand_json = false // Expand JSON payloads into individual values
convert_booleans = false // Convert boolean strings to actual boolean valuesThe MQTT Relay supports secure websocket communication with the Miniserver:
[miniserver]
use_websocket = trueWhen websocket communication is enabled:
- Secure encrypted communication using AES and RSA
- Token-based authentication with automatic token refresh
- Automatic reconnection handling
- Keepalive mechanism to maintain connection (every 30 seconds)
- Support for both ws:// and wss:// (secure websocket) connections
The websocket implementation provides:
- More reliable and secure communication compared to HTTP
- Real-time bidirectional communication
- Automatic handling of connection issues
- Support for both encrypted and unencrypted connections
[udp]
udp_in_port = 11884Attention: Do not change this value if you run MQTT Relay from within Docker - use docker port mapping if you need another port
[miniserver]
miniserver_ip = "127.0.0.1"
miniserver_port = 80
miniserver_user = ""
miniserver_pass = ""
miniserver_max_parallel_connections = 5
use_websocket = falseYou can update the relays's configuration on the fly using MQTT messages. All topics are prefixed with your configured base_topic.
Topic: config/set
Set will ceompletely overwrite the current settings with the given keys.
{
"subscriptions": ["new/topic1", "new/topic2"],
"topic_whitelist": ["new_whitelist_topic"]
}Topic: config/add
Add is just available for lists and will add the given elements to the list of the given keys.
{
"subscriptions": ["additional/topic"],
"topic_whitelist": ["new_allowed_topic"]
}Topic: config/remove
Remove is just available for lists and will remove the given elements to the list of the given keys.
{
"subscriptions": ["topic/to/remove"],
"topic_whitelist": ["topic_to_unallow"]
}Topic: config/get
Publish any message to this topic to receive the current configuration. The relay will respond on config/response with the current configuration in JSON format. Note that sensitive information (login credentials) will be removed from the response.
Example response on config/response:
{
"broker": {
"host": "192.168.X.X",
"port": 1883
},
"base_topic": "mqttrelay/",
"subscriptions": ["device/#", "sensor/#"],
"topic_whitelist": ["device_status", "sensor_data"],
"expand_json": true,
"convert_booleans": true,
"udpinport": 11884,
"use_http": true,
"miniserver_ip": "192.168.X.X"
}{base_topic}/config/update: Reload configuration from file{base_topic}/config/restart: Restart the MQTT Relay application
Enable automatic synchronization with your Miniserver's configuration:
[miniserver]
sync_with_miniserver = trueWhen enabled, the relay will:
- Automatically load Miniserver configuration on startup
- Update the topic whitelist based on Miniserver inputs
- Resync when Miniserver configuration changes
Caution: This function will assume that every Virtual Input is a possible target for forwarding mqtt messaages.
Configure your Miniserver to publish any message to {base_topic}/miniserverevent/startup on startup to trigger an automatic resync with the Miniserver configuration.
For development and testing, you can point the MQTT Relay to a mock Miniserver (basically any HTTP server):
[debug]
mock_ip = "192.168.X.X:<port>"
enable_mock = trueIt is not possible to use the mock Miniserver functionality with the websocket communication.
To make the mock Miniserver work, you need to set the use_websocket option to false in the [miniserver] section.
[miniserver]
use_websocket = falseThe mock server needs to support the following endpoints:
http://{mock_ip}/dev/sps/io/{topic}/{value}http://{mock_ip}/dev/sps/io/{topic}/
The mock server needs to return a 200 status code for successful requests.
The mock Miniserver functionality can be enabled/disabled without removing the IP configuration:
mock_ip: The IP address and port of your mock Miniserverenable_mock: Enable or disable the mock Miniserver functionality (default: false)
- The relay automatically restarts after configuration changes to apply new settings
- Regular backups of your configuration file are recommended
- Test configuration changes in a development environment first
- Topic monitoring can increase MQTT traffic, enable only when needed
- Live configuration updates via MQTT
- Automatic synchronization of whitelisted topics using the Miniserver configuration
- Topic monitoring and processing feedback
The Relay accepts UDP messages on the configured port (default: 11884). Messages should be space-separated in one of these formats:
publish topic message # Explicitly publish a message
retain topic message # Publish a retained message
topic message # Defaults to publish
Examples:
publish home/livingroom/light on
retain home/temperature 22.5
home/kitchen/light off # Will be published without retain
The MQTT Relay accepts simple text messages via UDP that tell it what to publish to MQTT. Here's how to format your messages:
- Simple Messages (automatically published):
kitchen/light on
bedroom/fan off
living/temperature 22.5
- Messages with Retain Flag (stays on MQTT broker after disconnect):
retain kitchen/light on
retain living/temperature 22.5
- Explicit Publish (same as simple messages):
publish kitchen/light on
- Topics with Spaces: Although it's a discouraged MQTT practice, you can use spaces in your topics naturally:
zigbee2mqtt/Living Room Light/set on
zigbee2mqtt/Kitchen Counter/brightness 80
Spaces however are only allowed between tokens, not at end of a topic.
zigbee2mqtt/Living Room Light/set status on -> topic: zigbee2mqtt/Living Room Light/set, payload: status on
zigbee2mqtt/Kitchen Counter/brightness 80 -> topic: zigbee2mqtt/Kitchen Counter/brightness, payload: 80
- JSON Messages: For devices that need JSON data, just include the JSON part at the end:
home/lights {"state": "on", "brightness": 100}
zigbee2mqtt/Living Room Light/set {"state": "ON", "brightness": 255}
The JSON payload will be sent as-is with no validation or quoting of the JSON string.
Everything before the first { is considered the topic.
When the relay runs in MQTT 5 mode (mqtt_version = "5" in the [broker] section), you can attach MQTT 5 user properties to a message coming from Loxone. Add an optional block in square brackets right after the (optional) command and before the topic:
[publish|retain] [key1=value1;key2=value2] topic payload
Rules:
- The block is optional. It is only treated as user properties if it starts with
[, has a closing], and contains at least one realkey=valuepair (non-empty key,=present). Otherwise the[...]is treated as a normal part of the topic. - Pairs are separated by
;, key and value by the first=. Values may contain spaces and additional=characters because the block is delimited by]. - Empty values are allowed (e.g.
[flag=]). Multiple pairs, including duplicate keys, are allowed (MQTT 5 permits repeated user property keys). - Malformed segments (e.g. without
=or with an empty key) are skipped with a warning.
Examples:
publish [source=loxone;room=kitchen] home/light on
[unit=celsius] home/temp 22.5
retain [origin=ms1] home/status online
Notes:
- User properties are only sent when MQTT 5 is enabled. If you provide them while running in MQTT 3.1.x mode, they are ignored (with a log warning).
- Limitation: a topic must not start with a valid
[key=value;...]block, since that prefix is interpreted as user properties.
Here are some typical use cases:
# Basic light control
kitchen/light on
living/light off
# Setting values
bedroom/temperature 21.5
kitchen/humidity 45
# Device control with JSON
home/thermostat {"mode": "heat", "target": 22}
# Retained status messages
retain home/system/status online
retain home/sensor/battery 80
This project was inspired by and builds upon the work of several other projects:
- The folks at Loxberry: For the original idea and implementation, their relentless work for the Loxone Community and sheer masterful work on Loxberry and its main plugins
- JoDehli (PyLoxone, pyloxone-api): For the Loxone Miniserver communication protocol implementation I adapted the websocket listener from
- Alladdin: For his Loxone related reference implementation and research
- Node-Red-Contrib-Loxone: For the Node-Red Loxone plugin, which made testing much easier and provided valueable insights in handling the websocket communication
- Loxone: For building such a great home automation ecosystem and providing the openness to build upon it
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.