Metadata-Version: 2.4
Name: nrf-ota
Version: 0.2.0
Summary: Python package for OTA updating the bootloader on nrf chips
Project-URL: Homepage, https://opendisplay.org
Project-URL: Repository, https://github.com/OpenDisplay-org/nrf-ota
Project-URL: Documentation, https://github.com/OpenDisplay-org/nrf-ota#readme
Author-email: g4bri3lDev <admin@g4bri3l.de>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: ble,bluetooth,dfu,firmware,nordic,nrf,ota
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Hardware
Requires-Python: >=3.11
Requires-Dist: bleak>=0.21.0
Provides-Extra: dev
Requires-Dist: mypy>=1.19.1; extra == 'dev'
Requires-Dist: ruff>=0.14.10; extra == 'dev'
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
Requires-Dist: pytest-cov>=7.0.0; extra == 'test'
Requires-Dist: pytest>=9.0.2; extra == 'test'
Description-Content-Type: text/markdown

[![Tests](https://github.com/OpenDisplay-org/nrf-ota/actions/workflows/test.yml/badge.svg)](https://github.com/OpenDisplay-org/nrf-ota/actions/workflows/test.yml)
[![PyPI](https://img.shields.io/pypi/v/nrf-ota)](https://pypi.org/project/nrf-ota/)
[![Python Version](https://img.shields.io/pypi/pyversions/nrf-ota)](https://pypi.org/project/nrf-ota/)

# nrf-ota

Flash firmware to Nordic nRF5x devices over BLE from Python. Implements the **Nordic Legacy DFU** protocol (nRF5 SDK ≤ 15.x) and works on Linux, macOS, and Windows.

## Installation

```bash
pip install nrf-ota
```

## CLI

No install required — run directly with [uvx](https://docs.astral.sh/uv/):

```bash
uvx nrf-ota firmware.zip                             # interactive device picker
uvx nrf-ota firmware.zip --device OD216205           # select by name (non-interactive)
uvx nrf-ota firmware.zip --device FC:06:1C:C8:DE:47  # select by address
```

Scans for nearby BLE devices, lets you pick one, and flashes the firmware. If the device is running application firmware the bootloader is triggered automatically.

## Library

```python
import asyncio
from nrf_ota import perform_dfu, scan_for_devices

async def main():
    devices = await scan_for_devices(timeout=5.0)

    await perform_dfu(
        "firmware.zip",
        devices[0],
        on_progress=lambda pct: print(f"\r{pct:.0f}%", end=""),
        on_log=print,
    )

asyncio.run(main())
```

## API

### `perform_dfu(zip_path, device, *, on_progress=None, on_log=None, packets_per_notification=...)`

Performs a full OTA update — triggers the bootloader if needed, waits for the device to reboot into DFU mode, transfers the firmware, and activates it.

| Parameter | Type | Description |
|-----------|------|-------------|
| `zip_path` | `str` | Path to the Nordic DFU ZIP file |
| `device` | `BLEDevice \| str` | Device from `scan_for_devices`, or a raw Bluetooth address |
| `on_progress` | `Callable[[float], None]` | Called with percentage (0–100) as firmware is sent |
| `on_log` | `Callable[[str], None]` | Called with status messages |
| `packets_per_notification` | `int` | Packets sent per receipt notification. Default: **8 on macOS**, 10 elsewhere. |

Raises `DFUError` on failure, `DeviceNotFoundError` if the bootloader can't be found after reboot.

### `scan_for_devices(timeout=5.0) -> list[BLEDevice]`

Scans for nearby named BLE devices and returns a list of `bleak.BLEDevice` objects.

### Exceptions

| Exception | Description |
|-----------|-------------|
| `DFUError` | Base exception for all DFU failures |
| `DeviceNotFoundError` | Bootloader not found after reboot |

## Platform notes

Works on Linux, macOS, and Windows via [bleak](https://github.com/hbldh/bleak). On macOS, the default `packets_per_notification` is lowered to 8 (from 10) to stay within CoreBluetooth's write-without-response flow control limits.

## Development

```bash
git clone https://github.com/OpenDisplay-org/nrf-ota.git
cd nrf-ota
uv sync --all-extras

uv run pytest tests/ -v
uv run ruff check .
uv run mypy src/nrf_ota
```