Metadata-Version: 2.5
Name: anker-ble-emulator
Version: 0.1.2
Summary: Emulated Anker Solix BLE devices for testing BLE clients through bleak
Project-URL: Repository, https://github.com/kb1ibt/anker-ble-emulator
Project-URL: Issues, https://github.com/kb1ibt/anker-ble-emulator/issues
Author-email: Shawn Stricker <github@kb1ibt.com>
License-Expression: MIT
License-File: LICENSE
Keywords: Anker,BLE,Solix,bleak,emulator,testing
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Framework :: AsyncIO
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: bleak>=3.0
Requires-Dist: construct>=2.10
Requires-Dist: cryptography>=43
Description-Content-Type: text/markdown

# anker-ble-emulator

Emulated Anker Solix BLE devices for testing BLE clients. A device plugs in under [bleak](https://github.com/hbldh/bleak) as a client backend, so your real client stack runs against it with no radio.

```python
from bleak import BleakClient

from anker_ble_emulator import A1783, EmulatedBleakBackend

device = A1783()  # SOLIX C2000 Gen 2, synthetic serial and MAC
async with BleakClient(device.ble_device, backend=EmulatedBleakBackend) as client:
    ...  # negotiate, authorize, send commands; notifications arrive as from the device

device.press_button()  # grant a pending owner confirmation
device.push(0x421)  # make the MCU push its recorded telemetry
```

For a client library that connects through bleak-retry-connector, `EmulatedConnection` patches its `establish_connection` so the library's own connect path lands on the emulator:

```python
from anker_ble_emulator.testing import EmulatedConnection

# Patches SolixBLE.device.establish_connection for the block.
async with EmulatedConnection(A1783()) as emulated:
    ...  # the library connects, negotiates and polls as against a real device
    emulated.check_assertions()  # if you set expect_ordered(...)
```

The emulated module runs the real negotiation (static-key GCM, P-256 ECDH, the session cipher), the module's authorization policy and its authorize timer; the MCU behind it answers with recorded, sanitized frames. See [docs/design.md](docs/design.md).
