Skip to content

Commit b5287dd

Browse files
committed
Fix BLE bonding persistence and add aggressive power management
- Add encrypted flags to HID characteristics to force pairing - Remove broken nimble_bond clearing code - Simplify keystore to exact-key matching only (RPA devices need re-pair) - Reduce sleep timeouts: 30s idle → light sleep, 1min → deep sleep - Update README with new power management timings
1 parent 0b1b9a3 commit b5287dd

File tree

5 files changed

+26
-27
lines changed

5 files changed

+26
-27
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ Hold **Shortcut1 + Shortcut3** for 10 seconds.
105105

106106
## Power Management
107107

108-
- **5 minutes** idle → Light sleep (BLE stays active)
109-
- **1 hour** of light sleep → Deep sleep
108+
- **30 seconds** idle → Light sleep (BLE stays active)
109+
- **1 minute** of light sleep → Deep sleep
110110
- **Wake**: Any button press or motion (if MPU6050 installed)
111111

112112
## Build

modules/ha_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def _send_discovery(self):
158158
("shortcut_1", "Shortcut 1"),
159159
("shortcut_2", "Shortcut 2"),
160160
("shortcut_3", "Shortcut 3"),
161-
("shortcut_4", "Shortcut 4"),
161+
("shortcut_4", "Shortcut 4"), # GPIO33 shared with I2C at boot
162162
("brightness_up", "Brightness Up"),
163163
("brightness_down", "Brightness Down"),
164164
]

modules/hid_keystores.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@ def get_secret(self, sec_type, index, key):
2828
value = None
2929

3030
if key is None:
31+
# Lookup by index
3132
i = 0
3233
for (t, _k), _val in self.secrets.items():
3334
if t == sec_type:
3435
if i == index:
3536
value = _val
37+
break
3638
i += 1
3739
else:
40+
# Lookup by exact key only - no fallback
41+
# RPA devices will need to re-pair after address change
3842
value = self.secrets.get(_key, None)
3943

4044
return value

modules/hid_services.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
F_READ_WRITE_NORESPONSE = bluetooth.FLAG_READ | bluetooth.FLAG_WRITE | bluetooth.FLAG_WRITE_NO_RESPONSE
1919
F_READ_WRITE_NOTIFY_NORESPONSE = bluetooth.FLAG_READ | bluetooth.FLAG_WRITE | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_WRITE_NO_RESPONSE
2020

21+
# Security flags for encrypted characteristics (require pairing)
22+
_FLAG_READ_ENCRYPTED = const(0x0200)
23+
_FLAG_WRITE_ENCRYPTED = const(0x1000)
24+
F_READ_ENC = F_READ | _FLAG_READ_ENCRYPTED
25+
F_READ_NOTIFY_ENC = F_READ_NOTIFY | _FLAG_READ_ENCRYPTED
26+
F_READ_WRITE_ENC = F_READ_WRITE | _FLAG_READ_ENCRYPTED | _FLAG_WRITE_ENCRYPTED
27+
F_READ_WRITE_NORESPONSE_ENC = F_READ_WRITE_NORESPONSE | _FLAG_READ_ENCRYPTED | _FLAG_WRITE_ENCRYPTED
28+
F_READ_WRITE_NOTIFY_NORESPONSE_ENC = F_READ_WRITE_NOTIFY_NORESPONSE | _FLAG_READ_ENCRYPTED | _FLAG_WRITE_ENCRYPTED
29+
2130
DSC_F_READ = const(0x02)
2231

2332
# Advertising types
@@ -244,20 +253,6 @@ def ble_irq(self, event, data):
244253

245254
def start(self):
246255
if self.device_state == self.DEVICE_STOPPED:
247-
# Clear NimBLE's internal bonding state to prevent IRK conflicts
248-
# This ensures Python keystore is the sole source of bonding data
249-
# Without this, deep sleep causes "failed to configure restored IRK" errors
250-
try:
251-
nvs = esp32.NVS("nimble_bond")
252-
for key in ["cccd", "peer_id", "our_id", "peer_sec", "our_sec"]:
253-
try:
254-
nvs.erase_key(key)
255-
except:
256-
pass
257-
nvs.commit()
258-
except:
259-
pass # Namespace may not exist on first boot
260-
261256
self.secrets.load_secrets()
262257
self._ble.irq(self.ble_irq)
263258
self._ble.active(True)
@@ -355,19 +350,19 @@ def __init__(self, name="BLE Keyboard"):
355350
self.HIDS = (
356351
UUID(0x1812), # Human Interface Device
357352
(
358-
(UUID(0x2A4A), F_READ), # HID Information
359-
(UUID(0x2A4B), F_READ), # Report Map
360-
(UUID(0x2A4C), F_READ_WRITE_NORESPONSE), # Control Point
361-
(UUID(0x2A4D), F_READ_NOTIFY, ( # Input Report (Keyboard)
353+
(UUID(0x2A4A), F_READ_ENC), # HID Information
354+
(UUID(0x2A4B), F_READ_ENC), # Report Map
355+
(UUID(0x2A4C), F_READ_WRITE_NORESPONSE_ENC), # Control Point
356+
(UUID(0x2A4D), F_READ_NOTIFY_ENC, ( # Input Report (Keyboard)
362357
(UUID(0x2908), DSC_F_READ), # Report Reference
363358
)),
364-
(UUID(0x2A4D), F_READ_WRITE_NOTIFY_NORESPONSE, ( # Output Report
359+
(UUID(0x2A4D), F_READ_WRITE_NOTIFY_NORESPONSE_ENC, ( # Output Report
365360
(UUID(0x2908), DSC_F_READ),
366361
)),
367-
(UUID(0x2A4D), F_READ_NOTIFY, ( # Input Report (Consumer)
362+
(UUID(0x2A4D), F_READ_NOTIFY_ENC, ( # Input Report (Consumer)
368363
(UUID(0x2908), DSC_F_READ), # Report Reference
369364
)),
370-
(UUID(0x2A4E), F_READ_WRITE_NORESPONSE), # Protocol Mode
365+
(UUID(0x2A4E), F_READ_WRITE_NORESPONSE_ENC), # Protocol Mode
371366
),
372367
)
373368

modules/shield_remote.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@
6464
]
6565

6666
# Power management settings
67-
IDLE_TIMEOUT_CONNECTED_MS = 5 * 60 * 1000 # 5 minutes before light sleep
68-
IDLE_TIMEOUT_DISCONNECTED_MS = 5 * 60 * 1000 # 5 minutes before light sleep
67+
IDLE_TIMEOUT_CONNECTED_MS = 30 * 1000 # 30 seconds before light sleep
68+
IDLE_TIMEOUT_DISCONNECTED_MS = 30 * 1000 # 30 seconds before light sleep
6969
LIGHT_SLEEP_MS = 50 # Light sleep interval between polls
70-
DEEP_SLEEP_AFTER_MS = 60 * 60 * 1000 # 1 hour of light sleep before deep sleep
70+
DEEP_SLEEP_AFTER_MS = 60 * 1000 # 1 minute of light sleep before deep sleep
7171

7272
# HID key codes (USB HID Keyboard Usage Tables - Page 0x07)
7373
KEY_UP = 0x52

0 commit comments

Comments
 (0)