Skip to main content

ngits-drf-iot-client

Identity-agnostic Django/DRF client for the IoT ESB automation and command APIs. It lets any application (a single-tenant app, a multi-tenant panel, ...) act as a thin BFF over the shared IoT platform without duplicating domain logic — the same way ngits-drf-cube-proxy sits in front of analytics.

The automation domain (models, evaluation, command dispatch) lives in the IoT platform services. This package is only the transport + tenant-scoping glue used by the presentation layer.

Install

pip install ngits-drf-iot-client
# during local development, before it is published:
pip install -e ../ngits-drf-iot-client

Configure (host settings.py)

IOT_ESB_BASE_URL = "https://iot-esb.example.com"  # client appends /api/v1
IOT_ESB_TOKEN = env("IOT_ESB_TOKEN")              # sent as X-Token
IOT_CLIENT_IDENTITY_RESOLVER = "myapp.iot.resolve_scope"
# optional:
IOT_CLIENT_TIMEOUT = 10

The resolver maps an authenticated user to the tenant(s) they may access:

# myapp/iot.py  (single-tenant app)
def resolve_scope(user):
    tenant = get_tenant_for(user)                    # your own lookup
    return None if tenant is None else tenant.uuid   # None -> 403

# multi-tenant panel
def resolve_scope(user):
    return list(get_tenants_for(user))               # iterable of tenant UUIDs

Use in a DRF view

from rest_framework import viewsets
from rest_framework.response import Response
from iot_client import IotClientMixin

class AutomationViewSet(IotClientMixin, viewsets.ViewSet):
    def list(self, request):
        client = self.get_iot_client()
        return Response(client.automations.list(tenants=self.get_tenant_scope()))

    def create(self, request):
        client = self.get_iot_client()
        return Response(client.automations.create(request.data), status=201)

Client surface

IotEsbClient exposes one sub-client per ESB resource:

  • client.automations — list, get, create, update, delete, activate, deactivate, rule_types, actions;
  • client.alert_rules — list, get, create, update, delete, activate, deactivate, condition_kinds, list_executions, get_execution, ack_execution;
  • client.commands — create, get;
  • client.event_subscriptions — list, upsert, update, delete.

All read methods accept a tenants scope; errors raise IotClientError(status_code=...).

Alert-rule executions

client.alert_rules.list_executions(tenants=scope, acked=False, **{"from": since})
client.alert_rules.get_execution(execution_id, tenants=scope)
client.alert_rules.ack_execution(
    execution_id, acked_by=f"myapp:user:{user.pk}", comment="on it", tenants=scope
)

acked_by is an opaque, host-assigned identifier; the ESB treats a repeated ack as a no-op. The notification policy of a rule (notify_on_triggered, notify_on_resolved, reminder_after_minutes) is sent through the regular create / update payloads.

Receiving platform events (alert notifications)

The IoT platform does not notify people itself: it delivers domain events (alert.triggered, alert.resolved, alert.reminder, alert.acknowledged) to the hosts that subscribed to them, and each host decides who gets notified.

1. Settings

INSTALLED_APPS += ["iot_client"]          # for the management command

IOT_CLIENT = {
    "EVENTS_SECRET": env("IOT_EVENTS_SECRET"),        # HMAC key, shared with the platform
    "EVENTS_CONSUMER": "myapp",                       # host name in the subscription key
    "TENANT_UUIDS_PROVIDER": "myapp.iot.tenant_uuids",  # () -> iterable of tenant UUIDs
    # optional, default: all four alert.* types
    # "EVENT_TYPES": ["alert.triggered", "alert.resolved"],
}

2. Receiving endpoint

Each event is a JSON POST with the headers X-Event-Id, X-Event-Type, X-Event-Timestamp (unix seconds) and X-Event-Signature: sha256=<hex HMAC-SHA256(secret, "{timestamp}.{body}")>. IotEventSignaturePermission verifies the signature in constant time and rejects timestamps older/newer than 300 s:

from rest_framework.response import Response
from rest_framework.views import APIView
from iot_client.events import IotEventSignaturePermission

class IotEventsView(APIView):
    authentication_classes = []            # signature only -> invalid = 403
    permission_classes = [IotEventSignaturePermission]

    def post(self, request):
        event = request.data               # schema_version, event_id, event_type, ...
        if already_processed(event["event_id"]):   # dedupe: delivery is at-least-once
            return Response(status=200)
        handle(event)                      # any exception -> 5xx -> platform retries
        return Response(status=200)

Deduplication by event_id and handling are the host's job. Without EVENTS_SECRET the permission raises IotClientImproperlyConfigured. For other setups use verify_signature(secret, body, timestamp, signature) directly, or override IotEventSignaturePermission.get_secret().

3. Registration (deploy step)

python manage.py iot_register_event_subscription --env prod \
    --target https://myapp.example.com/api/v1/iot/events/ [--tenant UUID ...]

For every tenant (--tenant, or all from TENANT_UUIDS_PROVIDER) it upserts one subscription keyed by (tenant, env, consumer) with the configured secret and EVENT_TYPES, so running it on every deploy never creates duplicates. It does not send is_active, so a subscription switched off on the platform stays off. The command exits non-zero if any tenant failed.

Metadata

Release files for ngits-drf-iot-client 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ngits-drf-iot-client 0.5.0
File Size Uploaded Provenance
ngits_drf_iot_client-0.5.0.tar.gz 22.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ngits-drf-iot-client 0.5.0
File Interpreter ABI Platform
ngits_drf_iot_client-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 43.5 kB

Release files / ngits_drf_iot_client-0.5.0.tar.gz

Download URL ngits_drf_iot_client-0.5.0.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2e9b09351fa689727e72fab0b37244ff2ca37da28bc20174158b0d0c26e4ac15
BLAKE2b-256 checksum
How to use checksums
64694de5ec8fbc26a36183efc239402b3b3d74cf52e6aa68db5d271eb578a655
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release files / ngits_drf_iot_client-0.5.0-py3-none-any.whl

Download URL ngits_drf_iot_client-0.5.0-py3-none-any.whl
Size 21.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5d03cc49225ef7c2268d770f866940b4b2177b7d261df3738c748d314c590818
BLAKE2b-256 checksum
How to use checksums
774656ea088f5ad508a480a532ebae7b2e346eafe68a2c8dc1c1eedbc2e01192
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Security for ngits-drf-iot-client 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page