Metadata-Version: 2.4
Name: zainar-halo
Version: 0.1.3
Summary: ZaiNar RF halo awareness — egocentric proximity for tracked nodes
Author: Zainar
License: Apache-2.0
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: device-connect-edge<0.3,>=0.2.5
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"

# zainar-halo

ZaiNar RF halo awareness — egocentric proximity for Device Connect nodes.

A robot's onboard sensors see line-of-sight only. ZaiNar's RF network sees
every tagged entity in a facility, through walls and around blind corners.
`zainar-halo` packages that awareness as Device Connect–native primitives:
a **halo** (per-node view of surrounding entities) computed locally by the
robot from a zone broadcast, with no GPU and no training run required.

## Install

```bash
pip install zainar-halo
```

Requires `device-connect-edge>=0.2.5`.

## What's in the box

| Class | What it does |
|---|---|
| `HaloConfig` | Per-node filter spec — radius, entity types, time-decay, detail |
| `Position` | One tracked entity's 3D position (cm-int, spatial_vocab wire types) |
| `HaloProvider` | Computes halos; also provides `heartbeat_provider_for()` for stock DC |
| `RobotDriver` | `DeviceDriver` subclass with `@on` subscription + local `compute_halo()` |
| `TMIPayload` / `TMIEntity` | TMI (Traffic Management Information) wire contract |

## Quick start — robot subscriber

```python
from zainar_halo import HaloConfig, RobotDriver

class MyRobot(RobotDriver):
    async def _on_halo_computed(self, halo: dict) -> None:
        # feed to costmap, planner, alerting...
        nearest = halo["nearest_entity_type"]
        dist_m  = (halo["nearest_distance_cm"] or 0) / 100
        print(f"{nearest} at {dist_m:.1f}m")

robot = MyRobot(halo_config=HaloConfig(radius_cm=1500, entity_types=["forklift", "human"]))
# register with DeviceRuntime and connect — the @on subscription is automatic
```

## Quick start — heartbeat provider (stock Device Connect, no core change)

```python
from zainar_halo import HaloConfig, HaloProvider, Position

provider = HaloProvider()
provider.set_config("amr-7", HaloConfig(radius_cm=1500, zone_of=my_zone_fn))

# returns a callable for DeviceRuntime.set_heartbeat_provider()
heartbeat_fn = provider.heartbeat_provider_for("amr-7", live_positions, now_fn=time.time)
runtime.set_heartbeat_provider(heartbeat_fn)
```

## Units and conventions

- **Lengths**: centimeters as `int` (`distance_cm`, `radius_cm`, `x_cm`/`y_cm`/`z_cm`)
- **Speeds**: cm/s as `int` (`closing_speed_cmps`, `vx_cmps`)
- **Entity types**: spatial_vocab wire values — `human`, `amr`, `forklift`, `drone`, `pallet`, …
- **3D throughout** — distance is 3D Euclidean; a drone 4m overhead is 400cm away

## See also

- `zainar-halo-agent-tools` — FastMCP server exposing halo tools to LLM agents
- `CLAUDE.md` at repo root — full architecture, scenario, and integration guide
