Skip to content

feat(ble-proxy): end-to-end BLE proxy commissioning + matter-ble-proxy Python lib#662

Open
Apollon77 wants to merge 25 commits into
mainfrom
bleproxy
Open

feat(ble-proxy): end-to-end BLE proxy commissioning + matter-ble-proxy Python lib#662
Apollon77 wants to merge 25 commits into
mainfrom
bleproxy

Conversation

@Apollon77
Copy link
Copy Markdown
Collaborator

@Apollon77 Apollon77 commented May 19, 2026

Summary

End-to-end BLE proxy commissioning support, plus a brand-new
matter-ble-proxy Python client library that the Home Assistant Matter
integration consumes (see companion HA core PR).

This is two logical changes squashed onto one branch:

  1. End-to-end BLE proxy commissioning — Use an external BLE proxy to commission Matter devices directly into the Matter server. This lifts the limitation to only support local ble adapters. Matter device commissioning over the proxy now completes end-to-end (PASE → CASE → operational).

  2. matter-ble-proxy Python package — new top-level python_ble_proxy/ package. Ships protocol logic + Bleak-based default backend + CLI. Used by the HA Matter integration to integrate with this new BLE proxy feature of the matter server, and provides a standalone reference CLI for testing.

matter server BLE Proxy support

JavaScript example using noble

  • CLI example including an example in JS which implements a ble-proxy client using "noble" to do the BLE logic and interacts with the server

matter-ble-proxy Python library

  • Public API: `MatterBleProxy`, `BleScanSource` /
    `BleDeviceResolver` ABCs, `AdvertisementData` dataclass,
    `BleakScanSource` / `BleakDeviceResolver` defaults.
  • Resource-optimized: scanner only runs between
    matter-server's `start_scan` / `stop_scan`. Fully torn down on
    proxy disconnect. No idle scanning.
  • All command handlers surface spec-defined error codes
    (`read_failed`, `write_failed`, `subscribe_failed`,
    `not_subscribed`, …) instead of bucketing into
    `internal_error`.
  • INFO-level command logging mirroring the JS `noble-ble-proxy`
    example (`[←CMD] id=… command …`), with base64 payloads stripped
    for readability.
  • `matter-ble-proxy` console entry: same flag set as the JS
    reference (`--server`, `--log-level`).
  • Package is published with the same versioning in sync with the matter server

Docs

  • Top-level `README.md` — added "BLE Proxy mode" section pointing
    at both reference clients + the protocol spec.
  • `packages/ble-proxy/README.md` — links to the Python package.
  • `python_ble_proxy/README.md` — install, CLI, library API.
  • `docs/ble-proxy-protocol.md` — new UUID-format section, clarified
    `response` field on `write_characteristic`, and the ATT Write
    Request requirement on `WRITE_DATA` binary frames.

🤖 Generated with Claude Code

Apollon77 and others added 9 commits February 16, 2026 13:46
Implements a new packages/ble-proxy package that enables Matter BLE commissioning
through a remote BLE-capable client (e.g. Home Assistant with ESPHome BLE proxies)
over a WebSocket protocol on the /ble endpoint.

New package (packages/ble-proxy):
- BleProxyProtocol: Protocol types, typed command map, binary frame codec
- BleProxyHandler: WebSocket server for /ble with hello handshake, Observable events
- ProxyBle/ProxyBleClient/ProxyBleScanner: matter.js Ble abstract class implementation
- ProxyBleCentralInterface/ProxyBleChannel: GATT operations and BTP over binary frames
- Noble-based reference client (npm run noble-ble-proxy) for testing and standalone use
- Integration tests (18 tests) and test utilities (BleProxyTestClient, MockBleDevice)

Server integration:
- New --ble-proxy CLI flag (mutually exclusive with --bluetooth-adapter)
- Registers ProxyBle with matter.js environment for transparent BLE commissioning

Documentation:
- docs/ble-proxy-protocol.md: Standalone protocol specification
- docs/plans/: Architecture design and implementation plan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents the Home Assistant matter integration changes needed for
BLE proxy support, including file-by-file change descriptions,
design decisions, and testing instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	package.json
#	packages/matter-server/package.json
- Resolved conflict in packages/matter-server/package.json: kept ble-proxy
  dependency while taking new matter.js version from main
- Updated packages/ble-proxy/package.json matter.js deps to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts:
#	packages/matter-server/package.json
#	packages/matter-server/src/MatterServer.ts
# Conflicts:
#	packages/matter-server/package.json
# Conflicts:
#	packages/matter-server/package.json
Adds a working BLE proxy package (@matter-server/ble-proxy) that lets a
remote BLE adapter drive Matter BLE commissioning over a WebSocket, plus
the matter-server integration that wires it onto the /ble endpoint and
the noble-based reference client.

Notable correctness fixes for an actual macOS-noble proxy run:

- UUID format: noble emits 32-char compact lowercase, matter.js consts use
  canonical dashed uppercase. Added toCanonicalUuid + MatterBle.isServiceUuid
  matching so the server accepts any case, with or without dashes, plus the
  16-bit short form. ESPHome-style clients and noble-style clients both work.
- BTP write semantics: Matter BTP C1 uses ATT Write Request (with response).
  Previous proxy default was withoutResponse, which the peripheral silently
  dropped — BTP handshake response never came. Initial JSON write and all
  binary-frame WRITE_DATA fragments now use response=true.
- scan-during-GATT hang on macOS: while noble scans (allowDuplicates=true)
  for the same service UUID, service.discoverCharacteristicsAsync never
  fires its delegate. The noble proxy client now stops scanning around
  connect + GATT discovery and resumes after.
- ProxyBle shutdown: register with env.runtime so close cascades to
  ProxyBleScanner. Fix a microtask race where cancelResolver and
  finishWaiter were both queued in the same tick — the discovery loop's
  awaiter could resume before canceled flipped true, re-arming a fresh
  waiter and hanging shutdown. Trip cancelResolvers, yield a microtask,
  then unblock awaiters.
- Override env.Ble AFTER MatterController.create — that call dynamically
  imports @matter/nodejs-ble, whose install.js auto-installs NodeJsBle
  when ble.enable=true. The proxy implementation must win.

Quality of life:

- Reference noble client adds per-line timestamps, dedup of repeated
  device_discovered events on unchanged advertisements, and noble warning
  + stateChange surfaces for diagnostics.
- Protocol spec gains an explicit UUID-format section, clarifies the
  response field on write_characteristic, and documents the
  with-response constraint on WRITE_DATA binary frames.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a new `matter-ble-proxy` Python package (under python_ble_proxy/)
that implements the client side of the BLE proxy WebSocket protocol.
The library is consumed by the Home Assistant Matter integration and
shipped with a Bleak-based reference CLI for standalone testing.

Highlights:

- `MatterBleProxy` handles the full protocol surface (start_scan,
  stop_scan, connect, disconnect, discover_services,
  discover_characteristics, read/write/subscribe/unsubscribe,
  request_mtu, plus WRITE_DATA / NOTIFICATION binary frames). Errors
  are mapped to spec-defined codes (read_failed, write_failed,
  subscribe_failed, not_subscribed, ...).
- Pluggable backends via `BleScanSource` and `BleDeviceResolver` ABCs.
  Default `BleakScanSource` / `BleakDeviceResolver` drive Bleak
  directly; integrators (e.g. Home Assistant) supply their own.
- Resource-optimized: the scanner only runs between matter-server's
  start_scan / stop_scan and is fully torn down on proxy disconnect.
- Per-address advertisement dedup (fingerprint of name, connectable,
  service_uuids, service_data) so the matter-server is not flooded
  with redundant `device_discovered` events during an in-flight
  commission.
- UUID normalization that collapses standard Bluetooth Base UUIDs to
  the short form so `service_uuids=["fff6"]` matches Bleak's
  canonical `0000fff6-0000-1000-8000-00805f9b34fb` advertisements.
- `matter-ble-proxy` console entry exposes the same flag set as the
  JS `noble-ble-proxy` example (`--server`, `--log-level`).
- INFO-level command log (`[←CMD] id=... command ...`) for visibility
  without DEBUG.

Repo plumbing:

- `npm run python-ble-proxy:{install,lint,lint-fix,typecheck,test,build}`
- `.github/workflows/test-python-ble-proxy.yml` runs ruff + mypy +
  pytest on python_ble_proxy/** changes.
- `release-npm.yml` mirrors the python_client PyPI publish flow for
  matter-ble-proxy: a `pypi_ble_proxy_needed` gate patches
  python_ble_proxy/pyproject.toml with the same PEP 440 version and
  builds + uploads alongside matter-python-client.

ProxyBleScanner shutdown hardening:

- Added a `#closed` flag the discovery loop now checks alongside
  `canceled`. Survives the case where commissioning supplies its own
  cancelSignal (and our per-query cancelResolver is therefore
  undefined), which previously left the loop re-arming a fresh waiter
  on shutdown and hanging the matter-server process.

Docs:

- `python_ble_proxy/README.md` covers install, CLI, library API.
- Top-level README and `packages/ble-proxy/README.md` cross-link to
  the new Python package and protocol spec.
- `docs/ble-proxy-protocol.md` gained a dedicated UUID-format section
  and clarified `response` semantics on write_characteristic and
  WRITE_DATA binary frames (Matter BTP requires ATT Write Request).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apollon77 and others added 8 commits May 19, 2026 19:45
…ocol spec version

- Remove `docs/plans/*.md` from the repo — these are local-only working
  notes per CLAUDE.md; the directory is already in `.gitignore` so new
  ones stay out, but the older tracked entries needed an explicit
  removal.
- Add explicit `superpowers/` / `.superpowers/` entries to
  `.gitignore` alongside the existing `/.*` catch-all, so AI-tooling
  scratch dirs are obviously excluded.
- Drop the `(Draft)` marker on `docs/ble-proxy-protocol.md` v1.0 now
  that the protocol is shipping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… base extension, document python client

- `ProxyBleChannel`: every fire-and-forget promise (`btpSession.handleIncomingBleData`, `channel.close()` on unexpected disconnect, BTP-session disconnect callback, openChannel failure cleanup) now attaches a `.catch(err => logger.debug/warn(...))` so failures stay visible instead of becoming unhandled rejections.
- `NobleBleProxyClient` (example): same treatment for `#handleCommand` dispatch, peripheral disconnect on shutdown, and the post-failure scan restart.
- `ProxyBleScanner`: replaced ~200 lines of duplicated waiter / query / cancellation logic with a 30-line subclass of matter.js's `BleScanner`. We only narrow `getDiscoveredDevice` to expose `ProxyPeripheral` and override `closeClient` to route through `ProxyBleClient`. Behavior parity comes for free (and stays in sync with future matter.js fixes).
- `packages/ble-proxy/README.md`: added a "Python Example Client" section pointing at the bundled `matter-ble-proxy` PyPI package + `npm run python-ble-proxy:run` so the two reference clients sit side-by-side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the new BLE proxy mode flag to both:

- `docs/cli.md`: row in the options table, cross-referenced with
  `--bluetooth-adapter` (mutually exclusive).
- `docs/docker.md`: `BLE_PROXY` row in the env-var table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Apollon77 Apollon77 marked this pull request as ready for review May 20, 2026 09:23
Copilot AI review requested due to automatic review settings May 20, 2026 09:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end BLE proxy commissioning to the Matter server by introducing a dedicated /ble WebSocket endpoint plus a new @matter-server/ble-proxy package, and ships a new matter-ble-proxy Python library/CLI intended for Home Assistant and standalone bridging.

Changes:

  • Add BLE proxy server endpoint support (/ble) and wire it into commissioning via a ProxyBle implementation.
  • Introduce new packages/ble-proxy workspace (protocol, handler, ProxyBle transport, noble reference client) with tests.
  • Add new python_ble_proxy/ package (library + Bleak CLI) plus CI workflow, docs, and release pipeline updates.

Reviewed changes

Copilot reviewed 43 out of 48 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tsconfig.json Adds TS project reference for the new packages/ble-proxy workspace.
README.md Documents “BLE Proxy mode” and links to protocol + reference clients.
python_ble_proxy/tests/test_protocol.py Adds unit tests for protocol constants and UUID normalization.
python_ble_proxy/tests/init.py Initializes Python test package.
python_ble_proxy/README.md Documents install, CLI usage, and library API for matter-ble-proxy.
python_ble_proxy/pyproject.toml Defines Python package metadata, deps, and tooling (ruff/mypy/pytest).
python_ble_proxy/matter_ble_proxy/py.typed Marks package as typed.
python_ble_proxy/matter_ble_proxy/protocol.py Adds protocol constants/types for the Python client.
python_ble_proxy/matter_ble_proxy/example/cli.py Adds Bleak-based reference CLI entrypoint.
python_ble_proxy/matter_ble_proxy/example/init.py Documents example module purpose.
python_ble_proxy/matter_ble_proxy/client.py Implements core Python BLE proxy client and command handling.
python_ble_proxy/matter_ble_proxy/bleak_backend.py Provides default Bleak-backed scan + resolve backend.
python_ble_proxy/matter_ble_proxy/init.py Exposes public Python API symbols.
python_ble_proxy/.gitignore Adds Python package-local ignores (venv, caches, dist).
packages/ws-controller/src/server/WebSocketControllerHandler.ts Switches /ws to noServer upgrade handling to avoid breaking other WS endpoints.
packages/ws-controller/package.json Bumps Matter.js SDK versions and optional @matter/nodejs-ble.
packages/ws-client/package.json Bumps @matter/testing version.
packages/matter-server/src/MatterServer.ts Adds --ble-proxy mode, registers /ble handler, and installs ProxyBle into the environment.
packages/matter-server/src/cli.ts Adds --ble-proxy CLI option + env var mapping.
packages/matter-server/package.json Adds dependency on @matter-server/ble-proxy and bumps Matter.js SDK versions.
packages/dashboard/package.json Bumps @matter/main version.
packages/custom-clusters/package.json Bumps @matter/main version.
packages/ble-proxy/tsconfig.json Adds composite TS config for the new workspace.
packages/ble-proxy/test/tsconfig.json Adds test TS config and references.
packages/ble-proxy/test/MockBleDevice.ts Adds mock peripheral generator used by BLE proxy integration tests.
packages/ble-proxy/test/BleProxyTestClient.ts Adds a test WS client that simulates the proxy side.
packages/ble-proxy/test/BleProxyProtocolTest.ts Adds unit tests for binary frame codec.
packages/ble-proxy/test/BleProxyIntegrationTest.ts Adds integration tests for /ble handler + ProxyBle flows.
packages/ble-proxy/src/tsconfig.json Adds library TS config and references.
packages/ble-proxy/src/ProxyBleScanner.ts Implements scanner wrapper using the proxy client.
packages/ble-proxy/src/ProxyBleClient.ts Implements proxy-side discovery client for server-side scanning.
packages/ble-proxy/src/ProxyBleChannel.ts Implements BLE central interface + BTP channel over the proxy protocol.
packages/ble-proxy/src/ProxyBle.ts Implements Ble environment provider that routes via the proxy.
packages/ble-proxy/src/index.ts Exports public API for @matter-server/ble-proxy.
packages/ble-proxy/src/example/NobleBleProxyClient.ts Adds noble-based reference implementation of the client side.
packages/ble-proxy/src/example/noble-ble-proxy.ts Adds CLI wrapper for the noble reference client.
packages/ble-proxy/src/BleProxyProtocol.ts Defines protocol messages/types and binary frame codec for TS.
packages/ble-proxy/src/BleProxyHandler.ts Implements /ble WebSocket handler (handshake + command routing).
packages/ble-proxy/README.md Documents server enablement and reference clients (JS + Python).
packages/ble-proxy/package.json Adds new published workspace package definition and scripts.
package.json Adds workspace entry + helper scripts for noble + python BLE proxy workflows.
package-lock.json Updates lockfile for new workspace, deps, and version bumps.
docs/docker.md Documents new BLE_PROXY env var.
docs/cli.md Documents new --ble-proxy CLI option and exclusivity with --bluetooth-adapter.
docs/ble-proxy-protocol.md Adds full BLE proxy protocol specification and clarifications.
.gitignore Ignores local AI-tooling scratch directories.
.github/workflows/test-python-ble-proxy.yml Adds CI workflow for Python BLE proxy lint/typecheck/tests.
.github/workflows/release-npm.yml Extends release pipeline to optionally publish matter-ble-proxy to PyPI.

Comment thread python_ble_proxy/matter_ble_proxy/client.py Outdated
Comment thread packages/ws-controller/src/server/WebSocketControllerHandler.ts Outdated
Comment thread packages/ble-proxy/src/BleProxyHandler.ts Outdated
Comment thread packages/ble-proxy/src/ProxyBleChannel.ts
Apollon77 and others added 2 commits May 20, 2026 12:28
…662 review

- Add `ble_proxy_enabled` to server_info handshake so clients can distinguish
  BLE proxy mode from local-adapter mode (`--ble-proxy` vs `--bluetooth-adapter`).
- Add a fallback `upgrade` listener in `WebServer.ts` that 404s + destroys
  unmatched WebSocket upgrades (handlers tag the request via
  `_matterHandledUpgrade`).
- `ProxyBleChannel`: wrap handshake observer in try/finally so the listener
  and timer always clean up, even on handshake timeout/reject.
- `python_ble_proxy.client._handle_discover_services`: guard against
  `client.services` being None instead of iterating None on platforms where
  Bleak's cache hasn't populated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… services guard

- Trim verbose explanations in `_handle_command`, `_handle_start_scan`,
  and `_handle_discover_services` to the non-obvious WHY.
- Annotate `services` as `BleakGATTServiceCollection | None` so mypy
  accepts the None guard added in the previous commit
  (Bleak's stub claims the property is always populated post-connect).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 53 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

packages/ble-proxy/src/BleProxyHandler.ts:202

  • sendCommand() has no timeout, so if the proxy client never responds (e.g., BLE stack stalls) the returned promise stays pending and the entry remains in #pendingCommands until disconnect. This can hang commissioning flows indefinitely. Consider adding a per-command timeout (and clearing the pending map entry on timeout) similar to packages/ws-client/src/client.ts's sendCommand implementation.
    async sendCommand<C extends BleProxyCommandName>(
        command: C,
        ...rest: BleProxyCommandMap[C]["args"] extends undefined ? [] : [args: BleProxyCommandMap[C]["args"]]
    ): Promise<BleProxyCommandMap[C]["result"]> {
        if (!this.connected || !this.#client) {
            throw new Error("BLE proxy client not connected");
        }

        const args = rest[0];
        const id = this.#nextCommandId;
        this.#nextCommandId = (this.#nextCommandId + 1) & 0xffff;
        const message: CommandMessage = { id, command };
        if (args !== undefined) {
            message.args = args as Record<string, unknown>;
        }

        const { promise, resolver, rejecter } = createPromise<Record<string, unknown> | undefined>();
        this.#pendingCommands.set(id, { resolver, rejecter });

        this.#client.send(JSON.stringify(message), err => {
            if (err) {
                this.#pendingCommands.delete(id);
                rejecter(new Error(`Failed to send command ${command}: ${err.message}`));
            }
        });

        return promise as Promise<BleProxyCommandMap[C]["result"]>;
    }

Comment thread packages/ws-controller/src/server/WebSocketControllerHandler.ts Outdated
Comment thread packages/ble-proxy/src/BleProxyHandler.ts Outdated
Comment thread packages/matter-server/src/MatterServer.ts
Comment thread python_ble_proxy/matter_ble_proxy/client.py
Apollon77 and others added 2 commits May 20, 2026 14:14
…bind cleanup; python_ble_proxy WS-close recovery

- `loadBleSupport`: short-circuit when `ble.proxy.enable` is true so proxy
  mode no longer hard-requires `@matter/nodejs-ble` to be installed.
- WebSocketControllerHandler / BleProxyHandler: track upgrade-listener
  removers as arrays so multiple `register()` calls (one per listen address)
  don't drop earlier servers' listeners.
- python_ble_proxy: factor scan/peripheral teardown into
  `_release_ble_resources` and invoke it from `_message_loop` finally so an
  unexpected WS close immediately stops scanning and disconnects peripherals
  instead of leaking until the caller invokes `disconnect()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… not env vars

VariableService rejects `ble.proxy.enable` once `ble.enable` has been set
(the `ble` segment is already a leaf, not a map), so the previous attempt
at a parallel env var fataled on startup:

  ImplementationError: Can't set ble.proxy.enable because segment proxy is not a map

Drop the env var and thread the boolean through MatterControllerOptions
instead. Verified by booting `--ble-proxy` end-to-end on macOS without
`@matter/nodejs-ble` installed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visibility for operators waiting on a proxy client to attach before
commissioning can start.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 54 changed files in this pull request and generated 5 comments.

Files not reviewed (1)
  • .idea/ha-matterjs-server.iml: Language not supported

Comment thread python_ble_proxy/matter_ble_proxy/client.py
Comment thread python_ble_proxy/matter_ble_proxy/client.py
Comment thread packages/ble-proxy/src/BleProxyHandler.ts
Comment thread packages/ble-proxy/package.json Outdated
Comment thread packages/ble-proxy/README.md Outdated
Apollon77 and others added 3 commits May 20, 2026 15:27
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…or allow_duplicates; fix deprecated get_event_loop

BleProxyHandler:
- Replace setTimeout/clearTimeout with `Time.getTimer(...)` (handshake)
  and `withTimeout(...)` (per-command) so timer reporting matches the
  rest of the stack and the cancel path naturally evicts the pending
  command on timeout. Uses `Seconds(...)` for the Duration brand.

python_ble_proxy:
- `_spawn_task`: prefer the loop captured in `connect()` and fall back to
  `asyncio.get_running_loop()`; `asyncio.get_event_loop()` is deprecated
  in Python 3.12+ and raises in threads without a running loop.
- `_handle_start_scan`: honor the protocol's `allow_duplicates` arg. When
  true (the server-side default) forward every advertisement; otherwise
  keep the per-address fingerprint dedup so 10 Hz Matter beacons don't
  spam the WebSocket.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 54 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • .idea/ha-matterjs-server.iml: Language not supported

Comment on lines 145 to +153
async register(server: HttpServer) {
logger.info(`Starting server: matter-server/${this.#serverVersion} (matter.js/${MATTER_VERSION})`);
const wss = (this.#wss = new WebSocketServer({ server: server, path: "/ws" }));
// Use noServer mode with a path-filtered upgrade listener.
// ws 8.x calls handleUpgrade unconditionally from its own upgrade listener, which
// sends HTTP 400 for non-matching paths and destroys the socket — breaking other
// WebSocket endpoints on the same server. By handling upgrade ourselves we only
// call handleUpgrade when the path actually matches.
const wss = (this.#wss = new WebSocketServer({ noServer: true }));
const upgradeHandler = (
// ws 8.x calls handleUpgrade unconditionally from its own upgrade listener, which
// sends HTTP 400 for non-matching paths and destroys the socket — breaking other
// WebSocket endpoints on the same server.
const wss = (this.#wss = new WebSocketServer({ noServer: true }));
Comment on lines +59 to +66
async startScanning(): Promise<void> {
if (this.#isScanning) {
return;
}

if (!this.#handler.connected) {
logger.info("BLE proxy not connected, deferring scan start");
return;
Comment on lines +335 to +342
service_uuids: list[str] = args.get("service_uuids", [])
service_uuid_set = {_normalize_uuid(u) for u in service_uuids} if service_uuids else None
allow_duplicates: bool = bool(args.get("allow_duplicates", False))

# When `allow_duplicates` is false, dedup by content fingerprint — Matter peripherals
# broadcast at ~10 Hz and the server only needs state changes. When true, the server
# explicitly opts in to the full stream (e.g. to track RSSI updates).
last_fingerprint: dict[str, tuple[str | None, bool, tuple[str, ...], tuple[tuple[str, bytes], ...]]] = {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants