Skip to content

Releases: DerekSeaman/irk-capture

v1.5.8

21 Feb 23:45

Choose a tag to compare

Release Notes v1.5.8

This release note reflects changes from v1.5.7 to v1.5.8.

Bug Fix

  • Fixed spurious encryption timeout on first connect in retry_security_if_needed(): When sec_init_time_ms_ was 0 (first connection since boot/reset), the local snapshot sec_init_time_copy was left as 0 after setting sec_init_time_ms_ = now. The subsequent timeout check (now - sec_init_time_copy) > SEC_TIMEOUT_MS evaluated as (uptime_ms - 0), which immediately exceeded SEC_TIMEOUT_MS (20 000 ms) on any device that had been running longer than 20 seconds. This caused every first pairing attempt to be aborted with a spurious "Encryption timeout" disconnect before the peer could respond. Fixed by also updating sec_init_time_copy = now after initializing sec_init_time_ms_.

Files Updated

  • components/irk_capture/irk_capture.cpp
  • ESPHome Devices/irk-capture-base.yaml
  • ESPHome Devices/irk-capture-full.yaml
  • RELEASE_NOTES_v1.5.8.md

v1.5.7

18 Feb 00:59

Choose a tag to compare

Release Notes v1.5.7

Boot Stability and GATT Reliability

  • Fixed Keyboard profile boot crash: The Keyboard BLE profile was causing GATT registration to fail at boot because NimBLE rejects HID services with no characteristics. Added a minimal HID Protocol Mode characteristic (0x2A4E) to make the HID service structurally valid.
  • Added Keyboard→Heart Sensor GATT fallback: If Keyboard GATT registration fails, the component now automatically falls back to the Heart Sensor profile instead of hard-failing. The fallback is persisted to NVS so subsequent boots avoid the same failure path.
  • Graceful no-GATT continuation: If all GATT profile registrations fail, the component continues operating without custom GATT services rather than calling mark_failed(), preventing unnecessary boot failures.
  • Relaxed non-fatal boot checks: NVS health-check failures and ble_store_config_init() failures are now non-fatal warnings rather than hard stops, improving resilience on flash-stressed hardware.
  • Setup flow hardening: setup() exits early if setup_ble() marks the component failed, preventing entity/UI initialization on a broken BLE stack.
  • Explicitly zero GATT handle globals before registration so handle state is predictable regardless of which profile or fallback path is taken.

Thread Safety and Concurrency

  • Serialized BLE control operations: Added a dedicated BLE operation mutex (ble_op_mutex_) with RAII guard (BleOpGuard) to serialize sensitive NimBLE calls (advertising start/stop, device name updates, MAC rotation, connection termination) across the ESPHome main loop and NimBLE callback contexts.
  • Fixed sec_retry_done_ race condition: The security retry flag is now written under mutex before calling ble_gap_security_initiate(), closing a window where a NimBLE callback could observe the stale false value and trigger a double retry.
  • Fixed irk_last_try_ms_ first-poll bypass: The IRK poll interval guard was bypassed on the first poll because the timestamp initialized to 0. It is now set to now_ms() alongside enc_ready_ when encryption completes.
  • Fixed mac_rotation_retries_ and mac_rotation_ready_time_ unprotected writes: These members are now reset inside existing MutexGuard blocks, consistent with the documented threading model.
  • Made getters thread-safe: get_ble_profile() and get_ble_name() now acquire state_mutex_ before reading shared state.
  • Fixed host_synced_ cross-core visibility: Uses std::atomic<bool> to ensure the NimBLE task's write is visible to the ESPHome main loop on dual-core ESP32 variants.

Correctness Fixes

  • Fixed duplicate Home Assistant state events on auto-stop: publish_and_log_irk() now checks is_advertising() before calling stop_advertising(), preventing a double call and double publish_state(false) when the disconnect handler has already stopped advertising.
  • Fixed IRK re-publish deduplication bypass: The reconnect path in on_connect() previously called publish_irk_to_sensors() directly, bypassing the 60-second rate limit, deduplication cache, and capture counter. It now routes through publish_and_log_irk().
  • Fixed BLE name length silent truncation: The name_len field is now clamped to 29 bytes with a warning log before the uint8_t cast.
  • Fixed commit_err misleading log: NVS set and commit errors are now tracked with separate variables so the log accurately reports which operation failed.
  • Advertising switch state now reflects reality: The advertising switch publishes the actual runtime state after start/stop attempts rather than echoing only the requested state.
  • NVS profile range validation: Persisted BLE profile values are range-checked before static_cast, preventing undefined behavior from corrupted NVS data.
  • BLE name whitespace trimming: sanitize_ble_name() trims leading and trailing spaces after character filtering.

Observability and Diagnostics

  • Diagnostic log on spurious double-disconnect: on_disconnect() now logs at DEBUG level if called while already disconnected, making NimBLE duplicate events visible in serial output.
  • Diagnostic log on L2CAP parameter update acceptance: Logs at DEBUG level when a peer-requested connection parameter update is accepted.
  • Diagnostic log on Keyboard→Heart Sensor fallback: Logs handle values after fallback registration to clarify GATT state in diagnostics.
  • Hardened NVS initialization logging: Added strict return-code checks and logging for nvs_flash_init(), erase/re-init paths, and NVS health-check operations.
  • Consistent failure logging: ble_store_config_init(), boot-time ble_store_clear(), ble_svc_gap_device_name_set(), and all terminate/start/stop paths now log failures consistently.

Code Quality

  • Removed dead code: Unused helper read_peer_bond_by_conn(), unused constant IRK_MIN_POLL_INTERVAL_MS, unused local adv_success.
  • Replaced magic GAP event numbers with named constants for readability and safer maintenance across ESP-IDF/NimBLE SDK variants.
  • Added explicit standard library includes: Added <string>, <vector> to header and <cstring> to implementation, removing reliance on transitive includes.

Files Updated

  • components/irk_capture/irk_capture.cpp
  • components/irk_capture/irk_capture.h
  • ESPHome Devices/irk-capture-base.yaml
  • ESPHome Devices/irk-capture-full.yaml
  • ESPHome Devices/irk-capture-device-remote.yaml
  • RELEASE_NOTES_v1.5.7.md (this file; replaces RELEASE_NOTES_v1.5.6.md)

v1.5.6

06 Feb 02:15

Choose a tag to compare

Reliability Fixes

  • Fixed MAC rotation race condition: refresh_mac() now sets the suppress_next_adv_ flag before triggering disconnect, preventing handle_gap_disconnect from restarting advertising while the MAC rotation is still in progress.
  • Component fails cleanly on critical init errors: setup() now calls mark_failed() and returns early if the FreeRTOS mutex cannot be created, and register_gatt_services() marks the component as failed if GATT registration fails.
  • NVS profile range validation: Persisted BLE profile values are now range-checked before static_cast, preventing undefined behavior from corrupted NVS data.
  • BLE name whitespace trimming: sanitize_ble_name() now trims leading and trailing spaces after character filtering, preventing names like " IRK " from wasting bytes in the advertising packet.
  • Fixed host_synced_ cross-core visibility: Changed from plain bool to std::atomic<bool> to ensure the NimBLE task's write is visible to the ESPHome main loop on dual-core ESP32 variants.

Code Quality

  • Added explicit standard library includes: Added <string>, <vector> to header and <cstring> to implementation, removing reliance on transitive includes from ESPHome/NimBLE headers.

v1.5.5

05 Feb 23:14

Choose a tag to compare

Bug Fixes

  • Fixed thread safety race conditions: Six pairing/polling state fields were accessed across threads without mutex protection, risking missed IRK captures on dual-core ESP32 variants.
  • Fixed 32-bit timer overflow: Absolute timer comparisons would fail after ~49.5 days of uptime. All timer checks now use wraparound-safe arithmetic.
  • Fixed post-disconnect IRK lookup: Used the event's embedded connection descriptor instead of ble_gap_conn_find, which could fail after NimBLE removed the descriptor.
  • Fixed config defaults mismatch: Python schema defaults now match the intended C++ behavior (continuous_mode=true, max_captures=10).
  • Fixed BLE name validation: Compile-time limit reduced from 29 to 12 bytes to match the runtime Samsung S24/S25 compatibility constraint.

Improvements

  • IRK cache size now scales with max_captures setting instead of being hardcoded to 10.
  • Replaced esp_restart() with App.safe_reboot() to avoid watchdog timeouts on profile change.
  • Consolidated dump_config output into a single log line, removing vTaskDelay workarounds.
  • Replaced magic number for NimBLE DHKey error with a named constant.
  • Simplified is_valid_irk, MAC generation, and NVS health check logic.
  • Added input validation for BLE profile select to reject invalid values.
  • Removed unused on_auth_complete method and to_hex_fwd dead code.

v1.5.4

18 Jan 00:54

Choose a tag to compare

Bug Fixes

  • Fixed BLE advertising not starting on boot: Resolved an issue where BLE advertising would not automatically start when start_on_boot: true was configured. The advertising initialization now correctly waits for the NimBLE host to sync before starting.

  • Fixed rc=21 (BLE_HS_EALREADY) error: Added defensive handling to prevent advertising start failures when the BLE GAP was in a transitional state.

Documentation

  • Various documentation updates including improved installation instructions, ESP32 variant reference images, and browser compatibility notes for serial flashing.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com

v1.5.3

11 Jan 16:20

Choose a tag to compare

v1.5.3 - Documentation Improvements & Remote Installation Option

This release adds a new remote installation method and improves documentation clarity.

New Features

Remote Installation Option (Recommended)

  • Added new Option 1 - Remote installation method that pulls both YAML package and IRK Capture component directly from GitHub
  • No local file downloads required - simplest installation method
  • Automatically gets latest version at build time
  • New template file: irk-capture-device-remote.yaml

Documentation Updates

Main README

  • Restructured installation section with three clear options:
    • Option 1 - Remote (Recommended): Pulls everything from GitHub
    • Option 2 - Local Package: Uses base YAML + device-specific YAML
    • Option 3 - Local Standalone: Single self-contained YAML file
  • Added step-by-step instructions for remote installation
  • Added clarification that IRK Capture cannot act as a Bluetooth proxy
  • Added note about Bermuda BLE Trilateration integration compatibility

ESPHome Devices README

  • Added documentation for new irk-capture-device-remote.yaml template
  • Updated "Which Configuration Should I Use" section with all three options
  • Clarified that standalone config still pulls component from GitHub at build time
  • Added note about accessing secrets via ESPHome Device Builder UI

Compatibility

  • ESP32 Variants: All (ESP32, C3, C6, S3, etc.)
  • Framework: ESP-IDF (required)
  • ESPHome: 2024.x or newer
  • Home Assistant: Optional (recommended for Private BLE Device integration)

Installation

See the README for detailed installation instructions.

For the simplest installation, use the new remote method:

external_components:
  - source:
      type: git
      url: https://github.com/DerekSeaman/irk-capture
      ref: v1.5.3
    components: [irk_capture]
    refresh: 1min

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

v1.5.2

11 Jan 04:43

Choose a tag to compare

Overview

v1.5.2 is a major update focused on Android device support and reliability improvements. This release introduces a new BLE profile system that enables IRK capture from Samsung Galaxy phones and other Android devices that previously needed workarounds.


New Features

BLE Profile Selector

A new dropdown in the ESPHome device page lets you switch between two BLE advertising profiles:

Profile Best For Advertised Name Service
Heart Sensor Apple devices (iPhone, iPad, Apple Watch), Android Watches Your configured name Heart Rate (0x180D)
Keyboard Android Phones (Samsung Galaxy, Pixel, etc.) "Logitech K380" HID (0x1812)

Why this matters: Samsung One UI 7 and other Android versions aggressively filter BLE devices in Bluetooth settings. The Keyboard profile advertises as a familiar Logitech keyboard, bypassing these filters and making the ESP32 visible for pairing.

Effective MAC Sensor

A new text sensor displays the current BLE MAC address being advertised by the ESP32.


Improvements

  • Thread Safety Overhaul - FreeRTOS mutex protection for all shared state
  • Non-Blocking MAC Rotation - Event-driven state machine, instant button response
  • Bond Table Management - Cleared on every boot for fresh pairing sessions
  • Memory Safety - IRK cache with hard cap of 10 entries and FIFO eviction
  • BLE Name Validation - 12-character limit enforced for Samsung compatibility
  • NimBLE Stack Optimization - Increased task stack size to 5KB

Bug Fixes

  • Fixed race condition in IRK cache causing potential heap corruption
  • Fixed deadlock when duplicate IRKs triggered advertising stop
  • Fixed zombie connection state where UI showed "connected" but no data flowed
  • Fixed heap fragmentation on extended capture sessions
  • Fixed pairing timeout handling with proper cooldown

Breaking Changes

None. All existing YAML configurations work without modification.


Tested Devices

Device OS Profile Status
iPhone iOS 26 Heart Sensor Working
Apple Watch watchOS 26 Heart Sensor Working
iPad iPadOS 26 Heart Sensor Working
Samsung Galaxy S25+ One UI 7 Keyboard Working
Samsung Galaxy Watch8 Classic Wear OS 6 Heart Sensor Working
Google Pixel 9 Android 15 Keyboard Working
Amazon Echo Show LineageOS 18.1 Heart Sensor Working

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

v1.5.1 - Android Watch Support

03 Jan 02:42

Choose a tag to compare

v1.5.1 - Android Watch Support

This release adds support for capturing IRKs from Android watches using the Gear Tracker II app.

New Features

Android Watch Support

  • Added new documentation section for capturing IRKs from Android watches
  • Gear Tracker II app enables pairing with heart rate sensor profile on watches that aggressively filter BLE devices
  • Added Samsung Galaxy Watch7 to tested devices

Bug Fixes

Configuration Fixes

  • Fixed invalid initial_option in select schema that caused ESPHome build failures
  • Updated external component URLs from irk-capture-bludroid to irk-capture

Documentation Updates

  • Consolidated Heart Sensor Profile description for Apple devices and Android watches
  • Renamed "Android devices" to "Android phones" where appropriate
  • Added troubleshooting section for Android watches
  • Added note about third-party app requirement for Android watches

Compatibility

  • ESP32 Variants: All (ESP32, C3, C6, S3, etc.)
  • Framework: ESP-IDF (required)
  • ESPHome: 2024.x or newer
  • Home Assistant: Optional (recommended for Private BLE Device integration)

Tested Devices

  • ✓ Apple devices (iPhone, Apple Watch, iPad) - iOS 18+
  • ✓ Android phones (Samsung Galaxy S25+, Google Pixel 9)
  • ✓ Samsung Galaxy Watch7

Installation

Use this in your ESPHome YAML:

external_components:
  - source:
      type: git
      url: https://github.com/DerekSeaman/irk-capture
      ref: v1.5.1
    components: [irk_capture]
    refresh: 1min

See the README for detailed installation instructions.


🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com

v1.5.0

31 Dec 14:23

Choose a tag to compare

IRK Capture v1.5.0 Release Notes

Overview

v1.5.0 is a major update focused on Android device support and reliability improvements. This release introduces a new BLE profile system that enables IRK capture from Samsung Galaxy phones and other Android devices that previously needed workarounds.


New Features

BLE Profile Selector

A new dropdown in the ESPHome device page lets you switch between two BLE advertising profiles:

Profile Best For Advertised Name Service
Heart Sensor Apple devices (iPhone, iPad, Apple Watch) Your configured name Heart Rate (0x180D)
Keyboard Android devices (Samsung Galaxy, Pixel, etc.) "Logitech K380" HID (0x1812)

Why this matters: Samsung One UI 7 and other Android versions aggressively filter BLE devices in Bluetooth settings. The Keyboard profile advertises as a familiar Logitech keyboard, bypassing these filters and making the ESP32 visible for pairing.

Usage:

  1. Select the profile matching your target device
  2. The ESP32 will automatically reboot to apply the new GATT services
  3. Wait ~30 seconds for the device to be ready (watch for the Effective MAC sensor to update)

Effective MAC Sensor

A new text sensor displays the current BLE MAC address being advertised by the ESP32. This helps you:

  • Verify MAC rotation is working after pressing "Generate New MAC"
  • Confirm the device is ready after a profile change or reboot
  • Troubleshoot pairing issues by confirming the advertised address

Improvements

Thread Safety Overhaul

The entire codebase has been refactored for production-grade thread safety on ESP32 single-core devices:

  • Added FreeRTOS mutex protection for all shared state between the NimBLE task and ESPHome main task
  • Implemented RAII MutexGuard class for exception-safe lock/unlock
  • Eliminated race conditions that could cause intermittent pairing failures

Non-Blocking MAC Rotation

The "Generate New MAC" button now uses an event-driven state machine instead of blocking delays:

  • Instant button response (no UI freeze)
  • Reliable MAC changes even during active connections
  • Automatic retry logic for transient errors

Bond Table Management

  • Bond table is now cleared on every boot, ensuring a fresh pairing session
  • Eliminates "phantom" pairing failures from stale bond data
  • Improves privacy by not persisting old IRKs across reboots

Memory Safety

  • IRK cache now has a hard cap of 10 entries with FIFO eviction
  • Pre-allocated vector capacity prevents heap fragmentation on ESP32-C3
  • In-place updates for duplicate IRKs (no memory bloat from reconnecting devices)

BLE Name Validation

  • Runtime sanitization of BLE device names from Home Assistant
  • Enforces 12-character limit for Samsung compatibility
  • Automatic fallback to "IRK Capture" for invalid input

NimBLE Stack Optimization

  • Increased NimBLE task stack size from 4KB to 5KB for logging safety
  • Prevents stack overflow during debug logging in GAP event callbacks

Bug Fixes

  • Fixed race condition in IRK cache causing potential heap corruption during rapid reconnections
  • Fixed deadlock when duplicate IRKs triggered advertising stop
  • Fixed zombie connection state where the UI showed "connected" but no data flowed
  • Fixed heap fragmentation on extended capture sessions with pre-allocated vectors
  • Fixed pairing timeout handling with proper cooldown to prevent rapid-fire reconnection loops

Breaking Changes

None. All existing YAML configurations work without modification.

Behavioral Changes

  1. Bond table cleared on boot - Devices must re-pair after ESP32 reboot. This is intentional for reliability.
  2. Profile change triggers reboot - Switching between Heart Sensor and Keyboard profiles requires a reboot to update GATT services (NimBLE limitation).

Tested Devices

Device OS Profile Status
iPhone iOS 26 Heart Sensor Working
Apple Watch watchOS 26 Heart Sensor Working
iPad iPadOS 26 Heart Sensor Working
Samsung Galaxy S25+ One UI 7 Keyboard Working
Samsung Galaxy Watch8 Classic Wear OS 6 Both Not Working
Google Pixel 9 Android 15 Keyboard Working
Amazon Echo Show LineageOS 18.1 Heart Sensor Working

Migration Guide

From v1.4.x

Add the new optional entities to your configuration (if desired):

select:
  - platform: irk_capture
    irk_capture_id: irk
    ble_profile:
      id: ble_profile_select
      name: "BLE Profile"

text_sensor:
  - platform: irk_capture
    irk_capture_id: irk
    effective_mac:
      name: "Effective MAC"

Then compile, flash, and power cycle your ESP32.

v1.4.5 - CI/CD Linting Fixes & README Badge

25 Dec 16:10

Choose a tag to compare

v1.4.5 - CI/CD Linting Fixes & README Badge

This maintenance release fixes CI/CD linting compatibility issues and adds a status badge to the README. No functional changes to IRK capture implementation.

Changes

Linting Status Badge

  • Added GitHub Actions linting workflow status badge to README
  • Badge displays real-time code quality check status
  • Green badge indicates all linting checks pass

Black Formatter Fixes

  • Fixed Python code formatting to match CI/CD Black configuration
  • Changed from line length 100 to Black default (88 characters)
  • Updated pre-commit hook to match GitHub Actions settings
  • All Python files now pass Black formatter validation

Files Fixed

  • button.py - Multi-line class declaration formatting
  • switch.py - Multi-line class declaration formatting
  • text_sensor.py - Multi-line schema formatting
  • Pre-commit hook updated for consistency

Technical Details

The pre-commit hook was using --line-length 100 while the GitHub Actions workflow used Black's default line length (88). This caused CI/CD failures even though code was formatted locally. This release aligns both configurations.

CI/CD Status: All linting checks now pass

  • ✅ yamllint - YAML configuration validation
  • ✅ clang-format - C++ code formatting
  • ✅ Black - Python code formatting

Compatibility

  • ESP32 Variants: All (ESP32, C3, C6, S3, etc.)
  • Framework: ESP-IDF (required)
  • ESPHome: 2024.x or newer
  • Home Assistant: Optional (recommended for Private BLE Device integration)

Tested Devices

  • ✓ Apple devices (iPhone, Apple Watch, iPad) - iOS 18+
  • ✓ Android devices (LineageOS, standard Android)

Installation

See the README for detailed installation instructions.

Use this in your ESPHome YAML:

external_components:
  - source:
      type: git
      url: https://github.com/DerekSeaman/irk-capture
      ref: v1.4.5
    components: [irk_capture]
    refresh: 1min

irk_capture:
  id: irk
  ble_name: "IRK Capture"
  start_on_boot: true

Commits Since v1.4.4

  • Fix Black formatting to match CI/CD default line length
  • Apply Black formatter to fix CI/CD linting errors
  • Add linting status badge to README

Upgrade Notes

If upgrading from v1.4.4 or earlier, simply update the ref: line in your ESPHome YAML configuration to v1.4.5. This is a drop-in replacement with no configuration changes required.


🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com