Skip to content
Back to top
View this page

# Usage

Assuming that you’ve followed the installation steps, you’re now ready to use this package.

The SLZB proxy is scan only, so devices are discoverable through `bleak` but not connectable.

Example usage with `bleak`:

    from __future__ import annotations

    import asyncio
    import logging

    import habluetooth

    from bleak_smlight import SMLIGHTConnectionManager, SMLIGHTDeviceConfig

    # An unlimited number of SLZB BLE proxies can be added here. ``source`` is a
    # stable unique id for the proxy (typically its MAC address), ``host`` is the
    # IP/hostname the UDP proxy server listens on, ``port`` is optional and
    # defaults to 5050.
    SMLIGHT_DEVICES: list[SMLIGHTDeviceConfig] = [
        {"source": "AA:BB:CC:DD:EE:FF", "name": "slzb-1", "host": "10.0.0.5"},
        {"source": "AA:BB:CC:DD:EE:00", "name": "slzb-2", "host": "10.0.0.6"},
    ]

    async def example_app() -> None:
        """Example application here."""
        import bleak

        await asyncio.sleep(5)  # Give time for advertisements to be received

        # Use bleak normally here
        devices = await bleak.BleakScanner.discover(return_adv=True)
        for d, a in devices.values():
            print()
            print(d)
            print("-" * len(str(d)))
            print(a)

        # Wait forever
        await asyncio.Event().wait()

    async def run() -> None:
        """Run the main application."""
        managers = [SMLIGHTConnectionManager(device) for device in SMLIGHT_DEVICES]
        await habluetooth.BluetoothManager().async_setup()
        try:
            # start() does not block on the device (the proxy client retries in
            # the background), so gather them concurrently and let any real
            # registration error surface instead of being swallowed.
            await asyncio.gather(*(manager.start() for manager in managers))
            await example_app()
        finally:
            await asyncio.gather(*(manager.stop() for manager in managers))

    logging.basicConfig(level=logging.DEBUG)
    asyncio.run(run())

## Device configuration

Each proxy is described by a `SMLIGHTDeviceConfig`:

Key | Required | Description
--- | --- | ---
`source` | yes | Stable unique id for the proxy, typically its MAC address.
`name` | yes | Human-friendly adapter name shown by `habluetooth`.
`host` | yes | IP or hostname the UDP proxy server listens on.
`port` | no | UDP port of the proxy server; defaults to `SLZB_BLE_SERVER_PORT` (5050).

`SMLIGHTConnectionManager.start()` returns once the scanner is registered and the proxy client has been started; it does not block waiting for the device to respond, because the underlying `pysmlight.BleProxyClient` retries in the background. Call `start()` once per manager instance; a second call raises `RuntimeError`. `stop()` is always safe to call, including before `start()`.

## Advanced: wiring `connect_scanner` directly

`SMLIGHTConnectionManager` is the recommended entry point: it builds the scanner, registers it with `habluetooth`, starts the proxy client, and tears everything down on `stop()`. Reach for `connect_scanner` only when you want to own the scanner registration and proxy-client lifecycle yourself.

`connect_scanner(source, name, host, port=SLZB_BLE_SERVER_PORT)` builds a `SMLIGHTScanner` plus a `pysmlight.BleProxyClient` wired to its advertisement callback, and returns a `SMLIGHTClientData`. It leaves three jobs to the caller:

1. Call `data.scanner.async_setup()` to attach the scanner to the running loop.

2. Register the scanner with the host-side Bluetooth manager (and unregister it on teardown).

3. `await data.client.start()` to begin receiving advertisements, and `data.client.stop()` on teardown.

    import habluetooth

    import bleak_smlight

    data = bleak_smlight.connect_scanner(
        "AA:BB:CC:DD:EE:FF", "slzb-1", "10.0.0.5"
    )
    unsetup = data.scanner.async_setup()
    unregister = habluetooth.get_manager().async_register_scanner(data.scanner)
    await data.client.start()

    # Later, on teardown:
    data.client.stop()
    unregister()
    unsetup()