Metadata-Version: 2.4
Name: noxdren
Version: 0.1.0
Summary: Official Python SDK for the Noxdren RangeIQ / RangeIQ+ API
Project-URL: Homepage, https://noxdren.com
Project-URL: Documentation, https://docs.noxdren.com
Project-URL: Source, https://github.com/noxdren/rangeiq-api/tree/main/sdks/python
Author-email: Noxdren <support@noxdren.com>
License-Expression: MIT
Keywords: api,drone,mission-planning,noxdren,rangeiq,sdk,uav
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.8
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest-asyncio; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# Noxdren Python SDK

Official Python client for the **Noxdren** API — battery-aware drone range
([RangeIQ](https://noxdren.com)) and multi-leg mission analysis (RangeIQ+).

```bash
pip install noxdren
```

## Quickstart

```python
import noxdren

client = noxdren.Client(api_key="ndx_...")   # your key from app.noxdren.com

# 1. See which drones are available
for d in client.drones.list().drones:
    print(d.key)

# 2. Range rings around a launch point
r = client.rangeiq.analyze(
    user_lat=37.5, user_lon=-122.3,
    selected_drone_key="dji_mavic_3",
    wind_speed=6.0, wind_direction=270,
)
for ring in r.result.rings:
    print(ring.label, ring.battery_threshold_pct)

# 3. Multi-leg mission from waypoints
m = client.missions.analyze_geometry(
    home={"lat": 37.5, "lon": -122.3},
    drone="dji_mavic_3",
    waypoints=[
        noxdren.Waypoint(lat=37.50, lon=-122.30, altitude_m=80),
        noxdren.Waypoint(lat=37.51, lon=-122.29, altitude_m=80, hover_time_s=10),
        noxdren.Waypoint(lat=37.52, lon=-122.31, altitude_m=100),
    ],
    weather=noxdren.Weather(wind_speed_mps=6.0, wind_dir_deg=270),
)
print(m.result.mission_verdict)            # "complete" | "marginal" | "cannot_complete"
```

Responses support attribute access (`r.result.rings[0].label`) and plain dict
access (`r["result"]["rings"]`) — whichever you prefer.

## Authentication

Pass your API key when you construct the client. It's sent as the `X-API-Key`
header on every request.

```python
client = noxdren.Client(api_key="ndx_...")
```

## Error handling

Every failure is a typed exception you can catch:

```python
import noxdren

try:
    client.rangeiq.analyze(user_lat=37.5, user_lon=-122.3, selected_drone_key="dji_mavic_3")
except noxdren.AuthenticationError:
    ...   # 401 — bad/expired key
except noxdren.ValidationError as e:
    print(e.code, e.message)   # 400/422 — fix the request
except noxdren.RateLimitError as e:
    print("retry after", e.retry_after, "s")   # 429
except noxdren.NoxdrenError as e:
    print(e.code, e.request_id)   # catch-all; request_id helps support
```

Transient errors (429, 5xx, network) are retried automatically with backoff
that respects `Retry-After`. Tune it: `Client(api_key=..., max_retries=0)`.

## Configuration

```python
noxdren.Client(
    api_key="ndx_...",
    base_url="https://api.noxdren.com",  # override for testing
    timeout=30.0,
    max_retries=2,
)
```

## What's covered

| Method | Endpoint |
| --- | --- |
| `client.drones.list()` / `.get(key)` | `GET /v1/drones`, `/v1/drones/{key}` |
| `client.rangeiq.analyze(...)` | `POST /v1/rangeiq/analyze` |
| `client.missions.analyze_geometry(...)` | `POST /v1/missions/analyze-geometry` |
| `client.missions.analyze(mission_file=...)` | `POST /v1/missions/analyze` (file upload) |
| `client.health()` / `.version()` | `GET /v1/health`, `/v1/version` |

Full request/response field reference: <https://docs.noxdren.com> and the live
schema at <https://api.noxdren.com/openapi.json>.
