Protocol update adds FFE4 Status format: 7×PD sensors (little-endian)
+ VBAT battery voltage (mV) + XOR checksum = 17 bytes.
- parseVendorStatus: handles 1-byte heartbeat, 17-byte ADC, 33-byte params
- handleValueChange: ADC type extracts PD array + battery % from VBAT
- Battery calculated as linear 3000mV(0%) to 4200mV(100%)
- PD data passed through status event for treatment pages
- Updated protocol doc with full byte table and old/new comparison
Heartbeat (1-byte FFE4 notify) now emits 'heartbeat' event only.
Only 33-byte state responses emit 'status' with parsed IO data.
Fixes timer jumping to 0 every second during treatment.
- handleValueChange now calls parseVendorStatus and emits 'status'
with legacy-compatible shape (mode_state, battery, remaining_ms, etc.)
- 1-byte heartbeat mapped to mode_state, 33-byte state parsed for
active IO detection
- Fixes regression where treating, index, wear-check, auto-scan pages
never received status updates in vendor_33 mode
- Also emits 'vendor_raw' with full parsed data for future use
- discoverChars uses properties (write/notify) to distinguish FFE1
command characteristic from FFE1 service UUID
- handleValueChange: vendor_33 mode emits vendor_status (FFE4 heartbeat)
and vendor_data separately, no longer tries parseFrame on raw bytes
- parseVendorStatus handles 1-byte heartbeat and 33-byte state readback
with proper IO1-IO5 field parsing
- Add expire_time > NOW() filter to findActive() preventing stale subscriptions
- Add express-rate-limit on login endpoints (user: 10/15min, admin: 5/15min)
- Add production guard for default admin credentials
- Fix BLE bindDevice userId encoding (uint32 instead of hexToBytes on numeric)
- Wrap adminCreate in transaction to prevent race condition
- Add settings cache invalidation after admin saves
- Read trial_days from settings instead of hardcoding 7
- Fix double JSON.stringify in commandDao.finish call
- Cancel stale pending bindings before creating new ones
- Reduce token refresh grace period from 3 days to 1 day
- Fix subscribe-success to fetch expiry from server (correct for renewals)
- Add keep-alive name property to DashboardView and SettingsView
- Fix BLE disconnect() to preserve listener registrations across reconnects