Metadata-Version: 2.4
Name: lightball-ble
Version: 0.1.1
Summary: CSRmesh BLE protocol and async client for Holiday Show Home LED Balls
Author: dallanwagz
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/dallanwagz/lightball-ble
Project-URL: Repository, https://github.com/dallanwagz/lightball-ble
Project-URL: Issues, https://github.com/dallanwagz/lightball-ble/issues
Keywords: bluetooth,ble,csrmesh,home-assistant,led,light
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Home Automation
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: bleak>=0.22.0
Requires-Dist: bleak-retry-connector>=3.5.0
Requires-Dist: cryptography>=42.0.0
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: ruff; extra == "test"
Dynamic: license-file

# lightball-ble

Async BLE protocol and client for **Holiday Show Home "Show Home" LED Balls** — the
battery-powered RGB orbs sold under the Holiday Showtime / Show Home brands. They
speak Qualcomm/CSR's **CSRmesh** over a GATT bridge; this library reimplements that
framing so you can control a ball from Python (and powers the Home Assistant
[`lightball`](https://github.com/dallanwagz/lightball) integration).

## Install

```bash
pip install lightball-ble
```

## Usage

You provide a `bleak` `BLEDevice` (the ball rotates its BLE address, so re-resolve
it by the stable local name — e.g. `LAB00001CEB11` — before each use):

```python
from bleak import BleakScanner
from lightball_ble import LightBall

device = await BleakScanner.find_device_by_name("LAB00001CEB11")
ball = LightBall(device, "LAB00001CEB11")

await ball.set_state(mode=1, color=0, level=2)   # steady, red, mid brightness
await ball.set_show(6)                            # MultiColor animated show
await ball.turn_off()

# Re-resolve the address later, then reuse the client:
ball.set_ble_device(await BleakScanner.find_device_by_name("LAB00001CEB11"))
```

### Command reference

- `set_state(mode, color, level, *, turn_on=True)` — `commonMode`: animation `mode`
  (0=off, 1=steady, 2=blink, 5=fade, 7=waves …), palette `color` index (0=red …
  28=MultiColor), brightness `level` 0–4.
- `set_show(show_sel, *, turn_on=True)` — `showView` animated preset
  (0=Christmas … 6=MultiColor).
- `turn_off()`.

## Protocol

`lightball_ble.protocol` exposes the low-level framing if you need it:

- **Network key**: `reverse(SHA-256(b"686868\x00MCP"))[:16]` (a fixed, product-line
  shared key — `derive_key()`).
- **Packet**: AES-128 counter-mode payload + an 8-byte byte-reversed HMAC-SHA256 MIC
  + a trailing `0xFF` (`make_packet`).
- **Transport**: packets > 20 bytes split across two GATT control points
  (`split_writes`).

This was reverse-engineered from the official "Show Home" Android app; see the
[lightball](https://github.com/dallanwagz/lightball) project for the full writeup.

## Develop

```bash
pip install -e ".[test]"
pytest --cov=lightball_ble --cov-report=term-missing
ruff check . && ruff format --check .
```

## License

Apache-2.0.
