Skip to Content

IronFlock SDK

El SDK de IronFlock permite que tus aplicaciones de borde interactúen con la plataforma IronFlock. Gestiona la autenticación automáticamente cuando se ejecuta en un dispositivo registrado y proporciona funciones para publicar datos, consultar historial, llamar procedimientos remotos entre dispositivos y actualizar metadatos de dispositivos.

SDKPaqueteRequisitos
Pythonironflock en PyPIPython 3.8+
JavaScriptironflock en npmNode.js 18+ o navegador moderno

Instalación

pip install ironflock

O añade ironflock al archivo requirements.txt de tu app.

Inicio rápido

import asyncio from ironflock import IronFlock async def main(): while True: await ironflock.publish_to_table("sensordata", { "temperature": 22.5, "humidity": 60 }) await asyncio.sleep(5) ironflock = IronFlock(mainFunc=main) ironflock.run()

Cuando se usa dentro de un contenedor de app de IronFlock, el SDK lee las credenciales de conexión del entorno automáticamente — no se necesita configuración manual.

Opciones del constructor

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParámetroDescripción
mainFuncUna función asíncrona que se ejecuta una vez establecida la conexión
serial_numberSobrescribe el número de serie del dispositivo. Por defecto usa la variable de entorno DEVICE_SERIAL_NUMBER

Publicación de datos

publishToTable / publish_to_table

Publica un registro de datos en una tabla de flota. El nombre de la tabla debe coincidir con una tabla definida en el archivo data-template.yml de tu app. El SDK enruta los datos automáticamente a la base de datos del proyecto correspondiente.

await ironflock.publish_to_table("sensordata", { "temperature": 22.5, "humidity": 60, "device_id": "sensor-001" })

appendToTable / append_to_table

Añade datos a una tabla de flota usando una llamada a procedimiento remoto en lugar de pub/sub. Usa esto cuando necesites confirmación de que los datos fueron persistidos.

result = await ironflock.append_to_table("sensordata", { "temperature": 22.5, "humidity": 60 })

publishRowsToTable / publish_rows_to_table

Publica muchas filas en un solo mensaje (inserción masiva) en una tabla de flota. La plataforma inserta todo el lote de forma atómica (todo o nada) en una sola operación. Usa esto para datos de alta frecuencia donde una ida y vuelta por fila sería demasiado costosa. Al igual que publishToTable, esto es fire-and-forget — la confirmación valida la entrega al router, no la inserción en la base de datos.

await ironflock.publish_rows_to_table("sensordata", [ {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5}, {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7}, ])

El segundo argumento es una lista no vacía de objetos de fila a insertar.

appendRowsToTable / append_rows_to_table

Añade muchas filas en una sola llamada a procedimiento remoto (inserción masiva) en una tabla de flota. La plataforma inserta todo el lote de forma atómica (todo o nada): si cualquier fila es inválida, se rechaza el lote completo y no se persiste nada. Prefiere esto sobre publishRowsToTable / publish_rows_to_table cuando necesites conocer el resultado de la inserción.

result = await ironflock.append_rows_to_table("sensordata", [ {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5}, {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7}, ]) # result -> {"success": True, "count": 2}

reportError / report_error

Reporta un error de aplicación en la tabla error-logs de tu flota. Esto es un envoltorio de conveniencia sobre publishToTable / appendToTable: marca la fila con source: "app", un level de severidad y una marca de tiempo, y luego la escribe como cualquier fila normal de tabla. El error llega a la misma tabla error-logs que usan los errores de sistema de fleetdb (etiquetados con source: "system"), por lo que se puede consultar con getHistory, transmitir con subscribeToTable / subscribe_to_table, usar en plantillas de tablero y entregar en tiempo real en transformed.error-logs — sin disparar el toast de error de sistema de la plataforma.

# Fire-and-forget (default): publishes to the error-logs table await ironflock.report_error("Sensor read timed out", level="warn") # Pass an exception to capture its traceback (falls back to the message) try: risky_operation() except Exception as err: await ironflock.report_error(err) # Use the append RPC when you want to await the insert outcome await ironflock.report_error("Calibration failed", level="error", append=True)

Parámetros:

ParámetroTipoDescripción
errorstr / string o excepción / ErrorEl mensaje de error, o una excepción cuyo traceback/stack (o mensaje) se registra
levelstr / string, opcionalSeveridad: "error", "warn", "info" o "debug". Por defecto "error"
appendbool / boolean, opcionalCuando es true, usa la RPC de append (devuelve el resultado de la inserción). Por defecto false (publicación fire-and-forget)
tspstr / string, opcionalSobrescritura de marca de tiempo ISO-8601. Por defecto la hora actual

En Python las opciones son argumentos con nombre (report_error(error, level=..., append=..., tsp=...)); en JavaScript se pasan a través de un objeto de opciones (reportError(error, { level, append, tsp })).

publish

Publica un mensaje en cualquier topic WAMP. Usa esto para mensajería personalizada o eventos que no se corresponden con una tabla de base de datos.

await ironflock.publish("com.myapp.alerts", { "level": "warning", "message": "Temperature threshold exceeded" })

Consulta de datos históricos

getHistory

Recupera datos históricos de una tabla de flota. Soporta filtrado, rangos de tiempo y paginación.

# Simple query data = await ironflock.getHistory("sensordata", {"limit": 100}) # Query with time range and filters data = await ironflock.getHistory("sensordata", { "limit": 500, "offset": 0, "timeRange": { "start": "2026-01-01T00:00:00Z", "end": "2026-03-01T00:00:00Z" }, "filterAnd": [ {"column": "temperature", "operator": ">", "value": 20}, {"column": "humidity", "operator": "<=", "value": 80} ] }) # Current value(s) only: the "latest" marker returns the newest row per entity current = await ironflock.getHistory("sensordata", { "limit": 100, "filterAnd": [{"latest": True}] })

Parámetros de consulta:

CampoTipoDescripción
limitint / numberMáximo de filas a devolver (1–10.000, obligatorio)
offsetint / numberDesplazamiento para paginación
timeRangedict / object{"start": "<fecha ISO>", "end": "<fecha ISO>"}
filterAndlist / arrayCondiciones de filtro AND, y/o el marcador latest (ver abajo)
columnslist / arrayColumnas a devolver (opcional). tsp, device_key y authid siempre se incluyen; omítelo para obtener todas las columnas

Operadores de filtro: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

Cada filtro es un objeto con las claves column, operator y value.

Leer los valores actuales. Una entrada {"latest": true} en filterAnd no es una condición de filtro sino un cambio de modo: el backend de datos devuelve solo la fila más reciente de cada entidad, derivada en SQL a partir de la clave de entidad que la tabla declara con maintainLatestFlagFor. Una tabla sin clave de entidad devuelve su única fila más reciente.

Las demás condiciones se combinan con el marcador como cabría esperar: las condiciones sobre columnas de la clave de entidad acotan qué entidades se devuelven, mientras que todas las demás condiciones y timeRange se aplican a las filas más recientes resultantes. Así, combinar {"latest": true} con un filtro sobre deleted oculta las entidades eliminadas en lugar de hacer reaparecer su fila anterior.

Las versiones anteriores de IronFlock almacenaban una columna física latest_flag. Ya no existe — un filtro heredado latest_flag = true se sigue aceptando y se trata como el marcador, pero el código nuevo debería usar {"latest": true}. El marcador latest no está disponible en getSeriesHistory.

getSeriesHistory / get_series_history

Recupera datos de series temporales submuestreados de una tabla de flota: columnas numéricas agregadas en intervalos de tiempo (p. ej., promedios por hora). Ideal para gráficos que abarcan rangos de tiempo largos. Disponible para tablas (no para transforms).

series = await ironflock.get_series_history("sensordata", { "metrics": ["temperature", "humidity"], "method": "AVG", "limit": 500, "timeRange": ["2026-01-01T00:00:00Z", "2026-03-01T00:00:00Z"], "groupBy": ["device_id"] })

Parámetros de consulta:

CampoTipoDescripción
metricslist / arrayColumnas numéricas a submuestrear
methodstr / stringAgregación por intervalo: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" o "LAST"
limitint / numberNúmero máximo de intervalos (1–10 000)
timeRangelist / array[start, end] — cadenas ISO datetime o números epoch-ms; null = extremo abierto (obligatorio)
groupBylist / arrayColumnas por las que agrupar la serie (opcional)
filterAndlist / arrayCondiciones de filtro AND (opcional). Solo condiciones de filtro — el marcador latest no se admite aquí; usa getHistory para leer los valores actuales

Suscripción a datos

subscribeToTable / subscribe_to_table

Se suscribe a actualizaciones en tiempo real de una tabla de flota. El handler se llama cada vez que se publican nuevos datos en la tabla. Las filas escritas a través de la ruta de inserción masiva (publishRowsToTable / appendRowsToTable) se entregan a tu handler una por una, por lo que el código del handler permanece igual sin importar cómo se escribieron los datos.

def on_sensor_data(*args, **kwargs): print("New reading:", args, kwargs) await ironflock.subscribe_to_table("sensordata", on_sensor_data)

subscribe

Se suscribe a cualquier topic WAMP para mensajería personalizada en tiempo real.

def on_alert(*args, **kwargs): print("Alert received:", args, kwargs) await ironflock.subscribe("com.myapp.alerts", on_alert)

Acceso a datos entre apps

Lee los datos de flota de otra app desde dentro de tu propia app, en el mismo proyecto. La app proveedora debe declarar tu app en la sección consumes: de su data-template.yml, y el usuario del proyecto debe conceder el acceso. El acceso es de solo lectura: puedes consultar el historial y suscribirte en tiempo real a las filas de las tablas y transforms que comparte el proveedor, pero no puedes escribir en ellas. Las conexiones a apps consumidas se almacenan en caché por app y se cierran automáticamente cuando tu instancia se detiene.

Si tu app posee la concesión comodín (consumes: [{ app: "*" }]), puedes descubrir y abrir proveedores de forma dinámica con listConsumableApps / list_consumable_apps y connectToAllApps / connect_to_all_apps (más abajo).

connectToApp / connect_to_app

Abre una conexión de solo lectura al backend de datos de otra app y devuelve un manejador (handle). El manejador expone getHistory / get_history, subscribeToTable / subscribe_to_table y getSeriesHistory / get_series_history (solo tablas) — las mismas consultas y suscripciones que usas en tus propias tablas — además de close y los catálogos compartidos tables / transforms.

# Open a read-only handle on another app's data backend weather = await ironflock.connect_to_app("weather-app") # Inspect what the provider shares print([t["tablename"] for t in weather.tables]) # Query history and subscribe, just like your own tables rows = await weather.get_history("forecasts", {"limit": 100}) def on_forecast(*args, **kwargs): print("New forecast:", args) await weather.subscribe_to_table("forecasts", on_forecast)

Parámetros:

ParámetroTipoDescripción
app_name / appNamestr / stringNombre de la app proveedora, tal como se declara en tu sección consumes:
stagestr / string, opcionalStage del proveedor: "dev" o "prod". Por defecto, el stage de tu propia app
on_error / onErrorcallable, opcionalSe invoca con un CrossAppAccessError si el acceso se deniega después de establecer la conexión (p. ej., si la concesión se revoca más tarde)

Si el acceso se deniega o se usa incorrectamente, se lanza un CrossAppAccessError (Python / JavaScript) con un campo code: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE o NOT_AUTHORIZED.

En Python, stage y on_error son argumentos con nombre; en JavaScript se pasan a través de un objeto de opciones (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Lista todos los proveedores no privados del proyecto — la primitiva de descubrimiento para apps que poseen la concesión de consumo comodín (consumes: [{ app: "*" }] en tu data-template.yml, concedida por el usuario del proyecto). Realiza una única llamada y no abre ninguna conexión: renderiza los catálogos devueltos en un selector, luego llama a connectToApp / connect_to_app para los que quieras — o a connectToAllApps / connect_to_all_apps para abrirlos todos a la vez.

Nota: Declara la concesión en el data-template.yml de tu app, y entrecomilla el * — un * sin comillas es un alias de YAML y no se parseará:

consumes: - app: "*"
providers = await ironflock.list_consumable_apps() for p in providers: print(p["app"], list(p["stages"].keys())) # e.g. "weather-app" ["dev", "prod"]

Cada entrada describe un proveedor:

CampoTipoDescripción
appstr / stringNombre de la app proveedora
provider_app_keyint / numberLa clave de app del proveedor
stagesdict / objectCatálogo por stage { dev?, prod? }; un stage está presente solo si el proveedor tiene un backend de datos para él. Cada catálogo contiene las tables y transforms no privadas que comparte

Genera (Python) / lanza (JavaScript) un CrossAppAccessError con code: NO_GRANT si tu app no posee ninguna concesión comodín.

connectToAllApps / connect_to_all_apps

Abre manejadores de solo lectura a todos los proveedores no privados del proyecto en una sola llamada (solo para consumidores comodín). Enumera los proveedores mediante listConsumableApps / list_consumable_apps y abre cada uno, omitiendo los que no tengan un backend de datos para el stage solicitado. Cada manejador se almacena en caché bajo la misma clave que connectToApp / connect_to_app, de modo que una llamada posterior a connectToApp(name) devuelve el manejador ya preparado. Los manejadores devueltos se cierran juntos cuando tu instancia se detiene.

apps = await ironflock.connect_to_all_apps( on_error=lambda err: print("Provider skipped:", err) ) for app in apps: rows = await app.get_history(app.tables[0]["tablename"], {"limit": 10}) print(app.app, rows)

Parámetros:

ParámetroTipoDescripción
stagestr / string, opcionalStage del proveedor: "dev" o "prod". Por defecto, el stage de tu propia app
continue_on_error / continueOnErrorbool / boolean, opcionalCuando es true (el valor por defecto), un proveedor que no se puede abrir se reporta a on_error / onError y se omite del resultado. Cuando es false, el primer fallo se lanza
on_error / onErrorcallable, opcionalSe invoca con cada proveedor que no se pudo abrir (mientras continue_on_error / continueOnError sea true), y con un CrossAppAccessError si una conexión ya abierta se deniega posteriormente (p. ej., si la concesión se revoca)

Devuelve los manejadores de proveedor abiertos correctamente (el mismo tipo de manejador que connectToApp / connect_to_app). Genera (Python) / lanza (JavaScript) un CrossAppAccessError con code: NO_GRANT si tu app no posee ninguna concesión comodín.

En Python, stage, on_error y continue_on_error son argumentos con nombre; en JavaScript se pasan a través de un objeto de opciones (connectToAllApps({ stage, onError, continueOnError })).

Almacenamiento de archivos gestionado

Cada backend de datos de app dispone de almacenamiento de objetos privado junto a sus tablas, accesible a través de la propiedad files. Úsalo para imágenes, PDF, fotogramas de cámara, blobs de firmware — cualquier cosa que no pertenezca a una fila de una tabla. No requiere configuración alguna: una app sin sección files: en su plantilla de datos también obtiene un espacio de nombres llamado default.

La idea clave es que almacenar un objeto te devuelve una URL permanente que puedes escribir directamente en una columna de tabla, de modo que un widget de tablero pueda renderizarla sin ningún trabajo adicional:

# Store an object and get a permanent URL back in the same call info = await ironflock.files.put("part-1.jpg", jpeg_bytes, content_type="image/jpeg") # The URL is safe to store in a table column — a dashboard widget can then # render <img src="{{photo_url}}"> without any extra round trip await ironflock.publish_to_table("inspections", part_id="1", photo_url=info.url) # Read it back data = await ironflock.files.get("part-1.jpg") # Walk every object under a prefix (pages are fetched for you) async for obj in ironflock.files.iter(prefix="2026/"): print(obj.key, obj.size)

Esa URL nunca caduca, pero no es un enlace público: solo permanece legible para un solicitante autenticado que tenga acceso READ sobre este backend de datos, y un proxy de autenticación lo vuelve a comprobar en cada petición. Por eso es seguro almacenarla en la base de datos.

Espacios de nombres

Un espacio de nombres es un prefijo de clave que lleva asociada una política — retención, reglas de compartición, tipos de contenido permitidos. No es un bucket aparte; todos los espacios de nombres de una app viven dentro de la única área de almacenamiento de esa app. Declara uno solo cuando un conjunto de objetos necesite reglas distintas; en caso contrario quédate en default y organiza tus objetos con rutas de clave como 2026/03/part-1.jpg.

Declara espacios de nombres adicionales en data-template.yml:

files: # Storage budget the app suggests for itself. The project user can change it, # and their setting is the one that gets enforced. quotaBytes: 5368709120 namespaces: - name: frames description: Raw camera frames, one JPEG per inspected part. contentTypes: ["image/jpeg"] maxObjectBytes: 20971520 retention: { deleteAfter: 30 days }

Ten en cuenta que el presupuesto se declara una sola vez para toda la app, no por espacio de nombres. Un espacio de nombres es solo un prefijo de clave dentro de la única área de almacenamiento de la app, así que no hay nada contra lo que aplicar un presupuesto por prefijo. maxObjectBytes es por espacio de nombres — limita un objeto individual, no un total.

Todos los métodos siguientes aceptan el espacio de nombres como argumento opcional y, por defecto, usan default.

Almacenar y leer objetos

# Bytes in, bytes out info = await ironflock.files.put("reports/march.pdf", pdf_bytes, content_type="application/pdf") data = await ironflock.files.get("reports/march.pdf") # Or straight from/to a local file — these stream on the large-object path, # so a multi-gigabyte file never has to fit in memory await ironflock.files.put_file("firmware/v2.bin", "/data/build/v2.bin") await ironflock.files.get_to_file("firmware/v2.bin", "/tmp/v2.bin")
MétodoDescripción
put(key, data, …)Almacena un objeto (bytes en Python, Uint8Array en JavaScript). Devuelve los metadatos del objeto, incluida su url
get(key, namespace?)Devuelve el contenido del objeto
put_file(key, path, …) / get_to_file(key, path, …)Solo Python. Almacena desde un archivo local, o escribe en él. Usa streaming en la ruta de objetos grandes
delete(key, namespace?)Elimina un objeto
copy(key, to, …)Copia un objeto, opcionalmente a otro espacio de nombres
move(key, to, …)Copia y luego elimina. No es atómico — el servicio no tiene un verbo de movimiento, así que un borrado fallido deja ambas copias

JavaScript no tiene utilidades para rutas de archivo porque el paquete se distribuye con una única compilación para Node y el navegador — lee y escribe los archivos locales tú mismo con fs.

put acepta: content_type / contentType (el tipo MIME; el espacio de nombres puede restringir cuáles se permiten) y namespace. En Python son argumentos con nombre; en JavaScript van en un objeto de opciones.

Listar e inspeccionar

MétodoDescripción
list(…)Una página de objetos. Devuelve objects, prefixes, is_truncated / isTruncated y un cursor que puedes volver a pasar para la página siguiente
iter(…) / iterate(…)Iterador asíncrono sobre todos los objetos bajo un prefijo, paginando automáticamente. Se llama iter en Python e iterate en JavaScript
stat(key, namespace?)Metadatos de un objeto sin transferir su contenido
exists(key, namespace?)Si un objeto existe o no
namespaces()Los espacios de nombres que esta app puede usar
usage(…)Cuánto almacenamiento está usando la app — véase más abajo
catalog()Espacios de nombres más los límites y cuotas emitidos por el servidor. Se almacena en caché tras la primera llamada

Los objetos se describen con los mismos campos en ambos SDK, siguiendo el estilo de nomenclatura de cada lenguaje: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 y url.

Uso de almacenamiento y cuota

usage responde desde el almacén de objetos en una sola llamada, así que los totales son exactos en lugar de sumados por el SDK:

u = await ironflock.files.usage() print(u.size_bytes, u.object_count, u.quota_bytes, u.free_bytes) # Break the total down per namespace (costs one listing per namespace) detailed = await ironflock.files.usage(detail=True) print(detailed.per_namespace) # {"default": 1048576, "frames": 73400320}
CampoSignificado
size_bytes / sizeBytesBytes almacenados actualmente
object_count / objectCountNúmero de objetos almacenados
quota_bytes / quotaBytesEl presupuesto aplicado. 0 significa ilimitado
free_bytes / freeBytesBytes restantes. -1 significa ilimitado — indicar 0 ahí se leería como “lleno”
per_namespace / perNamespaceBytes por espacio de nombres. Presente solo cuando pides el desglose detallado

El desglose por espacio de nombres está desactivado por defecto porque el almacén no puede responderlo directamente: lleva la contabilidad por área de almacenamiento, y un espacio de nombres es solo un prefijo, así que el SDK tiene que listar cada espacio de nombres para sumar los tamaños. Pídelo cuando lo necesites, no en una ruta crítica.

Aparecen dos cuotas distintas, y conviene no confundirlas. catalog() informa de ambas:

CampoSignificado
quota_bytes / quotaBytesLo que realmente se aplica, leído del almacén de objetos — el ajuste del usuario del proyecto
suggested_quota_bytes / suggestedQuotaBytesLo que pidió la plantilla de datos de la app. 0 si no pidió nada

Difieren siempre que un usuario ha subido o bajado el presupuesto de la app, y por eso el valor aplicado se lee del almacén y no de la plantilla — volver a desplegar la app no debe restablecer en silencio la elección de un usuario. Una interfaz puede mostrar ambos (“la app sugiere X, tú has establecido Y”). La aplicación efectiva siempre usa el primero.

Compartir objetos

Hay dos tipos de enlace, y la diferencia importa:

MétodoVigenciaQuién puede leerlo
url(key, …)PermanenteSolo un solicitante autenticado con READ sobre este backend de datos — se vuelve a comprobar en cada petición. Seguro para almacenar en una columna de tabla
share_url / shareUrlCaduca (15 min por defecto, acotado por el servidor)Cualquiera que tenga el enlace. Nada vuelve a comprobar la autorización cuando se usa

share_url / shareUrl es una capacidad al portador: entrégalo a una persona que necesite acceso temporal, y no lo almacenes en la base de datos. Usa url para todo aquello que renderice un tablero.

url devuelve None / undefined cuando el despliegue no tiene un borde HTTP (por ejemplo, un appliance de HTTP simple) — esa es la señal para recurrir a get. Pasar el etag de un objeto como argumento version permite que los navegadores cacheen la respuesta de forma inmutable.

upload_url / uploadUrl emite una URL con caducidad que acepta una subida directa, y devuelve url, method, headers y expires_in / expiresIn. Envía exactamente las cabeceras que devuelve, o la firma no se verificará.

Objetos grandes

El SDK elige el transporte según el tamaño, de forma automática — no hay nada que configurar:

Tamaño del objetoCómo viaja
Hasta el límite inline (actualmente 6 MiB)Una única llamada a través del router de mensajes
MayorDirectamente al almacenamiento de objetos por HTTPS, sin pasar por el router

El servidor informa del límite exacto en tiempo de ejecución como inline_max_bytes / inlineMaxBytes en catalog(), de modo que se puede subir sin publicar una nueva versión del SDK.

Quedan dos topes, y ambos reportan TOO_LARGE con un motivo que indica cuál de los dos has alcanzado:

  • 5 GiB — el límite de subida en una sola operación del almacén de objetos. La subida multiparte aún no está implementada.
  • El límite inline, allí donde no hay endpoint directo — un appliance aislado (air-gapped) no puede transferir un objeto grande en absoluto. Ningún reintento ni fragmento más pequeño ayudará, y el mensaje lo dice.

La ruta directa necesita que el dispositivo alcance el host del almacenamiento de objetos, no solo el router. Dos fallos habituales en campo tienen sus propios códigos en lugar de parecer problemas de autorización: PRESIGN_UNREACHABLE (un proxy que solo permite el router) y CLOCK_SKEW (el almacén de objetos rechaza las peticiones desfasadas más de 15 minutos — revisa el NTP del dispositivo).

Errores de almacenamiento de archivos

Toda operación de archivos genera (Python) / lanza (JavaScript) un FileStoreError que lleva un code estable y un reason legible para humanos. Ramifica según code, nunca según reason.

from ironflock.filestore import FileStoreError try: await ironflock.files.put("huge.bin", payload) except FileStoreError as e: if e.code == "QUOTA_EXCEEDED": print("Filestore is full:", e.reason) else: raise
CódigoSignificado
NOT_AUTHORIZEDEl llamador no puede realizar esta operación
NO_SUCH_NAMESPACEEl espacio de nombres no está declarado en la plantilla de datos
NO_SUCH_OBJECTLa clave no existe
TOO_LARGESupera el límite de transferencia en una sola llamada
OBJECT_TOO_LARGESupera el maxObjectBytes propio del espacio de nombres
QUOTA_EXCEEDEDEl filestore está lleno
CONTENT_TYPE_NOT_ALLOWEDEl espacio de nombres restringe los contentTypes
NOT_SUPPORTEDEl backend no puede hacer esto
NOT_AVAILABLEEste despliegue no tiene servicio de archivos
PRESIGN_UNREACHABLEEl almacenamiento de objetos no es accesible directamente (¿un proxy?)
CLOCK_SKEWEl reloj del dispositivo está demasiado desfasado
INTERNALCualquier otra cosa

Un servidor más nuevo puede introducir códigos que esta versión del SDK no conoce. Se propagan tal cual en code en lugar de colapsarse, así que trata un valor no reconocido como un fallo genérico.

En Python, FileStoreError se importa desde ironflock.filestore; en JavaScript se exporta desde la raíz del paquete (import { FileStoreError } from "ironflock").

Comunicación entre dispositivos

registerDeviceFunction / register_device_function

Registra un procedimiento que otros dispositivos del mismo proyecto pueden llamar. El SDK asigna automáticamente un espacio de nombres al procedimiento para el dispositivo actual.

def add(a, b): return a + b await ironflock.register_device_function("com.myapp.add", add)

register() es un alias de register_device_function().

callDeviceFunction / call_device_function

Llama a un procedimiento registrado por otro dispositivo. El SDK construye el topic WAMP completo automáticamente usando la clave del dispositivo de destino.

result = await ironflock.call_device_function( 42, # target device key "com.myapp.add", # procedure name args=[3, 5] # arguments ) print(result) # 8

call

Llama a un procedimiento remoto usando una URI WAMP completa. Usa esto para llamadas directas cuando conoces el topic exacto.

result = await ironflock.call("some.full.wamp.topic", args=[42])

Metadatos del dispositivo

setDeviceLocation / set_device_location

Actualiza la ubicación GPS del dispositivo en la plataforma. Los cambios se reflejan en tiempo real en los mapas de IronFlock.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
ParámetroRango
long-180 a 180
lat-90 a 90

El historial de ubicación no se almacena. Para rastrear la ubicación a lo largo del tiempo, crea una tabla dedicada y usa publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Devuelve la URL pública de acceso remoto para un puerto determinado del dispositivo.

url = ironflock.getRemoteAccessUrlForPort(8080) # "https://<device_key>-<app_name>-8080.app.ironflock.com"

Propiedades de conexión y ciclo de vida

PropiedadTipoDescripción
is_connectedboolIndica si la conexión con la plataforma está activa
connectionCrossbarConnectionLa instancia de conexión subyacente (uso avanzado)
MétodoDescripción
run()Inicia la conexión y ejecuta mainFunc (bloqueante)
await start()Inicia la conexión de forma asíncrona
await stop()Detiene la conexión y cancela tareas en ejecución
await run_async()Inicia y mantiene la conexión de forma asíncrona

Manejo de errores

Todos los métodos del SDK fallan de forma explícita: ante argumentos inválidos, una conexión perdida o un rechazo de la plataforma, lanzan una excepción (Python) o rechazan la promesa (JavaScript) con un mensaje que indica la operación, el topic y el motivo. Nada se descarta en silencio, así que envuelve en un bloque try las llamadas que quieras que sobrevivan.

try: rows = await ironflock.getHistory("sensordata", {"limit": 100}) except ValueError as e: # Invalid parameters — e.g. limit out of range, or a malformed filter print(f"Bad query: {e}") except RuntimeError as e: # Not connected, table not in the data-template, or the platform rejected the call print(f"Query failed: {e}")

En JavaScript, los fallos que provienen de la plataforma son instancias de WampError — una subclase normal de Error que además lleva la URI de error WAMP en error y la carga útil del error en args / kwargs. Todo lo demás (parámetros inválidos, sin conexión) es un Error normal.

Migración: las versiones anteriores del SDK registraban un mensaje y devolvían None / null cuando una llamada fallaba. Ahora lanzan un error en su lugar, por lo que el código con la forma if result is None: ya no detecta los fallos — usa try / except (o try / catch).

Uso en navegador (solo JavaScript)

El SDK de JavaScript funciona en navegadores modernos. Como los navegadores no tienen variables de entorno, pasa toda la configuración a través del constructor:

import { IronFlock } from "ironflock"; const ironflock = new IronFlock({ serialNumber: "device-serial-from-server", deviceKey: "my-device-key", appName: "MyWebApp", swarmKey: 10, appKey: 20, env: "PROD", }); await ironflock.start(); await ironflock.publishToTable("sensordata", [{ temperature: 22 }]);

Usa IronFlock.fromServer() para obtener la configuración desde tu backend en lugar de codificar las credenciales:

const ironflock = await IronFlock.fromServer("/api/ironflock-config"); await ironflock.start();

Tu endpoint del backend debe devolver un objeto JSON con las opciones de conexión (serialNumber, deviceKey, appName, swarmKey, appKey, env).

Registro de funciones para agentes de IA

El SDK puede registrar funciones que son invocables por agentes de IA. Registra un procedimiento y referencia su topic en tu ai-template.yml:

def get_sensor_reading(sensor_id): """Returns the latest reading from a sensor.""" reading = read_from_hardware(sensor_id) return { "sensor_id": sensor_id, "temperature": reading.temp, "humidity": reading.hum, "timestamp": reading.ts } await ironflock.register_device_function("sensors.get_latest", get_sensor_reading)

El agente de IA puede entonces llamar a esta función cuando un usuario haga una pregunta que requiera datos de sensores en tiempo real.

Para conectar el topic WAMP registrado con un agente de IA, referencíalo en el archivo .ironflock/ai-template.yml de tu app:

sensor_agent: tool_description: | Delega a este agente cuando el usuario pregunte sobre lecturas de sensores, datos en vivo del dispositivo o condiciones ambientales actuales. system_prompt: | Eres un especialista en datos de sensores. Usa get_current para obtener la lectura más reciente de cualquier sensor. Incluye siempre la unidad en tu respuesta. main: true max_context_tokens: 30000 max_iterations: 5 tools: get_current: description: Devuelve la lectura más reciente de un sensor. topic: sensors.get_latest parameters: sensor_id: type: string description: El identificador del sensor a consultar. required: true

El valor de topic (sensors.get_latest) debe coincidir con el nombre indicado en register_device_function / registerDeviceFunction en el código edge. IronFlock enruta automáticamente la llamada al dispositivo donde está registrada la función.

Para la referencia completa de ai-template.yml, consulta Definición de agentes y herramientas.

Variables de entorno

Estas variables son establecidas automáticamente por el runtime de IronFlock dentro de los contenedores de apps:

VariableDescripción
DEVICE_NAMENombre visible del dispositivo
DEVICE_SERIAL_NUMBERIdentificador único e inmutable del dispositivo
DEVICE_KEYClave del dispositivo para autenticación
SWARM_KEYIdentificador del proyecto
APP_KEYIdentificador de la app
APP_NAMENombre de la app
ENVEntorno: DEV o PROD
import os device_name = os.environ.get("DEVICE_NAME") serial = os.environ.get("DEVICE_SERIAL_NUMBER") project_key = os.environ.get("SWARM_KEY")
Last updated on