hoermoles-ble
Getting started: commissioning a drive
Everyday use is via the hoermoles-ble CLI (published separately as package
hoermoles-ble-cli, built on top of this library):
-
Install the CLI as a standalone tool - this gives you the
hoermoles-blecommand on your$PATH, no workspace checkout needed:uv tool install hoermoles-ble-cli
(Contributing to/developing this repo instead? Use
cd python && uv syncand prefix every command below withuv runinstead.) -
Get the QR code sticker's content onto disk - there's no camera-scanning app yet, so either read it with a separate scanner app, or take a photo of the sticker and decode it with a command-line QR decoder, e.g.
zbarimg(zbarimg --raw photo.jpg). Then save the decoded content once:hoermoles-ble save-qr "<QR code content>"
-
Scan for the drive over BLE - matches the saved QR code by serial number and saves the drive's product type to the device registry (see below):
hoermoles-ble scanSeeing only the short packet ("incompletely parsed", no
Admin taught: ...line) is normal and not a sign of anything - both our own drive (already paired) and the official app's own code treat this as a plain BLE scan-timing artifact, not something tied to admin/pairing status (the decompiled app has explicit, unconditional logic to recover once the longer packet eventually arrives - see the local reveng report). Just use a longer--timeoutor retryscan. -
Register (one-time pairing). Only works while the drive still accepts a new admin (
AdminsCanBeTeached=Truein the scan output above) - reset via the drive's menu 19/parameter 02 first if not (this invalidates the existing pairing, e.g. with your phone):hoermoles-ble register --address <MAC>
This derives and saves the root key under
~/.hoermoles/credentials/<MAC>.json- no QR code, app, or cloud needed again afterwards. -
Trigger the gate:
hoermoles-ble exec --address <MAC> impulse
From here on --address can usually be omitted (it defaults to the only/
first saved credentials). See packages/hoermoles-ble-cli/README.md for the
full command reference (menu-get/menu-set for operator settings,
view-log for the audit log/diagnostics counters, list-devices, ...) and
the workspace-level python/README.md for a longer walkthrough.
Library internals
Protocol core (protocol.py, crypto_rsa.py) and BLE client (client.py,
transport.py, ble_transport.py) for the Hoermann BlueSecur "Signed"
channel.
protocol.py deliberately has no third-party dependencies (stdlib only) and
serves as a template for ports to other languages. bleak (for the real BLE
transport) is an optional extra: pip install hoermoles-ble[bleak].
Reverse-engineered from the official Hoermann BlueSecur app; see the module
docstrings in protocol.py, client.py, and advertisement.py for details
on where each part of the protocol comes from.
Unit-tested in tests/ (uv run pytest from the workspace root python/) -
the pure protocol/crypto/config logic directly, client.py against an
in-memory fake transport. See the workspace README
for details; this package's live-hardware verification notes above are the
complement to that, not a replacement.
Operator menu/parameter settings
Besides the channel commands (open/close/light/...), the drive also exposes
its classic numbered configuration menus (menu 20 "reversal limit", menu 25
"operator light", menu 52 "speed door open", ...) over BLE - the same menus
normally set via the hand-transmitter/display sequence, exposed by the
official app as "operator settings". HoermannClient.read_properties() and
.write_properties() (in client.py) read/write these; menu_settings.py
provides the menu-number-to-wire-byte table and valid values per menu for
every product the official app knows about (Supramatic Serie 4/E4, Rollmatic
2, SilentDrive 2, Supramatic 4 H4, HET), sourced from the app's own embedded
menu-concept resources, one DriveMenuTable per (product_class, product_id)
in DRIVE_MENU_TABLES. See the module docstring for the exact
firmware/software scope per product (menu_group wire bytes are NOT
interchangeable across products or even across ProductIDs that share the
same advertised name) and the CLI's menu-get/menu-set commands for a
ready-to-use example.
The read direction (read_properties()/GET_PROPERTIES/GET_SELECTED_PROPERTIES)
is live-verified against a real Supramatic E4 - both a full, unfiltered read
and filtered multi-menu reads returned plausible values. One live-confirmed
protocol quirk: a GET_SELECTED_PROPERTIES request that mixes a menu group <
100 with one >= 100 makes the drive drop the connection instead of
answering - read_properties() avoids this via
protocol.batch_menu_groups_for_selected_properties(). The write direction
(write_properties()/SET_PROPERTIES) is only structurally derived from the
decompiled app and NOT yet confirmed live for any product - read a value back
before writing it, and double-check menu numbers/values against the printed
operator manual.
Device registry (which product a paired drive is)
devices.py keeps a separate <config_dir>/devices.json registry mapping
each MAC address to its product_class/product_id/product_name (and serial
number, if known) via save_device_info()/get_device_info()/
list_device_infos() - deliberately not part of credentials.py/
Credentials, since it's non-secret metadata and, unlike a per-device
credentials file, one file can list every drive ever seen. scan_devices()
itself is read-only (no root key needed, nothing persisted) - the CLI's
scan command is what saves a DeviceInfo for each device it finds with a
decodable product_class/product_id; register also saves one directly from
the QR code prefix. menu-get/menu-set use the registry to pick the right
DriveMenuTable automatically.
Audit log and diagnostics counters
HoermannClient.read_log()/.read_service_data() (in client.py) read the
same two things the app's "log"/diagnostics view shows: a security/access
audit log (who registered, which channel got toggled, blocked login
attempts, clock changes, ...) and service counters (operating hours, door
cycles, maintenance counters, ...) - see device_log.py for the
LogTag/ServiceType name tables and per-tag field decoding
(parse_log_fields()). Both are live-verified against a real Supramatic E4 -
that check is also what caught a transcription error in SERVICE_TYPE_NAMES
(wire byte 17 is ELEMENTS_COUNTER, not ENGINE_RUNTIME - the
wire-byte-to-meaning mapping in the decompiled app is an explicit switch
statement, not the ServiceType enum's own declared integer values). A
handful of ServiceType entries carry a proprietary Hoermann fixed-32-day-
month timestamp encoding instead of a plain counter - NOT decoded here (see
SERVICE_TYPE_IS_TIMESTAMP), only the plain-integer counters (including
operating hours) are. See the CLI's view-log command for a ready-to-use
example.
Metadata
Release files for hoermoles-ble 0.12.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hoermoles_ble-0.12.1.tar.gz | 65.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hoermoles_ble-0.12.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 122.7 kB
Release files / hoermoles_ble-0.12.1.tar.gz
| Download URL | hoermoles_ble-0.12.1.tar.gz |
|---|---|
| Size | 65.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2b38ad2b694a1b0006168e69df885e8a1bf26aa24a2e926d45408d1e8d04d347
|
|
BLAKE2b-256 checksum How to use checksums |
d6327d258c32da98167732018b73ad4cba95f2e213ad556fb8c2a391d3b55ba2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency logRelease files / hoermoles_ble-0.12.1-py3-none-any.whl
| Download URL | hoermoles_ble-0.12.1-py3-none-any.whl |
|---|---|
| Size | 57.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4422407a656cc0990379ed00cb238bac3c541261defc0ad77913619fced004b0
|
|
BLAKE2b-256 checksum How to use checksums |
7b57cdbeac8378b50399429d26e7f361c78e8dfbcdc4ca70324796c9874efba3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 21, 2026.
Transparency log