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.6.0.tar.gz (87.3 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.6.0-py3-none-any.whl (18.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: otensor_sdk-0.6.0.tar.gz
  • Upload date:
  • Size: 87.3 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.6.0.tar.gz
Algorithm Hash digest
SHA256 85a9c6589069e6c5ecd300743c90a2db86a51b07ade1db9ea29f87af5c9a73c5
MD5 c6f8878bf166c4059869b359fe78125e
BLAKE2b-256 c1608e447f3922efd15c0bef0add64d41a95f2448c16ba079eada1b4c4348370

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor_sdk-0.6.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.6.0-py3-none-any.whl.

File metadata

  • Download URL: otensor_sdk-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 18.0 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.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ee2ea72ebf0681694dca9ab3f4ec83b5e0e3e9bfd8432ea27a172be1db7cfe68
MD5 9f6cd3009523033ccec7c25476c28630
BLAKE2b-256 6938e4b560d8e60765bfd1f9eb8811c7280f6fc7d9efa9401c1140e7f673973c

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor_sdk-0.6.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

This release

0.6.0 This release

2 files

0.5.0

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