Metadata-Version: 2.4
Name: waveium
Version: 0.4.0
Summary: Official Python SDK for the Waveium API
Project-URL: Homepage, https://waveium.io
Project-URL: Documentation, https://docs.waveium.io
Project-URL: Repository, https://github.com/waveium/waveium-sdk
Author: Waveium Team
License: MIT
License-File: LICENSE
Keywords: api,environment,execution,rf-simulation,scene,sdk,waveium
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
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
Requires-Python: >=3.9
Requires-Dist: anyio>=4.0.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: typing-extensions>=4.9.0
Provides-Extra: dev
Requires-Dist: black>=24.0.0; extra == 'dev'
Requires-Dist: flake8>=7.0.0; extra == 'dev'
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Provides-Extra: parquet
Requires-Dist: pandas>=2.0.0; extra == 'parquet'
Requires-Dist: pyarrow>=14.0.0; extra == 'parquet'
Description-Content-Type: text/markdown

# Waveium Python SDK

Official Python client library for the [Waveium API](https://waveium.io).
Build and run RF propagation simulations on top of a real environment
model, stream progress via Server-Sent Events, and read the parquet
results straight into a pandas DataFrame.

**New here?** Start with the step-by-step walkthrough at
[`docs/getting_started.md`](docs/getting_started.md).

## Features

- Full coverage of the **Environment -> Scene -> Execution** workflow.
- Synchronous (`waveium.Client`) and asynchronous
  (`waveium.AsyncClient`) clients.
- Built-in SSE progress streaming with blocking `wait()` helpers and
  a `create_and_run()` convenience for the common case.
- `read_parquet()` streams result files straight into pandas without
  writing to disk; `structured=True` merges xyz triplets and complex
  re/im pairs for immediate analysis.
- Pydantic v2 models throughout for type safety.
- `.env` auto-loaded via `python-dotenv`.
- Typed exception hierarchy for precise error handling.

## Installation

```bash
pip install "waveium[parquet]"
```

The `[parquet]` extra pulls in `pandas` and `pyarrow`, which
`client.executions.read_parquet()` requires. Drop it if you only need
to drive simulations without reading results in-process.

Python 3.9 or newer. Tested up to 3.13.

## Quick start

```python
import waveium

client = waveium.init()  # reads WAVEIUM_API_KEY from env / .env

# 1. Create an environment (outdoor, route-based).
env_resp = client.environments.create(
    name="Stockholm Route",
    environment_type="outdoor",
    route={
        "start": "Stockholm Central Station",
        "end": "Gamla Stan",
        "area": "Stockholm, Sweden",
    },
    stations={"BS1": [59.3293, 18.0686, 25.0]},
)
env = client.environments.wait(
    env_resp.environment.environment_id, timeout=900
)

# 2. Create and generate a scene.
scene = client.scenes.create(
    environment_id=env.environment_id,
    name="Default Scene",
    parameters={
        "stations": {"BS1": [59.3293, 18.0686, 25.0]},
        "station_radio_config": {
            "BS1": {"tx_power_dbm": 20.0, "noise_floor_dbm": -100.0}
        },
    },
)
client.scenes.generate(scene.scene_id)
client.scenes.wait(scene.scene_id, timeout=3600)

# 3. Run an execution and stream progress.
execution = client.scenes.create_and_run(
    scene.scene_id,
    execution_name="Quick Start",
    timeout=3600,
)

# 4. Read the parquet results into a DataFrame.
df = client.executions.read_parquet(
    execution.execution_id, structured=True
)
print(df.columns)
client.close()
```

For a guided walkthrough -- including how to plot RSSI, SNR, and
channel gain -- see [`docs/getting_started.md`](docs/getting_started.md).

## Authentication

The SDK reads the API key from the `WAVEIUM_API_KEY` (or
`WAVEIUM_SDK_TOKEN`) environment variable. Create keys at
<https://app.waveium.io>.

### Using a `.env` file (recommended)

```env
WAVEIUM_API_KEY=wvx_paste_your_key_here
```

```python
import waveium
client = waveium.init()  # .env auto-loaded
```

### Passing the key explicitly

```python
client = waveium.init(api_key="wvx_...")
```

### Firebase JWT exchange

If you already have a signed-in Firebase user, exchange the JWT for a
long-lived API key in one call:

```python
client = waveium.auth(
    firebase_token="eyJ...",      # or set FIREBASE_TOKEN
    token_name="My Python App",
    expires_days=30,
)
```

Under the hood this POSTs the Firebase JWT to `/auth/tokens` with
`Authorization: Bearer <jwt>` and returns a `Client` bound to the new
`wvx_` key.

## Async usage

The `AsyncClient` mirrors the sync client one-for-one and works as an
async context manager:

```python
import asyncio
import waveium


async def main() -> None:
    async with waveium.AsyncClient() as client:
        execution = await client.scenes.create_and_run(
            "scn_xxx",
            execution_name="Async run",
            timeout=3600,
        )
        df = await client.executions.read_parquet(
            execution.execution_id, structured=True
        )
        print(df.head())


asyncio.run(main())
```

See `examples/async_streaming_usage.py` for the full set of async
streaming patterns.

## Available resources

| Resource | Purpose |
|---|---|
| `client.environments` | Create / list / get / wait on environments. |
| `client.scenes` | Scene lifecycle, `generate()`, `wait()`, `create_execution()`, `create_and_run()`, file upload. |
| `client.executions` | Execution lifecycle, SSE streaming, `wait()`, `get()`, `progress()`, `read_parquet()`, `download_parquet()`, `download_archive()`, post-processing. |
| `client.antenna_patterns` | List, upload, and manage antenna patterns. |
| `client.auth` | Create, list, and revoke API tokens (Firebase exchange lives here). |
| `client.users` | `me()` and admin user management. |
| `client.organizations` | Organization admin. |
| `client.teams` | Team membership management. |
| `client.projects` | Project scoping for shared work. |
| `client.roles` | RBAC role and permission queries. |

## Error handling

All SDK errors inherit from `waveium.WaveiumError`:

```python
from waveium import (
    WaveiumError,           # Base class
    AuthenticationError,    # 401
    AuthorizationError,     # 403
    NotFoundError,          # 404
    ConflictError,          # 409 (e.g. scene not completed yet)
    ValidationError,        # 400
    RateLimitError,         # 429
    APIError,               # 5xx
    NetworkError,           # connection / DNS / timeout
    FileUploadError,
    FileDownloadError,
    ConfigurationError,
    StreamError,
    StreamTimeoutError,
    StreamReconnectError,
    ExecutionFailedError,   # terminal "failed" SSE event
)

try:
    client.scenes.create_and_run(scene_id, timeout=3600)
except ExecutionFailedError as e:
    print(f"Execution {e.execution_id} failed: {e.error_message}")
except ConflictError:
    print("Scene is not in completed state yet")
except WaveiumError as e:
    print(f"SDK error: {e}")
```

## Configuration

| Variable | Meaning | Default |
|---|---|---|
| `WAVEIUM_API_KEY` | API key (starts with `wvx_`) | -- |
| `WAVEIUM_SDK_TOKEN` | Alias for `WAVEIUM_API_KEY` | -- |
| `FIREBASE_TOKEN` | Firebase JWT for `waveium.auth()` | -- |
| `WAVEIUM_BASE_URL` | API base URL | `https://api.waveium.io` |
| `WAVEIUM_TIMEOUT` | HTTP timeout in seconds | `60` |
| `WAVEIUM_MAX_RETRIES` | Retry budget for idempotent requests | `3` |

Override programmatically:

```python
client = waveium.init(
    api_key="wvx_...",
    base_url="https://api.waveium.io",
    timeout=120.0,
    max_retries=5,
)
```

## Examples

See the `examples/` directory for runnable scripts. Each reads
`WAVEIUM_API_KEY` from the environment (or a `.env` file).

| Script | What it shows |
|---|---|
| `basic_usage.py` | Full Env -> Scene -> Execution pipeline, synchronous. |
| `streaming_usage.py` | Four tiers of SSE streaming (raw stream, blocking wait, create-and-run, environment wait). |
| `async_usage.py` | Async environment creation. |
| `async_streaming_usage.py` | Async counterparts of the streaming tiers. |
| `load_and_plot.py` | Minimal results-only script: point it at an `execution_id` to plot channel power and CIR. |
| `helsinki_usage.py` | Full outdoor Helsinki route: run the sim, download the parquet, and plot a 2D PDP. |
| `helsinki_track_usage.py` | Outdoor bbox variant: generate/upload a GPX track with timestamps as the UE trajectory, run the sim, download the parquet, and plot a 2D PDP. |
| `upload_download.py` | Scene file upload and execution result download. |
| `underground_workflow.py` | Complex underground scenario driven by JSON tunnel graph and CSV stations/UE, using polling. |
| `underground_workflow_sse.py` | Same scenario using SSE streaming instead of polling. |
| `verify_sdk.py` | Quick smoke test: init, `users.me()`, list environments. |

## Development

```bash
cd python313
pip install -e ".[dev,parquet]"
```

Code quality gates (match the project's `pyproject.toml` and
`setup.cfg`):

```bash
black src/ examples/ tests/
flake8 src/ examples/ tests/
mypy --strict src/
pytest
```

## License

MIT -- see [`LICENSE`](LICENSE).

## Support

- Dashboard: <https://app.waveium.io>
- Documentation: <https://docs.waveium.io>
- Issues: <https://github.com/waveium/waveium-sdk/issues>
- Email: <support@waveium.io>
