Metadata-Version: 2.3
Name: trinity-connect-client
Version: 0.3.3
Summary: Official Python library for Trinity IoT Connect API interactions
Author: Jan Badenhorst, Andries Niemandt
Author-email: Jan Badenhorst <jan.badenhorst@trintel.co.za>, Andries Niemandt <andries.niemandt@trintel.co.za>
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Dist: requests>=2.32.4
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/trinity-telecomms/connect-py-client
Project-URL: Issues, https://github.com/trinity-telecomms/connect-py-client/issues
Description-Content-Type: text/markdown

# Connect Client for Python

[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Official Python library for interacting with the Trinity IoT Connect API. 
This library provides a clean interface for accessing the Connect API endpoints 
with comprehensive error handling, type hints, and good test coverage.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Error Handling](#error-handling)
- [Development](#development)
- [Contributing](#contributing)
- [License](#license)

## Installation

### From PyPI (Recommended)

The library is available on PyPI and can be installed using your preferred package manager:

#### Using uv (recommended)

```bash
uv add trinity-connect-client
```

#### Using pip

```bash
pip install trinity-connect-client
```

### From GitHub

You can also install directly from the GitHub repository:

#### Using uv

```bash
# Install latest release
uv add git+https://github.com/trinity-telecomms/connect-py-client

# Install specific version
uv add git+https://github.com/trinity-telecomms/connect-py-client@v0.2.1
```

#### Using pip

```bash
# Install latest release
pip install git+https://github.com/trinity-telecomms/connect-py-client

# Install specific version
pip install git+https://github.com/trinity-telecomms/connect-py-client@v0.2.1
```

## Quick Start

```python
from trinity_connect_client import ConnectClient
from trinity_connect_client.exceptions import ResourceNotFoundError, UnauthorisedError

# Initialize the client
client = ConnectClient(
  api_version="v4",
  base_url="https://capi.trintel.co.za",
  token="your-service-account-token"
)

# Get a device by ID (returns dict)
try:
    device = client.devices.get(device_id=123)
    print(f"Device name: {device['name']}")
except ResourceNotFoundError:
    print("Device not found")
except UnauthorisedError:
    print("Access denied")
```

## Using Response Models

The library provides type-safe dataclass models for API responses:

```python
from trinity_connect_client import ConnectClient, Device, Company, Folder

client = ConnectClient(
    api_version="v4",
    base_url="https://capi.trintel.co.za",
    token="your-service-account-token"
)

# Get device as dict, then convert to model for type safety
device_dict = client.devices.get(device_id=123)
device = Device.from_dict(device_dict)

# Now you have full type hints and IDE autocomplete
print(f"Device: {device.name}")
print(f"UID: {device.uid}")
print(f"Status: {device.status}")

# Works with all response types
company_dict = client.orgs.get(company_id=1)
company = Company.from_dict(company_dict)

folders_list = client.orgs.get_folders(company_id=1)
folders = [Folder.from_dict(f) for f in folders_list]
```

**Available Models:**
- `Device` - Device information
- `Company` - Company/organization information
- `Folder` - Folder information
- `DeviceData` - Device telemetry data
- `DeviceEvent` - Device events
- `DeviceCommand` - Device commands

## Configuration

### Environment Variables

You can set your service account token via environment variables:

```bash
export CONNECT_API_TOKEN="your-service-account-token"
export CONNECT_API_BASE_URL="https://capi.trintel.co.za"
```

```python
import os
from trinity_connect_client import ConnectClient

client = ConnectClient(
    api_version="v4",
    base_url=os.getenv("CONNECT_API_BASE_URL"),
    token=os.getenv("CONNECT_API_TOKEN")
)
```

## Migration from v0.1.x to v0.2.0

Version 0.2.0 introduces breaking changes to authentication:

### What Changed
- Credentials-based authentication (email/password) has been removed
- Service account token authentication is now required
- Token caching logic has been removed (tokens are long-lived)
- The `auth` module and login endpoint are no longer available

### Upgrading

**Before (v0.1.x):**
```python
client = ConnectClient(
    base_url="https://capi.trintel.co.za",
    credentials={
        "email": "user@example.com",
        "password": "password"
    },
    cache=cache_instance  # Optional
)
```

**After (v0.2.0):**
```python
client = ConnectClient(
    base_url="https://capi.trintel.co.za",
    token="your-service-account-token"
)
```

**How to get a service account token:**
Contact your Trinity IoT administrator to generate a service account token for your application.

## Device Commands

The library supports two types of commands for devices:

### Issue Custom Commands

Send custom RPC commands directly to devices:

```python
# Issue a custom command to a device by UID
command = {
    "rpc": "reboot",
    "args": ["force"],
    "pid": "0",
    "ttl": 300,
    "qos": 0
}
response = client.devices.issue_command_by_uid("device-uid-123", command)
```

### Issue Stored Commands

Issue pre-configured stored commands (automation scripts) by their numeric code:

```python
# Issue a stored command
response = client.devices.issue_stored_command_by_uid(
    device_uid="device-uid-123",
    command_code=240  # Stored command code
)
```

Stored commands are pre-configured automation scripts in Connect, referenced by their numeric code (e.g., 240, 248) rather than direct device-level RPCs.

## Device Management

The library provides comprehensive device management operations:

### Device Retrieval

```python
# Get device by ID (returns dict)
device = client.devices.get(device_id=123)

# Get device by UID
device = client.devices.get_by_uid(device_uid="DEVICE123456789")

# Get latest data by UID
data = client.devices.get_latest_data_by_uid("DEVICE123456789")

# Get events by UID
events = client.devices.get_events_by_uid("DEVICE123456789")

# Get commands by UID
commands = client.devices.get_commands_by_uid("DEVICE123456789")

# List devices by folder
devices = client.devices.list_by_folder(folder_id=5)
```

### Device Creation

```python
# Create a new device
from trinity_connect_client import Device

device_data = {
    "folder": 5,
    "uid": "DEVICE123456789",  # 15-20 alphanumeric uppercase characters
    "name": "My Device",
    "description": "Device description"
}
device = client.devices.create(device_data)
# Returns Device model instance
print(f"Created device: {device.name}")
```

### Device Update (Partial)

Use `update_by_uid` for partial updates - only specified fields change:

```python
# Partial update - omitted fields remain unchanged
device = client.devices.update_by_uid("DEVICE123456789", {
    "name": "Updated Name",
    "description": "New description",
    "folder": 10  # Move to different folder
})
```

### Device Overwrite (Full Replacement)

Use `overwrite_by_uid` for full replacement - all fields must be specified:

```python
# Full replacement - requires folder and uid
device = client.devices.overwrite_by_uid("DEVICE123456789", {
    "folder": 5,
    "uid": "DEVICE123456789",  # Required
    "name": "Overwritten Device",
    "description": "Complete replacement",
    "serial_number": "SN12345"
})
```

**Note:** `update_by_uid` (PATCH) vs `overwrite_by_uid` (PUT):
- `update_by_uid`: Partial update, omitted fields remain unchanged
- `overwrite_by_uid`: Full replacement, all required fields must be provided

### Device Deletion

Permanently delete a device:

```python
# Delete device by UID (returns None)
client.devices.delete_by_uid("DEVICE123456789")
```

**Warning:** Deletion is permanent and irreversible. Consider using lifecycle states for non-destructive deactivation.

## Error Handling

The library raises specific exceptions for different error conditions:

```python
from trinity_connect_client.exceptions import (
    ResourceNotFoundError,
    UnauthorisedError,
    ConnectAPIError
)

try:
    device = client.devices.get(device_id=123)
except ResourceNotFoundError:
    print("Device not found (404)")
except UnauthorisedError:
    print("Authentication failed (401)")
except PermissionError:
    print("Access forbidden (403)")
except ConnectAPIError as e:
    print(f"API error: {e}")
except ValueError as e:
    print(f"Invalid input: {e}")
```

## Development

### Setting up Development Environment

```bash
# Clone the repository
git clone https://github.com/trinity-telecomms/connect-py-client.git
cd connect-py-client

# Install dependencies with uv
uv sync

# Run tests
uv run pytest

# Run linting
uv run ruff check .
```

### Running Tests

```bash
# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=trinity_connect_client

# Run specific test file
uv run pytest tests/modules/devices/test_devices_api.py
```

### Building the Package

This project uses the `uv_build` backend for building distributions:

```bash
# Build both wheel and source distribution
uv build

# Build only wheel
uv build --wheel

# Build only source distribution
uv build --sdist

# Build to a specific directory
uv build --out-dir dist/
```

The built distributions will be available in the `dist/` directory.

## Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Add tests for new functionality
5. Run the test suite (`uv run pytest`)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request

## License

This project is licensed under the MIT Licence - 
see the [LICENCE](LICENSE) file for details.