Metadata-Version: 2.4
Name: atlas-asset-client
Version: 0.2.4
Summary: Async HTTP client for Atlas Command.
Author: ATLAS Team
License-Expression: MIT
Project-URL: Homepage, https://github.com/atlas/command
Project-URL: Repository, https://github.com/atlas/command
Project-URL: Documentation, https://github.com/atlas/command/wiki
Keywords: atlas,command,http,asset
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Software Development :: Libraries
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: <3.13,>=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Dynamic: license-file

# Atlas Command HTTP Client (Python)

`atlas-asset-client` is a lightweight async wrapper around the Atlas Command REST API. It replaces the
retired WebSocket helpers and provides strongly-typed convenience methods for working with
entities, tasks, objects, and query endpoints via HTTP.

## Installation

```bash
pip install atlas-asset-client
```

During local development inside this repository:

```bash
pip install -e Atlas_Command/connection_packages/atlas_asset_ws_client
```

## Quickstart

```python
import asyncio
from atlas_asset_ws_client import AtlasCommandHttpClient

async def main() -> None:
    async with AtlasCommandHttpClient("http://localhost:8000") as client:
        entity = await client.create_entity(
            entity_id="asset-1",
            entity_type="asset",
            alias="Demo Asset",
            components={"telemetry": {"latitude": 40.7, "longitude": -74.0}},
        )
        print("Created entity:", entity["entity_id"])

        tasks = await client.list_tasks(limit=10)
        print("Existing tasks:", [t["task_id"] for t in tasks])

        snapshot = await client.get_full_dataset()
        print("Snapshot counts:", {k: len(v) for k, v in snapshot.items()})

        with open("mission_video.mp4", "rb") as video:
            stored = await client.create_object(
                file=video,
                object_id="mission-video-1",
                usage_hint="mission_video",
                referenced_by=[{"entity_id": "asset-1"}],
            )
            print("Uploaded object:", stored["object_id"])

asyncio.run(main())
```

## Entity Types Guide

Atlas Command supports several entity types, each with different purposes and component structures. All entities are created using the `create_entity()` method, but the `entity_type` parameter and `components` structure differ based on what you're representing.

### Assets

**Purpose:** Assets represent taskable autonomous agents that can execute commands and report telemetry. Examples include drones, rovers, security cameras, and other controllable hardware.

**When to use:** Register any physical or virtual device that can receive tasks from Atlas Command.

**Required fields:**
- `entity_id`: Unique identifier for the asset (string)
- `entity_type`: Must be `"asset"` (string)
- `alias`: Human-readable name for the asset (string)

**Common components:**
- `telemetry`: Location and movement data (`latitude`, `longitude`, `altitude_m`, `speed_m_s`, `heading_deg`)
- `task_catalog`: Supported task types the asset can execute
- `health`: System status (e.g., `battery_percent`)
- `communications`: Connection state (`link_state`)
- `sensor_refs`: Array of attached sensor configurations
- `media_refs`: Array of object references for camera feeds or thumbnails

**Example:**

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    asset = await client.create_entity(
        entity_id="drone-alpha-01",
        entity_type="asset",
        alias="Drone Alpha 01",
        components={
            "telemetry": {
                "latitude": 40.7128,
                "longitude": -74.0060,
                "altitude_m": 120,
                "speed_m_s": 8.2,
                "heading_deg": 165
            },
            "task_catalog": {
                "supported_tasks": ["move_to_location", "survey_grid"]
            },
            "health": {
                "battery_percent": 76
            },
            "communications": {
                "link_state": "connected"
            }
        }
    )
```

### Tracks

**Purpose:** Tracks represent observed entities detected by sensors or other assets. They are passive entities that track movement and characteristics of detected objects, but cannot receive tasks.

**When to use:** Register any detected object or entity that needs to be monitored but not controlled. Examples include vehicles, people, or other objects detected by security cameras or radar systems.

**Required fields:**
- `entity_id`: Unique identifier for the track (string)
- `entity_type`: Must be `"track"` (string)
- `alias`: Human-readable name for the track (string)

**Common components:**
- `telemetry`: Current location and movement data
- `mil_view`: Classification and tracking information (`classification`: friendly/hostile/neutral/unknown/civilian, `last_seen`)
- `sensor_refs`: Sensors that detected this track
- `media_refs`: Object references for images or videos of the track

**Example:**

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    track = await client.create_entity(
        entity_id="target-alpha",
        entity_type="track",
        alias="Target Alpha",
        components={
            "telemetry": {
                "latitude": 40.7128,
                "longitude": -74.0060,
                "altitude_m": 120,
                "speed_m_s": 8.2,
                "heading_deg": 165
            },
            "mil_view": {
                "classification": "unknown",
                "last_seen": "2025-11-23T10:05:00Z"
            }
        }
    )
```

### Geofeatures

**Purpose:** Geofeatures represent geographic features or zones on the map. They can be points, lines, polygons, or circles representing waypoints, routes, boundaries, restricted areas, or other geographic annotations.

**When to use:** Register any geographic annotation that needs to be displayed on the map. Common use cases include waypoints, patrol routes, no-fly zones, survey areas, or boundaries.

**Required fields:**
- `entity_id`: Unique identifier for the geofeature (string)
- `entity_type`: Must be `"geofeature"` (string)
- `alias`: Human-readable name for the geofeature (string)
- `components.geometry`: Geometry definition based on type

**Geometry types:**

#### Point Geofeature

A single coordinate location. Use for waypoints or point-of-interest markers.

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    point = await client.create_entity(
        entity_id="waypoint-alpha",
        entity_type="geofeature",
        alias="Waypoint Alpha",
        components={
            "geometry": {
                "type": "Point",
                "coordinates": [-74.0060, 40.7128]  # [longitude, latitude]
            }
        }
    )
```

#### LineString Geofeature

A path or route defined by multiple coordinates. Use for patrol routes, flight paths, or boundaries.

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    linestring = await client.create_entity(
        entity_id="patrol-route-alpha",
        entity_type="geofeature",
        alias="Patrol Route Alpha",
        components={
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [-74.0060, 40.7128],
                    [-74.0070, 40.7130],
                    [-74.0080, 40.7135],
                    [-74.0090, 40.7140]
                ]  # Array of [longitude, latitude] pairs
            }
        }
    )
```

#### Polygon Geofeature

A closed area defined by coordinates. The first and last coordinate must be the same to close the polygon. Use for restricted zones, survey areas, or regions of interest.

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    polygon = await client.create_entity(
        entity_id="area-of-interest-alpha",
        entity_type="geofeature",
        alias="Area of Interest Alpha",
        components={
            "geometry": {
                "type": "Polygon",
                "coordinates": [[
                    [-74.0060, 40.7128],
                    [-74.0070, 40.7128],
                    [-74.0070, 40.7130],
                    [-74.0060, 40.7130],
                    [-74.0060, 40.7128]  # Must close the polygon
                ]]  # Note: coordinates is an array of coordinate rings
            }
        }
    )
```

#### Circle Geofeature

A circular area defined by a center point and radius. Use for circular zones, coverage areas, or proximity alerts.

```python
async with AtlasCommandHttpClient("http://localhost:8000") as client:
    circle = await client.create_entity(
        entity_id="perimeter-epsilon",
        entity_type="geofeature",
        alias="Perimeter Epsilon",
        components={
            "geometry": {
                "point_lat": 40.7128,  # Center latitude
                "point_lng": -74.0060,  # Center longitude
                "radius_m": 500  # Radius in meters
            },
            "geometry_type": "circle"
        }
    )
```

**Common components for geofeatures:**
- `geometry`: Geometry definition (required)
- `geometry_type`: Explicit type specification (for circles: `"circle"`)
- `description`: Human-readable description of the geofeature
- `mil_view`: Classification metadata if applicable

## Features

- Uses `httpx.AsyncClient` under the hood with pluggable transport/timeouts.
- Convenience methods for every public endpoint:
  - `list_entities`, `get_entity`, `create_entity`, `update_entity`, `delete_entity`,
    `get_entity_by_alias`, `update_entity_telemetry`
  - `list_tasks`, `create_task`, `get_task`, `update_task`, `delete_task`,
    `get_tasks_by_entity`, `start_task`, `complete_task`, `fail_task`
- `list_objects`, `create_object` (uploads a file via `/objects/upload`), `get_object`,
- `update_object`, `delete_object`,
- `get_objects_by_entity`, `get_objects_by_task`, `find_orphaned_objects`,
- `add_object_reference`, `remove_object_reference`, `get_object_references`,
- `validate_object_references`, `cleanup_object_references`
  - `get_changed_since`, `get_full_dataset`
- Optional bearer token support via the `token=` constructor parameter.
- Context manager support (`async with client:`) to manage connection lifecycle.

## Field reference

### Client configuration

- `AtlasCommandHttpClient(base_url, *, token=None, timeout=10.0, transport=None)` – requires `base_url`,
  optional `token`, `timeout`, and `transport`.

### Entities

- `list_entities(*, limit=100, offset=0)` – optional pagination parameters based on defaults.
- `get_entity(entity_id)` – requires `entity_id`.
- `get_entity_by_alias(alias)` – requires `alias`.
- `create_entity(*, entity_id, entity_type, alias, components=None)` – requires `entity_id`, `entity_type`, and `alias`;
  `components` are optional.
- `update_entity(entity_id, *, components=None)` – requires `entity_id`, optional component patch.
- `delete_entity(entity_id)` – requires `entity_id`.
- `update_entity_telemetry(entity_id, *, latitude=None, longitude=None, altitude_m=None, speed_m_s=None, heading_deg=None)`
  – requires `entity_id`; telemetry values are optional and only set when provided.

### Tasks

- `list_tasks(*, status=None, limit=25)` – optional `status` and page size.
- `get_task(task_id)` – requires `task_id`.
- `create_task(*, task_id, components=None)` – requires `task_id`; `components` optional.
- `update_task(task_id, *, components=None)` – requires `task_id`; `components` optional.
- `delete_task(task_id)` – requires `task_id`.
- `get_tasks_by_entity(entity_id, *, status=None, limit=25)` – requires `entity_id`; filters optional.
- `start_task(task_id)` / `complete_task(task_id)` – each requires `task_id`.
- `fail_task(task_id, *, error_message=None, error_details=None)` – requires `task_id`; error info optional.

### Objects

- `list_objects(*, content_type=None, limit=100, offset=0)` – optional filters.
- `get_object(object_id)` – requires `object_id`.
- `create_object(file, *, object_id, usage_hint=None, referenced_by=None)` – requires `file` data and `object_id`;
  `usage_hint` and `referenced_by` optional.
- `update_object(object_id, *, usage_hints=None, referenced_by=None)` – requires `object_id`; metadata optional.
- `delete_object(object_id)` – requires `object_id`.
- `get_objects_by_entity(entity_id, *, limit=50)` – requires `entity_id`, optional pagination.
- `get_objects_by_task(task_id, *, limit=50)` – requires `task_id`, optional pagination.
- `add_object_reference(object_id, *, entity_id=None, task_id=None)` / `remove_object_reference(...)`
  – require `object_id`; provide either `entity_id` or `task_id` to target the reference.
- `find_orphaned_objects(*, limit=100)` – optional limit.
- `get_object_references(object_id)` / `validate_object_references(object_id)` / `cleanup_object_references(object_id)` –
  each requires `object_id`.

### Queries

- `get_changed_since(since, *, limit_per_type=None)` – requires `since`; optional per-type limit.
- `get_full_dataset(*, entity_limit=None, task_limit=None, object_limit=None)` – filters are optional.

## Configuration

```python
client = AtlasCommandHttpClient(
    "https://atlas.example.com",
    token="my-api-token",
    timeout=30.0,
)
```

You can also pass a custom `httpx` transport for testing:

```python
transport = httpx.MockTransport(my_handler)
client = AtlasCommandHttpClient("http://testserver", transport=transport)
```

## Breaking changes

- `create_entity` now requires `alias` and no longer accepts `published_at`, `updated_at`, or `extra`.
- `create_task` / `update_task` drop `status`/`extra`; lifecycle helpers no longer accept `started_by` or `result`.
- `create_object` is the only object-creation helper and always uploads a file via `/objects/upload`; storage metadata and sizing is server-managed.

## Testing

Run the suite with:

```bash
pip install -e .[dev]
pytest
```

The tests use `httpx.MockTransport` so they do not require a running Atlas Command instance.
