Skip to main content

otensor-sdk

PyPI Python License

Python SDK that runs on your device — publishes telemetry and receives commands from the Otensor IoT platform. Built for makers on Raspberry Pi and other Linux hardware.

PT-BR — SDK Python que roda no dispositivo: publica telemetria e recebe comandos da plataforma Otensor. Feito para makers com Raspberry Pi e outro hardware Linux. A documentação abaixo está em inglês.

from otensor_sdk import OtensorSDK

device = OtensorSDK().connect()  # reads OTENSOR_API_BASE_URL/OTENSOR_API_KEY/OTENSOR_DEVICE_ID
sensor = device.slot("main")

sensor.publish_property("temperature", 24.5)

@sensor.on_action("set_pixels")
def light_up(payload: dict) -> None:
    print("platform asked for:", payload)

Which package do I need?

Otensor ships two Python packages with different jobs:

otensor-sdk (this one) otensor
Runs On the hardware (Raspberry Pi, Linux board) Anywhere (laptop, server)
Job Publish telemetry, execute commands React to events, write automations
Talks to MQTT broker + REST API REST API

Use both if you want a device that reports data and logic that reacts to it. They are independent — neither requires the other.

Requirements

  • Python 3.11+
  • An Otensor instance (self-hosted or managed)
  • An API key (sk-…) and a device registered in the dashboard

Install

pip install otensor-sdk

Hardware wrappers (otensor_sdk.sensehat, otensor_sdk.gpio) rely on sense-hat and gpiozero, which ship with Raspberry Pi OS. They are not pip dependencies of this package, so installing here never drags in hardware libraries you don't need.

Configuration

OtensorSDK() reads its config from environment variables (loaded from a .env file automatically, or from whatever's already exported) — no need to pass them as arguments every time:

OTENSOR_API_BASE_URL=https://api.your-otensor.example   # or http://localhost:8000
OTENSOR_API_KEY=sk-...
OTENSOR_DEVICE_ID=...

Pass any of api_base_url / api_key / device_id explicitly to OtensorSDK(...) to override the corresponding env var — useful for multi-device scripts or tests. If a value is missing from both the constructor and the environment, OtensorSDK raises OtensorConfigError right away (no silent fallback to the wrong host).

Quick start

from otensor_sdk import OtensorSDK

sdk = OtensorSDK()  # OTENSOR_API_BASE_URL / OTENSOR_API_KEY / OTENSOR_DEVICE_ID from .env

device = sdk.connect()          # fetches MQTT credentials and connects
main = device.slot("main")      # a slot is one capability of the device

# Register handlers BEFORE publishing, so a command arriving right after
# connect() is not missed.
@main.on_action("set_pixels")
def handle_set_pixels(payload: dict) -> None:
    print("light up:", payload)

main.publish_property("temperature", 24.5)

Slots

A device declares capabilities, each bound to a named slot — a board can carry a sensor unit on main, a relay on relay1 and a motion sensor on pir1 at the same time. Properties and actions are always scoped to a slot, so the platform validates each reading against the right schema.

device.slot("main").publish_property("humidity", 61.2)
device.slot("relay1").publish_property("state", True)

Publishing on a schedule

TelemetryPublisher groups every source into one message per tick, instead of one message per property:

from otensor_sdk import TelemetryPublisher

publisher = TelemetryPublisher(device, interval=5.0)
publisher.set_source("main", "temperature", read_temperature)
publisher.set_source("main", "humidity", read_humidity)

publisher.run_forever()          # blocks until Ctrl-C
# publisher.publish_once()       # or drive the loop yourself

Complete example — no hardware required

Runs anywhere with simulated sensors, so you can see the full loop before touching a Raspberry Pi:

import random
from otensor_sdk import OtensorSDK, TelemetryPublisher

sdk = OtensorSDK()
device = sdk.connect()
main = device.slot("main")

@main.on_action("set_pixels")
def set_pixels(payload: dict) -> None:
    print("LED matrix ->", payload.get("pixels"))

@main.on_action("clear_display")
def clear_display(payload: dict) -> None:
    print("LED matrix cleared")

publisher = TelemetryPublisher(device, interval=5.0)
publisher.set_source("main", "temperature", lambda: round(random.uniform(18, 32), 1))
publisher.set_source("main", "humidity", lambda: round(random.uniform(40, 80), 1))

try:
    publisher.run_forever()
except KeyboardInterrupt:
    device.disconnect()

Raspberry Pi hardware

from otensor_sdk.sensehat import SenseHatUnit, LedMatrixSwitch
from otensor_sdk.gpio import DigitalInput, DigitalOutput

unit = SenseHatUnit()
unit.bind("main", device, publisher)          # sensors + set_pixels/clear_display, wired in one call

LedMatrixSwitch(sense_hat=unit._sense).bind("led", device)  # same LED matrix as an on/off switch
DigitalOutput(pin=17).bind("relay1", device)  # actions: turn_on / turn_off / toggle
DigitalInput(pin=4).bind("pir1", device)      # publishes motion_detected on edge

LedMatrixSwitch exposes the LED matrix as a plain switch (turn_on/turn_off/toggle, state property) — fills the matrix with a solid color to turn on, clears it to turn off. Same contract as DigitalOutput, no set_pixels payload needed for simple on/off control.

bind() registers the action handlers and the telemetry sources for that slot, so you don't wire each property by hand.

Reliability

Built for devices that stay online for weeks on flaky networks:

  • Reconnect-safe commands — the cmd topic is re-subscribed on every connect, including automatic reconnects. Without this, telemetry keeps flowing while commands go silently mute after the first network blip.
  • State republishdevice.on_reconnect(fn) fires on first connect and every reconnect. Hardware wrappers use it to re-publish current state, which the dashboard would otherwise show as stale until the next physical change.
  • Last Will — the broker marks the device offline if it drops without a clean disconnect.
  • Local ackdevice.on_ack(fn) fires in-process the moment this device confirms a command, with {commandId, slot, action, success, error?}. Lets local code know its own command finished without a REST round-trip.

API reference

Symbol Purpose
OtensorSDK(api_base_url=None, api_key=None, device_id=None) Entry point; each param falls back to OTENSOR_API_BASE_URL/OTENSOR_API_KEY/OTENSOR_DEVICE_ID
.connect() -> Device Fetches MQTT session and connects
Device.slot(name) -> SlotHandle Handle scoped to one capability
Device.publish_telemetry(nested) Publish {slot: {property: value}} directly
Device.on_reconnect(fn) / .on_ack(fn) Lifecycle callbacks
Device.disconnect() Stop the MQTT loop
SlotHandle.publish_property(name, value) One reading for this slot
SlotHandle.on_action(name) Decorator registering a command handler
TelemetryPublisher(device, interval) Batched periodic publishing
.set_source(slot, property, fn) Register a value producer
.run_forever() / .publish_once() Drive the publish loop

Exceptions: OtensorError (base), OtensorAuthError, OtensorSessionError, OtensorHardwareError.

Versioning

Semantic versioning. While on 0.x, a minor bump may carry breaking changes — pin the minor version in production:

otensor-sdk~=0.2.0

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

otensor_sdk-0.5.0.tar.gz (86.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

otensor_sdk-0.5.0-py3-none-any.whl (17.8 kB view details)

Uploaded Python 3

File details

Details for the file otensor_sdk-0.5.0.tar.gz.

File metadata

  • Download URL: otensor_sdk-0.5.0.tar.gz
  • Upload date:
  • Size: 86.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for otensor_sdk-0.5.0.tar.gz
Algorithm Hash digest
SHA256 c25292f939fdcf5f3f19aef3d6de9081ecd17203f2015897b899f261b47ccce2
MD5 b5ca4de3bc627b9b0e50e80bfef0f9a8
BLAKE2b-256 fcaa319fd252be8fc059e213bc86dba30f63754fb128ac859b74bf84ea69fac7

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor_sdk-0.5.0.tar.gz:

Publisher: publish-sdk-python.yml on Oseiasdfarias/otensor-platform

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file otensor_sdk-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: otensor_sdk-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 17.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for otensor_sdk-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 514bf1dd34e4f8f9d7d63a40c475db348ee54109498484d34e39c82cd9f542d2
MD5 333518929212a12cbb5ed4a3f9880858
BLAKE2b-256 17eb66862b9681d5d83048d7618382060be3833c2affce3cf53c2f58fb50dc7d

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor_sdk-0.5.0-py3-none-any.whl:

Publisher: publish-sdk-python.yml on Oseiasdfarias/otensor-platform

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.6.0

2 files

This release

0.5.0 This release

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

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