Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
0ed1602
feat(manager): add AUTO scanning mode with on-demand active windows
bdraco May 22, 2026
d71c8ef
fix(auto): address review feedback on scheduler and HaScanner stop
bdraco May 22, 2026
cfec48f
perf(auto): cythonize AutoScanScheduler so on_advertisement is a C call
bdraco May 22, 2026
6c0f3a0
perf(auto): annotate on_advertisement locals and cdef _matches
bdraco May 22, 2026
7ab17b8
feat(manager): replace BleakCallback kwargs with async_register_activ…
bdraco May 22, 2026
7797260
refactor(auto): drop service_uuid, simplify to address-only API
bdraco May 22, 2026
d54f0b4
fix(auto): detect ACTIVE fallback to PASSIVE in async_request_active_…
bdraco May 22, 2026
4d3f3ba
fix(auto): back off failed sweeps and hoist reschedule out of hot loop
bdraco May 22, 2026
056e2d1
refactor(auto): tighten cold paths and close active-window state races
bdraco May 22, 2026
b4e25aa
refactor(auto): 12h sweep interval, 15s duration, 10min initial delay
bdraco May 22, 2026
68b6515
refactor(auto): one persistent worker task per scanner instead of per…
bdraco May 22, 2026
e055ce5
fix(auto): break circular cimport triangle that broke macOS CI
bdraco May 22, 2026
e169359
fix(manager): defer channels.bluez import to break macOS Cython init …
bdraco May 22, 2026
0eb280c
refactor(auto): restore typed BaseHaScanner cimport in scheduler pxd
bdraco May 22, 2026
3540555
fix(auto): re-drop BaseHaScanner cimport to break 3-way cycle on macOS
bdraco May 22, 2026
251518c
fix(manager): drop AutoScanScheduler cimport to settle macOS Cython init
bdraco May 22, 2026
8c6f1cf
fix(auto): restore cimport via mutual cycle to recover hot-path dispatch
bdraco May 22, 2026
fc21c43
fix(auto): drop the BluetoothManager cimport from auto_scheduler.pxd
bdraco May 22, 2026
1d2c995
docs(auto): trim verbose comments and docstrings
bdraco May 22, 2026
da09455
perf(auto): type const aliases and drop useless cancellederror re-raise
bdraco May 22, 2026
2547285
fix(__init__): import manager before base_scanner to settle macOS Cyt…
bdraco May 22, 2026
807c853
fix(auto): address review comments and DRY scanner restart pattern
bdraco May 22, 2026
da771a0
perf(manager): keep typed on_advertisement dispatch via cython.locals…
bdraco May 22, 2026
4e467b9
test(auto): cover new AUTO scheduler / scanner code paths
bdraco May 22, 2026
ee07cf2
test(auto): drive auto_scheduler coverage to 100% and expand coalesce…
bdraco May 22, 2026
d419a9b
test(scanner): cover BaseHaScanner default + HaScanner end-window edg…
bdraco May 22, 2026
9069dc9
feat(auto): add is_tracking, mark_due, mark_sweep_due, async_tick APIs
bdraco May 22, 2026
d95c599
Revert "feat(auto): add is_tracking, mark_due, mark_sweep_due, async_…
bdraco May 22, 2026
491cc94
refactor(auto): use manager public accessors instead of _all_history/…
bdraco May 22, 2026
5ed7d6d
Merge remote-tracking branch 'origin/main' into auto-scan-mode
bdraco May 22, 2026
8be974c
refactor(auto): merge per-device and sweep dispatch into a single _tick
bdraco May 22, 2026
f376b8d
perf(auto): promote _ScannerWorker to a cdef class with cpdef helpers
bdraco May 22, 2026
95a95ec
refactor(auto): pass manager directly into _ScannerWorker
bdraco May 22, 2026
5e7cee6
refactor(auto): drop the global sweep lock and stagger first sweeps
bdraco May 22, 2026
4909024
refactor(auto): inline the _run_window wrapper into _tick
bdraco May 22, 2026
a30e0ef
feat(auto): kick-start tracking at registration time
bdraco May 22, 2026
f01f9de
docs(auto): expand module docstring with a flow diagram and invariants
bdraco May 22, 2026
91be515
test(auto): cover 4-scanner ownership, add_request edges, _run loop
bdraco May 22, 2026
4531049
fix(auto): advance next-due before the window await to close owner-fl…
bdraco May 22, 2026
2c2a58e
fix(scanner): skip restart in async_request_active_window if still AC…
bdraco May 22, 2026
afeecef
feat(manager): default scan_interval/scan_duration on async_register_…
bdraco May 22, 2026
06ef79a
feat(manager): bump DEFAULT_ACTIVE_SCAN_INTERVAL to 5 minutes
bdraco May 22, 2026
0c6be07
fix(auto): address remaining bluetoothbot review items
bdraco May 22, 2026
c421373
Merge branch 'main' into auto-scan-mode
bdraco May 22, 2026
dd1520e
docs(auto): note _next_event_at O(M) cost is intentional at HA scale
bdraco May 22, 2026
84e1e06
Merge branch 'main' into auto-scan-mode
bdraco May 22, 2026
2c57faf
fix(auto): wake new owner's worker when a device migrates between sca…
bdraco May 22, 2026
7428a88
docs(auto): update scheduler diagram and add a Migration section
bdraco May 22, 2026
472d953
docs(auto): clean up stale comments and tighten edge cases
bdraco May 22, 2026
4636739
fix(auto): only wake the owner's worker when add_request actually ins…
bdraco May 22, 2026
cf00c17
fix(auto): address bot suggestions on slots, stagger, and timer drift
bdraco May 22, 2026
06c91a4
test(scanner): lock in active-window timer drift fix
bdraco May 22, 2026
6e4220c
Merge remote-tracking branch 'origin/main' into auto-scan-mode
bdraco May 22, 2026
23ab2df
fix(auto): measure scan_interval between window starts, not after win…
bdraco May 22, 2026
64e1e6c
fix(scanner): cancel pending timer in _arm_active_window_timer, defla…
bdraco May 22, 2026
ffddf59
fix(scanner): AUTO mode passive args + spurious fallback warning + do…
bdraco May 22, 2026
21b69eb
fix(auto): normalize address case and skip seed-then-prune kick-start
bdraco May 22, 2026
ced1404
Merge remote-tracking branch 'origin/main' into auto-scan-mode
bdraco May 22, 2026
7d71e83
fix(auto): post-merge review polish
bdraco May 22, 2026
719d92c
fix(auto): post-review polish - reset failure flag, restore eager import
bdraco May 22, 2026
e8ccd76
fix(auto): drop stale 'added' local from add_request pxd declaration
bdraco May 22, 2026
5d127bc
fix(auto): make AutoScanScheduler.start() truly idempotent
bdraco May 22, 2026
3a5f9ad
test(auto): cover all branches of start() replay loop
bdraco May 22, 2026
b70e8cf
fix(auto): deflake replay test, document scan_duration clamp, guard a…
bdraco May 22, 2026
8ef7014
fix(auto): prune orphan _needs on remove_scanner, reject empty address
bdraco May 22, 2026
f26dbdc
fix(scanner): clear _scan_mode_override on any restart error, not jus…
bdraco May 22, 2026
da66e9c
fix(auto): reject non-finite scan_interval/scan_duration
bdraco May 22, 2026
522181a
revert(auto): drop per-tick isfinite check in _coalesce_duration
bdraco May 22, 2026
7f4aeac
fix(auto): tighten ActiveScanRequest.scan_duration to non-Optional
bdraco May 22, 2026
17da0ca
docs(auto): trim verbose docstrings and address macOS-UUID correctness
bdraco May 22, 2026
31677b3
perf(scanner): toggle scanning_mode in-place across active-window cycle
bdraco May 22, 2026
67efc9b
test(auto): deflake first-sweep stagger assertion
bdraco May 22, 2026
04d4ff9
refactor(scanner): extract _effective_mode() helper
bdraco May 22, 2026
960e426
fix(scanner): apply shrink-prevention guard to the still-ACTIVE locke…
bdraco May 22, 2026
ba3787e
fix(scanner): gate active-window toggle on IS_LINUX, split into helpers
bdraco May 22, 2026
e74f49d
refactor(scanner): rename active-window entry helpers
bdraco May 22, 2026
c3e93c5
docs: trim verbose docstrings again
bdraco May 22, 2026
b44664b
refactor: DRY up active-window helpers
bdraco May 22, 2026
cf55c0b
fix(auto): cover sync-phase _tick errors, scanning flag on toggle fai…
bdraco May 22, 2026
5f009c7
fix(auto): guard private bleak attr, clear stale needs, validate dura…
bdraco May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

TO_CYTHONIZE = [
"src/habluetooth/advertisement_tracker.py",
"src/habluetooth/auto_scheduler.py",
"src/habluetooth/base_scanner.py",
"src/habluetooth/manager.py",
"src/habluetooth/models.py",
Expand Down
105 changes: 105 additions & 0 deletions src/habluetooth/auto_scheduler.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import cython

from .models cimport BluetoothServiceInfoBleak

cdef double _AUTO_INITIAL_SWEEP_DELAY
cdef double _AUTO_REDISCOVERY_INTERVAL
cdef double _AUTO_REDISCOVERY_SWEEP_DURATION
cdef double _AUTO_WINDOW_MAX_DURATION
cdef double _AUTO_WINDOW_MIN_DURATION


cdef class ActiveScanRequest:

cdef public str address
cdef public double scan_interval
cdef public double scan_duration


cdef class _ScannerWorker:

cdef public object _scheduler
cdef public object _scanner
cdef public object _manager
cdef public object _wake
cdef public object _task
cdef public double _window_end
cdef public double _sweep_last_completed
cdef public bint _failed_window

cpdef void start(self, object loop, double initial_offset=*)

cpdef void stop(self)

cpdef void wake(self)

@cython.locals(
source=str,
needs=dict,
address=str,
entries=dict,
next_at=double,
earliest=double,
)
cpdef double _next_event_at(self, double now)

@cython.locals(
source=str,
needs=dict,
address=str,
entries=dict,
due=list,
due_buckets=list,
all_due=list,
)
cpdef tuple _collect_due_buckets(self, double now)

@cython.locals(
entries=dict,
due=list,
request=ActiveScanRequest,
)
cpdef void _advance_due(self, list due_buckets, double from_time)


cdef class AutoScanScheduler:

cdef public object _manager
cdef public dict _requests_by_address
cdef public dict _needs
cdef public dict _workers
cdef public object _loop
cdef public bint _running

@cython.locals(
existing=dict,
)
cpdef void add_request(self, ActiveScanRequest request)

cpdef void remove_request(self, ActiveScanRequest request)

cpdef void add_scanner(self, object scanner)

@cython.locals(
source=str,
address=str,
)
cpdef void remove_scanner(self, object scanner)

@cython.locals(
address=str,
requests=set,
)
cpdef void on_advertisement(self, BluetoothServiceInfoBleak service_info)

@cython.locals(
existing=dict,
request=ActiveScanRequest,
)
cpdef void _seed_requests(
self, str address, set requests, double now
)

cpdef void start(self, object loop)

cpdef void stop(self)
Loading
Loading