Metadata-Version: 2.4
Name: hnnp-sdk
Version: 0.1.4
Summary: HNNP Python SDK
Author: NearID
License: UNLICENSED
Project-URL: Homepage, https://github.com/hawkeye17-warex/hnnp
Project-URL: Repository, https://github.com/hawkeye17-warex/hnnp
Keywords: nearid,hnnp,presence,ble,security,sdk
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# HNNP Python SDK

This directory contains the official Python SDK for interacting with the HNNP Cloud backend.
It provides a simple, clean API client plus webhook verification utilities.

All behavior MUST strictly follow: protocol/spec.md

---

## Features

- REST API client (requests-based)
- Webhook signature verification
- Typed dataclasses for events, links, sessions
- Error handling and pagination helpers

---

## Installation

pip install hnnp-sdk

Requires: Python 3.9+ and `requests`.

---

## Usage Example

from hnnp_sdk import HnnpClient

client = HnnpClient(
    api_key="YOUR_API_KEY_OR_ORG_TOKEN",
    org_id="org_123",
    base_url="https://api.hnnp.example",
    return_models=True
)

If you prefer raw JSON dictionaries, set `return_models=False`.

events = client.list_events()

client.link_presence_session(
    presence_session_id="psess_001",
    user_ref="emp_45"
)

---

## Webhook Verification

from hnnp_sdk import verify_webhook_signature

is_valid = verify_webhook_signature(
    secret=webhook_secret,
    timestamp=timestamp_header,
    raw_body=raw_body_bytes,
    signature_hex=signature_header,
)

---

## Flask Webhook Example

```python
import os
from flask import Flask, request, jsonify
from hnnp_sdk import verify_webhook_signature

app = Flask(__name__)

WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"]

@app.route("/hnnp/webhook", methods=["POST"])
def hnnp_webhook() -> "tuple[dict, int]" | "tuple[dict, int, dict]":
    signature = request.headers.get("X-HNNP-Signature", "")
    timestamp = request.headers.get("X-HNNP-Timestamp", "")
    raw_body = request.get_data()  # bytes

    if not verify_webhook_signature(WEBHOOK_SECRET, timestamp, raw_body, signature):
        return jsonify({"error": "invalid_webhook_signature"}), 400

    # At this point, request.json is trusted HNNP payload
    print("Received HNNP webhook:", request.json)
    return jsonify({"status": "ok"}), 200
```

---

## FastAPI Webhook Example

```python
import os
from fastapi import FastAPI, Request, HTTPException
from hnnp_sdk import verify_webhook_signature

app = FastAPI()
WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"]

@app.post("/hnnp/webhook")
async def hnnp_webhook(request: Request):
    signature = request.headers.get("X-HNNP-Signature", "")
    timestamp = request.headers.get("X-HNNP-Timestamp", "")
    raw_body = await request.body()  # bytes

    if not verify_webhook_signature(WEBHOOK_SECRET, timestamp, raw_body, signature):
        raise HTTPException(status_code=400, detail="invalid_webhook_signature")

    payload = await request.json()
    print("Received HNNP webhook:", payload)
    return {"status": "ok"}
```

---

## Methods

client.list_receivers()
client.create_receiver(...)
client.update_receiver(...)
client.list_events()
client.list_sessions()
client.list_links()
client.create_link(...)
client.link_presence_session(presence_session_id, user_ref)
client.activate_link(link_id)
client.revoke_link(link_id)
client.unlink(link_id)  # alias for revoke_link

# Extended org console APIs (requires org admin auth)
client.list_orgs(...)
client.get_org(org_id)
client.get_org_settings(org_id)
client.update_org_settings(org_id, settings)
client.get_org_config(org_id)
client.update_org_config(org_id, config)
client.get_org_metrics_realtime(org_id, params)
client.list_org_users(org_id)

client.list_org_sessions(...)
client.get_org_sessions_summary(...)
client.create_org_session(payload)
client.update_org_session(session_id, payload)
client.delete_org_session(session_id)
client.finalize_org_session(session_id)

client.get_presence_live(...)
client.get_presence_logs(...)
client.get_presence_events(...)
client.list_locations()
client.list_groups()
client.create_group(payload)
client.update_group(group_id, payload)
client.delete_group(group_id)
client.get_attendance(...)
client.export_attendance_csv(...)

client.get_billing_summary()
client.list_invoices()
client.get_invoice_pdf(invoice_id)

client.list_incidents(...)
client.get_incident(incident_id)
client.get_incident_rollcall(incident_id)
client.get_safety_status()
client.get_safety_summary()

---

## Typed models

Dataclasses are available in `hnnp_sdk.types` for receivers, events, sessions, billing, incidents, and safety summaries.

---

## File Structure

hnnp_sdk/
  client.py
  http.py
  webhook.py
  types.py
  __init__.py

tests/
  test_client.py
  test_webhook.py

---

## Environment Variables (optional)

WEBHOOK_SECRET=your_org_webhook_secret

---

## Notes

- HMAC verification MUST use exact rules defined in Section 10.3 of spec.md (v2).
- All network calls MUST use HTTPS
- Must not alter any packet format or cryptographic behavior
- Some endpoints require an **org admin JWT** (org console auth) instead of an API key.

---

## Pagination helpers

```python
for evt in client.iterate_events(org_id="org_123"):
    print(evt.id)

for sess in client.iterate_sessions(org_id="org_123"):
    print(sess.session_id)

for rcv in client.iterate_receivers(org_id="org_123"):
    print(rcv.receiver_id)
```

---

## Rate limiting

The client retries on `5xx` and `429` responses with exponential backoff.  
If `Retry-After` is provided, it is respected (up to 30s).

---

## Testing

python -m pytest -q

---

## Reference

Full protocol: ../../protocol/spec.md
