Skip to main content

Otensor — biblioteca Python para criar automações IoT em código

Project description

otensor

PyPI Python License

Python library for writing IoT automations as code — react to device events, run schedules, and trigger actions or notifications on the Otensor platform. Runs anywhere (laptop, server), not on the device itself.

PT-BR — Biblioteca Python para escrever automações IoT em código: reagir a eventos de dispositivos, rodar agendamentos e disparar ações ou notificações na plataforma Otensor. Roda em qualquer lugar (laptop, servidor), não no dispositivo. A documentação abaixo está em inglês.

from otensor import connect

bot = connect("sk-...", base_url="http://localhost:8000")

bot.when("living-room-sensor", "temperature").above(30).do("fan", "turn_on")
bot.every_day("18:00").do("porch-light", "turn_on")

bot.run()

Which package do I need?

Otensor ships two Python packages with different jobs:

otensor (this one) otensor-sdk
Runs Anywhere (laptop, server) On the hardware (Raspberry Pi, Linux board)
Job React to events, write automations Publish telemetry, execute commands
Talks to REST API MQTT broker + 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) and its base_url
  • An API key (sk-…) generated in the dashboard

Install

pip install otensor

Optional extras: otensor[email] (SMTP alerts), otensor[industrial] (Modbus/OPC-UA/InfluxDB), otensor[integrations] (Google Calendar, Slack), or otensor[all] for everything.

Configuration

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

base_url has no default and must always be passed explicitly — a silent default would point at the wrong instance.

Two modes

Simple mode — fluent chaining

No classes, no decorators. Best for straightforward rules.

import os
from dotenv import load_dotenv
from otensor import connect

load_dotenv()

bot = connect(
    api_key=os.environ["OTENSOR_API_KEY"],
    base_url=os.environ["OTENSOR_BASE_URL"],
)

bot.when("living-room-sensor", "temperature").above(30).do("fan-relay", "turn_on")
bot.when("living-room-sensor", "temperature").below(25).do("fan-relay", "turn_off")

bot.when("garage-door", "status").equals("open") \
   .notify(email="owner@home.com", message="Garage door was opened!")

bot.weekdays("08:00").do("office-gate", "open")
bot.every_day("06:00").do("garden-valve", "open")

bot.run()  # blocks until Ctrl-C

Advanced mode — decorators + AutomationContext

Full control: multiple conditions, custom logic, direct access to the event context.

from otensor import Client, on, schedule, when

client = Client(api_key="sk-...", base_url="http://localhost:8000")


@on(client.device("living-room-sensor").property("temperature").above(30))
def turn_on_fan(ctx):
    ctx.command("fan-relay", action="turn_on")
    ctx.log(f"Fan turned on — temperature: {ctx.value}°C")


@on(client.device("living-room-sensor").property("temperature").below(25))
@when(lambda ctx: ctx.value is not None)
def turn_off_fan(ctx):
    ctx.command("fan-relay", action="turn_off")


@schedule("0 8 * * 1-5", client=client)
def office_opening(ctx):
    ctx.command("office-gate", action="open")
    ctx.notify(channel="whatsapp", message="Office is open.")


client.run()

test_mode() and simulate() let you exercise handlers without a live MQTT connection — useful in unit tests:

bot = connect(api_key="sk-test", base_url="http://localhost:8000").test_mode()
bot.when("sensor-01", "temperature").above(30).do("fan-relay", "turn_on")
bot.simulate("sensor-01", "temperature", 35)

API reference

Simple mode (SimpleBot)

Symbol Purpose
connect(api_key, *, base_url, tenant_id=None) -> SimpleBot Entry point
.when(device_id, property_name) Start a condition — chain .above(v) / .below(v) / .equals(v) / .changes()
...trigger.do(target_device, action) Run an action when the condition matches
...trigger.notify(email=None, *, message="", channel=None) Send an email and/or a channel notification
.every_day(time) / .weekdays(time) / .weekends(time) Start a schedule (time is "HH:MM") — chain .do(device_id, action)
.run() Connect to MQTT and block until Ctrl-C
.test_mode() / .simulate(device_id, property_name, value) Exercise handlers without a live connection

Advanced mode (Client, decorators, AutomationContext)

Symbol Purpose
Client(api_key, *, base_url, tenant_id=None) Entry point
client.device(device_id).property(name) Build a condition — same .above/.below/.equals/.changes() chain
@on(condition) Register a handler for a device event
@schedule(cron, *, client=None) Register a handler on a cron schedule
@when(predicate) Extra filter stacked on top of @on
client.run() Connect to MQTT, start the scheduler, block until Ctrl-C
ctx.value / .device_id / .property / .unit / .ts Event data injected into every handler
ctx.command(device_id, *, action, payload=None, slot=None) Send a tracked command (ack/history)
ctx.send_email(to, *, subject="", body="") Send an email via the platform
ctx.notify(*, channel="email", message="") Send a notification via the platform
ctx.log(message, *, level="info") Structured log
ctx.history(n=10) Last n readings for the current device/property
ctx.audit(*, action, details=None) Write an audit entry

Integrations

Symbol Purpose
Email.configure(*, smtp_host, smtp_port=587, username, password, from_addr) One-time SMTP setup (otensor[email])
Email.send(to, *, subject="", body="") Send an email standalone (outside a handler)
Webhook.post(url, *, payload=None, headers=None, timeout=10.0) POST JSON to any URL, raises on non-2xx

Roadmap

Not in 0.1 — dropped when the public API was cut down to what actually works, tracked for a later release:

  • Duration filters (for_duration(minutes=...)) — trigger only after a condition holds for a period, not on the first match.
  • Pipeline — chained source → publish → condition → action, for direct hardware reads (e.g. Modbus) without going through a device already published to the platform.
  • @debounce — suppress repeated firings within a time window.
  • @learn — record handler behavior for the AI Engine to learn from.
  • @predictive — trigger on ML-detected anomalies rather than fixed thresholds.
  • ctx.ask_user() — pause a handler and wait for human input via notification.

Versioning

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

otensor~=0.1.0

License

MIT

Project details


Download files

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

Source Distribution

otensor-0.1.0.tar.gz (97.4 kB view details)

Uploaded Source

Built Distribution

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

otensor-0.1.0-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file otensor-0.1.0.tar.gz.

File metadata

  • Download URL: otensor-0.1.0.tar.gz
  • Upload date:
  • Size: 97.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for otensor-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8dc2a695dc3f40d59bca63c5789cee4df6797e4efcb06817829b9624158a1921
MD5 490b12c60414981cbf77966c6132bd60
BLAKE2b-256 8ec9dc6f17d4ed1ed4055f2f0c1a1e3abac3d27338ceef739d7d9f347ced7fce

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor-0.1.0.tar.gz:

Publisher: publish-lib-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-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: otensor-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for otensor-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 53db08a6ea05d1b60577e95b24e2c4d0e655c090cfca7d1ef7ec18db4f627dc4
MD5 8069d80d4b7a0c2a075ee5518534283d
BLAKE2b-256 f50adf865464d9ca2133cae7a6854d1d295bd30b7a6eddcaf4a944c32a164981

See more details on using hashes here.

Provenance

The following attestation bundles were made for otensor-0.1.0-py3-none-any.whl:

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

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page