Pure per-device advertisement cache with emit-gating and a bounded size.
No I/O — Bluez.Client owns the side effects (it emits the
adverts this module returns).
Each device path maps to %{props, last_raw, last_emit, last_seen}. On
upsert/4 the new props are merged, the advert is reconstructed
(Bluez.Advert), and an advert is returned to emit only when:
- it's the first sighting, or
- the reconstructed AD payload changed (sensor data update), or
- the heartbeat interval has elapsed (keeps RSSI/last-seen fresh in HA without forwarding every PDU).
The map is capped at @max_devices (LRU by last_seen) so a device spraying
randomized MACs — each a new BlueZ object path — can't grow it without bound
(BlueZ's InterfacesRemoved is the only other prune and is outside our
control).
Summary
Functions
Emit decision (pure). Emit on first sight (last_raw == nil), on a payload
change, or once the heartbeat interval has elapsed.
An empty cache.
Drop path's entry (BlueZ emitted InterfacesRemoved for it).
Distinct devices seen within the last window_ms of monotonic now_ms —
the web tab's "devices (15 min)" stat. A plain scan bounded by the LRU
cap, so it's cheap enough to run per stats tick.
Number of device entries currently cached.
Merge new_props for path at monotonic now_ms. Returns
{cache, adverts} where adverts is [] or a single reconstructed advert
the caller should emit.
Types
Functions
Emit decision (pure). Emit on first sight (last_raw == nil), on a payload
change, or once the heartbeat interval has elapsed.
@spec new() :: t()
An empty cache.
Drop path's entry (BlueZ emitted InterfacesRemoved for it).
@spec seen_within(t(), integer(), pos_integer()) :: non_neg_integer()
Distinct devices seen within the last window_ms of monotonic now_ms —
the web tab's "devices (15 min)" stat. A plain scan bounded by the LRU
cap, so it's cheap enough to run per stats tick.
Note the cap also bounds the answer: with > 512 active
devices the count saturates at the cap (eviction forgets the oldest).
@spec size(t()) :: non_neg_integer()
Number of device entries currently cached.
Merge new_props for path at monotonic now_ms. Returns
{cache, adverts} where adverts is [] or a single reconstructed advert
the caller should emit.