Metadata-Version: 2.4
Name: openioe
Version: 1.1.2
Summary: Python Library for OpenIoE
Author: OpenIoE contributors
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Provides-Extra: mqtt
Requires-Dist: paho-mqtt<3,>=2.1; extra == "mqtt"

# OpenIoE user guide

> 🟩 **Setup** · 🟨 **Configuration** · 🟪 **Device operations** · 🟦 **Reference**

Use OpenIoE to read and write device data over HTTP and publish or receive MQTT
messages. All device operations select an entry from your configuration by `idx`.
Python 3.10 or newer is required.

For complete configuration details, architecture, module extensions, testing,
and releases, see [ReadmeDev.md](ReadmeDev.md).

## Table of contents

- [Installation](#installation)
- [Load your configuration](#load-your-configuration)
- [Read device data](#read-device-data)
- [Write device data](#write-device-data)
- [MQTT publish and subscribe](#mqtt-publish-and-subscribe)
- [Example programs](#example-programs)
- [Modules](#modules)
- [Handling errors](#handling-errors)

## Installation

🟩 **Setup**

Run from the repository root:

```sh
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e .
python3 examples/quickstart.py
```

For MQTT support, install the optional dependency:

```sh
python3 -m pip install -e '.[mqtt]'
```

HTTP methods use Python's standard library. MQTT uses optional Paho MQTT 2.x;
HTTP operations and configuration loading work without it.

## Load your configuration

🟨 **Configuration**

Copy [examples/config.example.json](examples/config.example.json) to `config.json`
and replace the placeholder IDs and credentials with your project settings.

```python
from openioe import Config
from openioe.devices import Devices

config = Config("config.json")
devices = Devices(config=config)
```

You may also use `Config(config_file="config.json")`,
`Config.from_json("config.json")`, or `Devices(config_file="config.json")`.
Relative paths resolve against the current working directory.

A minimal HTTP configuration is:

```json
{
  "base_url": "https://api.openioe.in",
  "api_key": "your-account-api-key",
  "api_version": "v1",
  "timeout": 30,
  "devices": [{
    "idx": 1,
    "device_id": "your-device-id",
    "read_apikey": "your-read-api-key",
    "write_apikey": "your-write-api-key",
    "communication_protocol": "http"
  }]
}
```

`idx` is the integer stored on the device entry, not its list position. Exactly
one entry must match. The library automatically selects `read_apikey` for reads
and `write_apikey` for writes. Keep actual credentials out of version control.

### Provide configuration in Python

Labeled constructor input:

```python
config = Config(
    base_url="https://api.openioe.in",
    api_key="your-account-api-key",
    devices=[{
        "idx": 1,
        "device_id": "your-device-id",
        "read_apikey": "your-read-api-key",
        "write_apikey": "your-write-api-key",
        "communication_protocol": "http",
    }],
)
devices = Devices(config=config)
```

Ordered input uses `base_url`, `api_key`, `api_version`, `timeout`; resource lists
remain keyword arguments:

```python
config = Config("https://api.openioe.in", "your-account-api-key", "v1", 30,
                devices=[{"idx": 1, "device_id": "your-device-id",
                          "read_apikey": "your-read-api-key",
                          "write_apikey": "your-write-api-key"}])
```

`api_version` defaults to `"v1"` and `timeout` to 30 seconds. Do not mix file input
with direct settings. All module constructors accept a shared `Config` instance.

## Read device data

🟪 **HTTP read**

```python
response = devices.readdata(idx=1)
print(response["data"])
```

This POSTs to `/v1/readdata` using the selected device's `device_id` and
`read_apikey`. Omitted `is_bulk` defaults to `False`; omitted timestamps or `None`
are sent as empty strings. Supply a time range when needed:

```python
response = devices.readdata(
    idx=1,
    is_bulk=False,
    from_timestamp="2026-06-02 00:10:04.629315+00",
    to_timestamp="2026-06-02 00:10:04.629315+00",
)
```

The full response dictionary is returned, for example:

```json
{"data": {}, "message": "success"}
```

## Write device data

🟪 **HTTP write**

```python
response = devices.writedata(idx=1, data={"voltage": 230})
print(response)
```

This POSTs to `/v1/writedata`. The request's `device_api_key` is automatically
set to the configured device's **`write_apikey`**. `data` must be a JSON-serializable
dictionary. `data_type` defaults to `"http"` and can be supplied explicitly:

```python
response = devices.writedata(idx=1, data={"voltage": 230}, data_type="http")
```

The full response dictionary is returned. HTTP endpoints use your configured
`base_url` and `api_version`.

## MQTT publish and subscribe

🟪 **MQTT messaging**

Install MQTT support with `python3 -m pip install -e '.[mqtt]'`.
Use a device with `communication_protocol: "mqtt"`, `MQTT_CLIENT`,
`MQTT_USERNAME`, `MQTT_PASSWORD`, and a nonempty `MQTT_TOPIC` list. Set the
project's `MQTT_BROKER` and, if needed, `MQTT_PORT`. See the
[full example configuration](examples/config.example.json).

```python
result = devices.publish(idx=1, data={"voltage": 230}, topic="data/monitor", qos=1)
print(result)

messages = devices.subscribe(idx=1, topic="data/control", num_messages=1)
for message in messages:
    print(message["topic"], message["payload"].decode("utf-8"))
```

| Setting | Behavior |
| --- | --- |
| Topic | Must match a configured `MQTT_TOPIC`. Omit for publish only if one topic exists; omit for subscribe to receive from all configured topics. |
| Payload | Publish accepts dict/list (JSON encoded), string, bytes, or bytearray. Subscribe returns raw payload bytes. |
| QoS | `qos=0` by default; accepts 0, 1, or 2. QoS 0 does not confirm broker delivery. |
| Retain | Publish defaults to `retain=False`. |
| Message count | Subscribe defaults to `num_messages=1`, then disconnects. |
| Client IDs | Publish uses `{MQTT_CLIENT}_python_pub`; subscribe uses `{MQTT_CLIENT}_python_sub`. |
| Timeout | Uses `config.timeout`; subscribe raises `TimeoutError` if the requested batch does not arrive. |
| TLS | Set `tls=True`; uses PEM `Server_CA` if provided, otherwise system trust. |

Both methods connect for the operation and disconnect afterward. Subscribe blocks
until its message count is reached or it times out. Publish returns `topic`, `mid`,
`qos`, and `retain`; each received message contains `topic`, `payload`, `qos`, and
`retain`. MQTT authenticates with its username/password rather than HTTP API keys.
Avoid simultaneous publishers for the same device or simultaneous subscribers for
the same device, since each role shares a client ID. Publish and subscribe use
different IDs and can run together.

TLS does not turn on automatically when `Server_CA` exists. The configured port
is always respected; if omitted, the default is 1883 or 8883 with TLS. The example
certificate placeholder must be replaced before use with TLS, or omitted to use
system trust.

## Example programs

🟩 **Run examples**

Run from the repository root after installing the library:

| Program | Usage |
| --- | --- |
| [quickstart.py](examples/quickstart.py) | `python3 examples/quickstart.py` — constructs clients without network calls |
| [devices1.py](examples/devices/devices1.py) | Loads the script-local account export named in the example; constructs the device client |
| [deviceread.py](examples/devices/deviceread.py) | Loads the account export relative to the working directory and reads configured `idx=9`; edit the file/path and idx for your project |
| [devicewrite.py](examples/devices/devicewrite.py) | `python3 examples/devices/devicewrite.py /path/to/config.json --idx 1` |
| [devicepublish.py](examples/devices/devicepublish.py) | `python3 examples/devices/devicepublish.py /path/to/config.json --idx 1 --topic data/monitor` |
| [devicesubscribe.py](examples/devices/devicesubscribe.py) | `python3 examples/devices/devicesubscribe.py /path/to/config.json --idx 1 --topic data/control --count 1` |

Device read/write and MQTT examples make live network calls. MQTT examples accept
`--tls`; subscribe accepts `--count` and an optional `--topic`.

## Modules

🟦 **Available modules**

| Module | Class | Current availability |
| --- | --- | --- |
| `openioe.auth` | `Auth` | Configuration entry point for authentication integration |
| `openioe.devices` | `Devices` | HTTP read/write and MQTT publish/subscribe |
| `openioe.dataset` | `Dataset` | Configuration entry point for datasets |
| `openioe.ai` | `AI` | Configuration entry point for AI workflows |
| `openioe.boards` | `Boards` | Configuration entry point for boards |
| `openioe.edge` | `Edge` | Configuration entry point for edge nodes |
| `openioe.digitaltwins` | `DigitalTwins` | Configuration entry point for digital twins |
| `openioe.usermanagement` | `UserManagement` | Configuration entry point for users and roles |

```python
from openioe.edge import Edge
from openioe.edge.ota import OTA

edge = Edge(config=config)
ota = OTA(config=config)
```

OTA is a nested package entry point; firmware transfer is not implemented.
Indexed AI model and edge firmware objects are preserved in the configuration.
See the [developer reference](ReadmeDev.md) for the complete format and module examples.

## Handling errors

🟨 **Troubleshooting**

Invalid inputs, missing devices, duplicate indices, or missing device keys raise
`ValueError`. Missing files raise filesystem exceptions. HTTP errors, connection
errors, timeouts, and malformed JSON responses propagate to callers. MQTT
subscription denial raises `PermissionError`; missing Paho raises `ImportError`
with installation instructions. TLS verification failures raise SSL exceptions.

Check that your `idx`, API keys, MQTT credentials, topics, and broker port match
your exported project configuration. An MQTT subscription timeout can mean no
matching messages arrived before `config.timeout`. Partial batches are not returned.
Network requests are not automatically retried.

For detailed request bodies, exceptions, validation rules, testing, and release
information, use [ReadmeDev.md](ReadmeDev.md).
