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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hoermoles_ble-0.5.0.tar.gz.
File metadata
- Download URL: hoermoles_ble-0.5.0.tar.gz
- Upload date:
- Size: 59.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0766414e3b7b4970623b07943cb98f365a9f4fc7614298487bd7324e5c5ea3c
|
|
| MD5 |
8eed610ba100adae8e6914f703c0397e
|
|
| BLAKE2b-256 |
0a2bcf6fa3d58b2205f106720a25cb2d4bd6dc6f60bb586fd3065aa7dd8e63d5
|
Provenance
The following attestation bundles were made for hoermoles_ble-0.5.0.tar.gz:
Publisher:
pypi-publish.yml on the78mole/hoermoles-ble
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hoermoles_ble-0.5.0.tar.gz -
Subject digest:
f0766414e3b7b4970623b07943cb98f365a9f4fc7614298487bd7324e5c5ea3c - Sigstore transparency entry: 2235873563
- Sigstore integration time:
-
Permalink:
the78mole/hoermoles-ble@d34392e2effaeec3b4af2c948268a35f3863b8b0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the78mole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@d34392e2effaeec3b4af2c948268a35f3863b8b0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hoermoles_ble-0.5.0-py3-none-any.whl.
File metadata
- Download URL: hoermoles_ble-0.5.0-py3-none-any.whl
- Upload date:
- Size: 53.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d180e804f758fdabcaecbb19612aad406f20b5a7f4d2eb208ce89182f093d12
|
|
| MD5 |
ea6a5e64b4873db27e88dc98f4370678
|
|
| BLAKE2b-256 |
46c5eeada2cdc264b902e62cc2902b8f230bf034d87a439d4332c06cd2131181
|
Provenance
The following attestation bundles were made for hoermoles_ble-0.5.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on the78mole/hoermoles-ble
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hoermoles_ble-0.5.0-py3-none-any.whl -
Subject digest:
1d180e804f758fdabcaecbb19612aad406f20b5a7f4d2eb208ce89182f093d12 - Sigstore transparency entry: 2235874189
- Sigstore integration time:
-
Permalink:
the78mole/hoermoles-ble@d34392e2effaeec3b4af2c948268a35f3863b8b0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the78mole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@d34392e2effaeec3b4af2c948268a35f3863b8b0 -
Trigger Event:
push
-
Statement type: