Metadata-Version: 2.4
Name: nexalware
Version: 0.1.0
Summary: Typed client for the Nexalware API - control physical devices and read their telemetry from any Python agent or app.
Author: Nexalware
License-Expression: MIT
Project-URL: Homepage, https://nexalware.com
Project-URL: Documentation, https://docs.nexalware.com/docs/sdk/sdk-python
Project-URL: Repository, https://github.com/Darrey1/nexalware-homepage
Project-URL: Issues, https://github.com/Darrey1/nexalware-homepage/issues
Keywords: nexalware,iot,device-control,robotics,sdk
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Home Automation
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# nexalware

Typed client for the [Nexalware](https://nexalware.com) API, control physical devices and read their telemetry from any Python agent or app. Zero third-party dependencies, built on `urllib` from the standard library, so dropping it into any existing agent environment can never trigger a version conflict.

Writing in TypeScript instead? See [`@nexalware/sdk`](https://www.npmjs.com/package/@nexalware/sdk) on npm. Want an MCP-aware host to discover these as tools automatically instead of calling them from code? See [`@nexalware/mcp`](https://www.npmjs.com/package/@nexalware/mcp).

## Install

```bash
pip install nexalware
```

## Quickstart

```python
from nexalware import NexalwareClient

client = NexalwareClient(api_key="nxw_live_sk_your_key_here")

client.turn_on("dev_a1b2c3")

latest = client.get_latest_telemetry("dev_a1b2c3")
print(latest["state"], latest["telemetry"])
```

Get an API key from the [dashboard](https://nexalware.com) (API Keys), and make sure it has a DeviceGrant covering the device(s) and command(s) you call, an ungranted key authenticates fine but every call is rejected with a 403.

> **Field names match the API's own JSON, not snake_case.** Return values are plain `dict`s (typed as `TypedDict` for editor/type-checker support), with the same field names the REST API itself uses, e.g. `latest["subDeviceId"]`, not `latest["sub_device_id"]`. Method and argument names are proper Python `snake_case`, only the data payloads keep the wire format.

## `NexalwareClient(api_key, base_url=...)`

| Param | Type | Required | Meaning |
|---|---|---|---|
| `api_key` | str | yes | A secret key from the dashboard. |
| `base_url` | str | no | Override for a self-hosted or staging deployment. Defaults to `https://api.nexalware.com`. |
| `timeout` | float | no | Abort a request after this many seconds. Defaults to `30.0`. |

## Errors

Every method raises `NexalwareApiError` on a non-2xx response, it never returns a "silent" error value.

| Attribute | Type | Meaning |
|---|---|---|
| `status` | int | HTTP status code. |
| `error` | str | Short machine-readable code, e.g. `"FORBIDDEN"`, `"NOT_FOUND"`. |
| `message` | str \| None | Human-readable reason, same text a dashboard user would see. |
| `details` | list \| None | Only present on a 400 validation failure. |

```python
from nexalware import NexalwareApiError

try:
    client.send_command("dev_a1b2c3", "SET_BRIGHTNESS", params={"level": 60})
except NexalwareApiError as err:
    print(err.status, err.error, err.message)
```

## Methods

- `list_devices(project_id=None)` - the devices this key can actually act on, only what its own DeviceGrant(s) cover. Call this first to discover valid `device_id` values instead of needing them hardcoded or pasted in.
- `get_commands(device_id)` - the command catalog this device accepts.
- `send_command(device_id, cmd, params=None, target=None)` - send a command to a device, or, with `target` set, to one specific sub-device behind it.
- `turn_on(device_id)` / `turn_off(device_id)` - shorthand for `send_command(device_id, "ON" | "OFF")`.
- `get_telemetry(device_id, metric=None, limit=None, since=None)` - historical telemetry readings, newest first.
- `get_latest_telemetry(device_id)` - current state snapshot plus the most recent reading per metric.
- `list_sub_devices(device_id)` / `get_sub_device(device_id, sub_device_id)` - physical devices connected locally behind a master device.
- `get_sub_device_telemetry(device_id, sub_device_id, metric=None, limit=None, since=None)` - same shape as `get_telemetry`, scoped to one sub-device.
- `send_sub_device_command(device_id, sub_device_id, cmd, params=None)` - convenience wrapper over `send_command` with `target` already set.
- `list_schedules(device_id)` / `get_schedule_context(device_id)` - a device's active schedules, and the commands available to schedule.
- `create_schedule(device_id, slot, on_ts, off_ts, ...)` / `update_schedule(...)` / `delete_schedule(device_id, slot)` - manage a device's schedule slots (0-4).
- `get_schedule_history(device_id)` - a device's completed or cancelled schedules, most recent first.

Full parameter/return types for every method: **[SDK Reference](https://docs.nexalware.com/docs/sdk/sdk-python)**.

## Links

- [Docs](https://docs.nexalware.com)
- [Device Orchestration](https://docs.nexalware.com/docs/device-orchestration) - sub-devices, and the contract a master implements to report them.
- [Authentication & Access](https://docs.nexalware.com/docs/concepts/authentication-and-access) - API keys and DeviceGrants.

## License

MIT
