A Python implementation of the Alpha Connect protocol used to configure the GLT (Gebäudeleittechnik / Building Management) interface on Alpha Innotec / LuxTronic heat pump controllers, plus an integrated USB dongle license writer.
- Alpha Connect protocol reference: https://pimassetsprdst.blob.core.windows.net/assets/apc_Original/58/86/51205886.pdf
- License block layout reference: https://github.com/Jaarden/luxtronic-glt-licensetool
The protocol details, packet formats, and license-block layout in this repo were
reverse-engineered from the appl firmware binary (ARM 32-bit ELF) with radare2
and Ghidra. Everything here is verified against the actual decompiled functions
inside the running controller — no protocol details were guessed.
Claude Code was used for assisting me in creating a web wrapper arround the UDP communication, porting the C-code into python and creating this readme :)
Two things, exposed as tabs in a single local web UI:
-
Alpha Connect Tool — sends a UDP packet on port 4444 that:
- Writes
/home/GLT.confon the controller - Calls
InitGLT()internally - Starts the BACnet server on port 47808 (or Modbus TCP on port 502)
No SSH, no physical access, no reboot. This is exactly what the official Alpha Connect Windows tool does, replicated in Python.
- Writes
-
License Manager — reads/writes/decrements the 512-byte license block on a USB dongle. Ported from
Jaarden/luxtronic-glt-licensetoolwith hardening for the intermittency bugs of the original C tool (stdio buffering, filesystem overwrites, device-vs-partition confusion).
# UDP configuration only (no license operations):
python3 alpha_connect_web.py
# Enable license operations (needs raw block-device access):
sudo python3 alpha_connect_web.pyOpen http://localhost:8080/ in a browser. Choose the tab you need:
- Alpha Connect Tool — enter the heat pump's IP, pick a mode (0/1/2), fill in the BACnet identity (auto-hidden when Modbus is selected), click Apply Configuration + Enable BACnet. The tool sends the UDP packet and automatically probes BACnet on port 47808 to confirm success.
- License Manager — scan for USB dongles, pick one, and create / check / decrement the license count.
Everything is stdlib-only — no pip install needed.
| File | Purpose |
|---|---|
alpha_connect_web.py |
Main app: local HTTP server + browser UI for both tools |
alpha_connect.py |
Standalone CLI for the UDP protocol only |
fernwartung_server.py |
Experimental Fernwartung FTP emulator (not required for normal use) |
glt_manager.html |
Older single-file webapp (superseded by alpha_connect_web.py) |
appl |
The LuxTronic firmware binary — analyzed with Ghidra to derive the protocol |
Packet format is semicolon-separated ASCII. First three fields are integer
command/subcommand/mode; the rest are string arguments consumed positionally
by UDP_Handler at 0xddc54 in the firmware.
5000;47809;<mode>;<bacnet_port>;<reserved>;<Fkt_AT>;<Fkt_Freigabe>;
<Fkt_Sollwert>;<Fkt_Sollwert_MK1>;<Fkt_Sollwert_MK2>;<Fkt_Sollwert_MK3>;
<Fkt_Ba_Heizen>;<Fkt_Ba_Brauchw>;<Fkt_Ba_MK1>;<Fkt_Ba_MK2>;<Fkt_Ba_MK3>;
<Fkt_Ba_Kuehl>;<Fkt_Ba_Lueftung>;<Fkt_Ba_Schwimmbad>;
<vendorname>;<vendor_id>;<modelname>;<location>;<device_description>;
<device_id>;<devicename>;
5000= command "config write"47809= subcommand 0xBAC1 — write GLT config (subcommand 0xBAC0 is read-only, it only responds with the current config)mode— 0 = disabled, 1 = BACnet, 2 = Modbus TCPbacnet_port— usually 47808 (0xBAC0)- 14
Fkt_*toggles corresponding to the fields the firmware writes into/home/GLT.conf - 7 identity strings that populate the BACnet Device Object
The firmware handler then:
- Opens
/home/GLT.confand writes each field - Calls
Sync()to flush to disk - Sets
Einst_GLT_aktiviert = 23viastoreParameter(0x3CC, ...)— a reload marker thatInitGLT()reacts to - Calls
InitGLT(), which reads the freshGLT.confand creates theBACnetorTCP_ModBusobject and starts listening
The license lives in the last 512 bytes of the raw USB device. Layout:
| Offset | Size | Meaning |
|---|---|---|
0x34 |
2 bytes LE | License count (uint16) |
0x65 |
2 bytes | Fixed 01 02 (checksum-source, arbitrary values) |
0x69 |
2 bytes | Fixed 03 04 (checksum-source, arbitrary values) |
0x1FC |
2 bytes LE | Stored checksum (uint16) |
| everything else | — | Random padding (ignored by the check) |
The checksum is a simple three-part sum:
stored == (raw[0x34] + raw[0x35]) + (raw[0x65] + raw[0x66]) + (raw[0x69] + raw[0x6A])
Verified against UnlockBMS::getCheckSum at 0x1b1d88 and
UnlockBMS::checkLicenses at 0x1b2068 in the firmware.
Reverse-engineering and reference implementation only. Provided as-is for educational and research purposes. Modifying settings on a production heat pump controller may violate warranty terms.

