You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Repurposed 2026-06-20. This issue originally proposed a native BLE-GATT
dial backend to work around a BlueZ HID-over-GATT (HoG) attach failure. That
failure is no longer reproducible — on current hardware BlueZ bridges the
Ulanzi Dial cleanly (it creates /dev/input/event* nodes with a valid
keyboard descriptor), most likely fixed by a BlueZ/kernel update since the
original report (2026-06-14). With HoG working, the real remaining blocker
turned out to be evdev access + the disabled enable-toggle, which is what
this issue now tracks. The original BLE-GATT diagnosis is preserved at the
bottom as a contingency record.
Problem
On Linux the Ulanzi Dial shows "disconnected" in the mapper even though the
device is paired, connected, and presenting input nodes. Two compounding causes,
both verified on hardware:
The evdev node isn't accessible. BlueZ HoG creates the dial's /dev/input/event* nodes as root:input 0660. A normal AetherSDR process
(not root, not in the input group, no uaccess ACL) cannot open()
them. EvdevEncoderManager::findMatchingDevice() hit EACCES and silently continued — so even with the feature enabled and the name
matching exactly, the dial read as disconnected with no diagnostic.
Verified on hardware
BlueZ HoG bridges fine: the dial enumerates as Ulanzi Dial Keyboard
(event25) + Mouse + two Ulanzi Dial nodes (HID 0005:FFF1:0082).
It emits exactly the keycodes the decoder expects: rotation = KEY_VOLUMEUP/KEY_VOLUMEDOWN, buttons = KEY_PLAYPAUSE/MUTE/ PREVIOUSSONG/NEXTSONG, chords = Ctrl+Y/Z/V/C.
Before the fix: open("/dev/input/event25") → EACCES (user not in input,
no uaccess). After installing a uaccess udev rule: logind applies a user:<me>:rw ACL automatically on connect, open() succeeds, and turning
the dial tunes the radio (source=ulanzi-dial intent=IncrementalTune).
Fix
Linux-specific (this is a Linux-specific access defect — the cross-platform
exception; Windows/macOS HID paths unchanged):
Detect a present-but-inaccessible dial.EvdevEncoderManager now reads the
device name from sysfs (/sys/class/input/event*/device/name, always
world-readable), so it recognizes the dial even when it can't open the node.
On EACCES it emits a new accessRequired(name) signal and logs a clear
warning instead of failing silently.
One-click, on-demand access grant. When accessRequired fires, the Ulanzi
Dial mapper shows a "Grant access" button that installs a uaccess udev
rule via polkit/pkexec (KDE/GNOME auth prompt), reloads udev, and
re-scans. Gated on actual device detection + user consent — nothing is
installed on machines that don't have the dial.
packaging/linux/70-ulanzi-dial.rules — the canonical rule, committed as a
reference and for distro packagers who prefer to ship it unconditionally; it
is not installed by the build.
Principle XI (demonstrate) — verified live: dial detected → permission
granted → rotation tunes the radio.
Related
ULANZI H100 DIAL NO CONNECT #3670 — Windows H100 "no connect" is a different root cause (the Windows
matcher hardcodes the product string "Ulanzi Dial"; the H100 may report a
different name). Separate matcher-broadening task, still awaiting the
reporter's exact device string.
Original BLE-GATT-backend diagnosis (2026-06-14) — kept as a contingency record
The original premise was that BlueZ HoG failed to attach (input-hog profile accept failed), so no /dev/input/event* node was created and a native
BLE-GATT backend was needed to read the dial directly over org.bluez GATT.
That HoG-attach failure is not reproducible on current hardware. If it recurs on
other setups (older BlueZ, different dial firmware), the BLE-GATT-backend
approach below is the fallback — the HID data is fully reachable over GATT.
Proposed approach: BleHidDialManager (Linux only) via QtDBus → org.bluez
GATT; StartNotify on the report characteristics and decode per the descriptor,
emitting the same encoder/button signals as EvdevEncoderManager; aggregate so
the evdev backend is preferred and BLE-GATT is the fallback when HoG fails.
Problem
On Linux the Ulanzi Dial shows "disconnected" in the mapper even though the
device is paired, connected, and presenting input nodes. Two compounding causes,
both verified on hardware:
UlanziDialEnableddefaults toFalse(intentionally, to avoid the macOS Input-Monitoring prompt — privacy(hid): UlanziDialMacOSManager unconditionally requests Input Monitoring permission on every macOS launch #3257), so the
Linux backend never even scans unless the user ticks Settings → Serial →
USB Control Surfaces → "Enable Ulanzi Dial".
/dev/input/event*nodes asroot:input 0660. A normal AetherSDR process(not root, not in the
inputgroup, nouaccessACL) cannotopen()them.
EvdevEncoderManager::findMatchingDevice()hitEACCESandsilently
continued — so even with the feature enabled and the namematching exactly, the dial read as disconnected with no diagnostic.
Verified on hardware
Ulanzi Dial Keyboard(event25) + Mouse + two
Ulanzi Dialnodes (HID0005:FFF1:0082).KEY_VOLUMEUP/KEY_VOLUMEDOWN, buttons =KEY_PLAYPAUSE/MUTE/PREVIOUSSONG/NEXTSONG, chords =Ctrl+Y/Z/V/C.open("/dev/input/event25")→EACCES(user not ininput,no
uaccess). After installing auaccessudev rule: logind applies auser:<me>:rwACL automatically on connect,open()succeeds, and turningthe dial tunes the radio (
source=ulanzi-dial intent=IncrementalTune).Fix
Linux-specific (this is a Linux-specific access defect — the cross-platform
exception; Windows/macOS HID paths unchanged):
EvdevEncoderManagernow reads thedevice name from sysfs (
/sys/class/input/event*/device/name, alwaysworld-readable), so it recognizes the dial even when it can't open the node.
On
EACCESit emits a newaccessRequired(name)signal and logs a clearwarning instead of failing silently.
accessRequiredfires, the UlanziDial mapper shows a "Grant access" button that installs a
uaccessudevrule via polkit/pkexec (KDE/GNOME auth prompt), reloads udev, and
re-scans. Gated on actual device detection + user consent — nothing is
installed on machines that don't have the dial.
packaging/linux/70-ulanzi-dial.rules— the canonical rule, committed as areference and for distro packagers who prefer to ship it unconditionally; it
is not installed by the build.
Constitution
evdev access defect); Windows/macOS unchanged.
granted → rotation tunes the radio.
Related
matcher hardcodes the product string
"Ulanzi Dial"; the H100 may report adifferent name). Separate matcher-broadening task, still awaiting the
reporter's exact device string.
Original BLE-GATT-backend diagnosis (2026-06-14) — kept as a contingency record
The original premise was that BlueZ HoG failed to attach (
input-hog profile accept failed), so no/dev/input/event*node was created and a nativeBLE-GATT backend was needed to read the dial directly over
org.bluezGATT.That HoG-attach failure is not reproducible on current hardware. If it recurs on
other setups (older BlueZ, different dial firmware), the BLE-GATT-backend
approach below is the fallback — the HID data is fully reachable over GATT.
Captured HID Report Map (RID 1 = Keyboard, RID 2 = Consumer, RID 3 = Mouse,
RID 251/252 = vendor 64-byte reports):
Proposed approach:
BleHidDialManager(Linux only) via QtDBus →org.bluezGATT;
StartNotifyon the report characteristics and decode per the descriptor,emitting the same encoder/button signals as
EvdevEncoderManager; aggregate sothe evdev backend is preferred and BLE-GATT is the fallback when HoG fails.