Skip to Content

SDK do IronFlock

O SDK do IronFlock permite que suas aplicações de borda interajam com a plataforma IronFlock. Ele cuida da autenticação automaticamente quando executado em um dispositivo registrado e fornece funções para publicar dados, consultar histórico, chamar procedimentos remotos entre dispositivos e atualizar os metadados do dispositivo.

SDKPacoteRequer
Pythonironflock no PyPIPython 3.8+
JavaScriptironflock no npmNode.js 18+ ou navegador moderno

Instalação

pip install ironflock

Ou adicione ironflock ao requirements.txt da sua aplicação.

Início 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()

Quando usado dentro de um contêiner de aplicação do IronFlock, o SDK lê as credenciais de conexão do ambiente automaticamente — nenhuma configuração manual é necessária.

Opções do Construtor

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParâmetroDescrição
mainFuncUma função assíncrona que é executada assim que a conexão é estabelecida
serial_numberSobrescreve o número de série do dispositivo. O padrão é a variável de ambiente DEVICE_SERIAL_NUMBER

Publicando Dados

publishToTable / publish_to_table

Publica um registro de dados em uma tabela da frota. O nome da tabela deve corresponder a uma tabela definida no data-template.yml da sua aplicação. O SDK roteia automaticamente os dados para o banco de dados correto do projeto.

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

appendToTable / append_to_table

Acrescenta dados a uma tabela da frota usando uma chamada de procedimento remoto em vez de pub/sub. Use isto quando você precisar de confirmação de que os dados foram persistidos.

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

publishRowsToTable / publish_rows_to_table

Publica várias linhas em uma única mensagem (inserção em lote) em uma tabela da frota. A plataforma insere todo o lote atomicamente (tudo ou nada) em uma única operação. Use isto para dados de alta frequência, onde uma ida e volta por linha seria custosa demais. Assim como publishToTable, isto é fire-and-forget — a confirmação atesta a entrega ao roteador, não a inserção no banco de dados.

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}, ])

O segundo argumento é uma lista não vazia de objetos de linha a inserir.

appendRowsToTable / append_rows_to_table

Acrescenta várias linhas em uma única chamada de procedimento remoto (inserção em lote) a uma tabela da frota. A plataforma insere todo o lote atomicamente (tudo ou nada): se qualquer linha for inválida, todo o lote é rejeitado e nada é persistido. Prefira isto a publishRowsToTable / publish_rows_to_table quando você precisar do resultado da inserção.

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 um erro da aplicação na tabela error-logs da sua frota. Isto é um invólucro de conveniência sobre publishToTable / appendToTable: ele carimba a linha com source: "app", um nível de severidade level e um timestamp, e então a escreve como qualquer linha de tabela normal. O erro chega na mesma tabela error-logs que os erros de sistema do fleetdb usam (marcados com source: "system"), de modo que ele pode ser consultado com getHistory, transmitido com subscribeToTable / subscribe_to_table, usado em board-templates e entregue em tempo real em transformed.error-logs — sem disparar o toast de erro de sistema da 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âmetroTipoDescrição
errorstr / string ou exceção / ErrorA mensagem de erro, ou uma exceção cujo traceback/stack (ou mensagem) é registrado
levelstr / string, opcionalSeveridade: "error", "warn", "info" ou "debug". O padrão é "error"
appendbool / boolean, opcionalQuando true, usa a RPC de acréscimo (retorna o resultado da inserção). O padrão é false (publicação fire-and-forget)
tspstr / string, opcionalSobrescrita de timestamp ISO-8601. O padrão é o horário atual

Em Python as opções são argumentos nomeados (report_error(error, level=..., append=..., tsp=...)); em JavaScript elas são passadas via um objeto de opções (reportError(error, { level, append, tsp })).

publish

Publica uma mensagem em qualquer tópico WAMP. Use isto para mensagens ou eventos personalizados que não correspondem a uma tabela do banco de dados.

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

Consultando Dados Históricos

getHistory

Recupera dados históricos de uma tabela da frota. Suporta filtragem, intervalos de tempo e paginação.

# 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 da consulta:

CampoTipoDescrição
limitint / numberNúmero máximo de linhas a retornar (1–10.000, obrigatório)
offsetint / numberDeslocamento para paginação
timeRangedict / object{"start": "<ISO datetime>", "end": "<ISO datetime>"}
filterAndlist / arrayCondições de filtro AND e/ou o marcador latest (veja abaixo)
columnslist / arrayColunas a retornar (opcional). tsp, device_key e authid são sempre incluídas; omita para obter todas as colunas

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

Cada filtro é um objeto com as chaves column, operator e value.

Lendo os valores atuais. Uma entrada {"latest": true} em filterAnd não é uma condição de filtro, e sim uma troca de modo: o backend de dados retorna apenas a linha mais recente de cada entidade, derivada em SQL a partir da chave de entidade que a tabela declara com maintainLatestFlagFor. Uma tabela sem chave de entidade retorna a sua única linha mais recente.

As demais condições se combinam com o marcador como você esperaria: condições sobre colunas da chave de entidade restringem quais entidades são retornadas, enquanto todas as outras condições e o timeRange são aplicados às linhas mais recentes resultantes. Assim, combinar {"latest": true} com um filtro de deleted oculta as entidades excluídas em vez de fazer a linha anterior delas reaparecer.

Versões anteriores do IronFlock armazenavam uma coluna física latest_flag. Ela não existe mais — um filtro legado latest_flag = true continua sendo aceito e tratado como o marcador, mas código novo deve usar {"latest": true}. O marcador latest não está disponível em getSeriesHistory.

getSeriesHistory / get_series_history

Recupera dados de séries temporais reduzidos (down-sampled) de uma tabela de frota: colunas numéricas agregadas em intervalos de tempo (por exemplo, médias por hora). Ideal para gráficos que abrangem longos intervalos de tempo. Disponível para tabelas (não 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 da consulta:

CampoTipoDescrição
metricslist / arrayColunas numéricas a serem reduzidas
methodstr / stringAgregação por intervalo: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" ou "LAST"
limitint / numberNúmero máximo de intervalos (1–10.000)
timeRangelist / array[start, end] — strings ISO datetime ou números epoch-ms; null = extremidade aberta (obrigatório)
groupBylist / arrayColunas para agrupar a série (opcional)
filterAndlist / arrayCondições de filtro AND (opcional). Apenas condições de filtro — o marcador latest não é suportado aqui; use getHistory para ler os valores atuais

Inscrevendo-se em Dados

subscribeToTable / subscribe_to_table

Inscreve-se em atualizações em tempo real de uma tabela da frota. O handler é chamado sempre que novos dados são publicados na tabela. As linhas escritas pelo caminho de inserção em lote (publishRowsToTable / appendRowsToTable) são entregues ao seu handler uma de cada vez, de modo que o código do handler permanece o mesmo independentemente de como os dados foram escritos.

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

subscribe

Inscreve-se em qualquer tópico WAMP para mensagens personalizadas em tempo real.

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

Acesso a Dados Entre Apps

Leia os dados de frota de outro app de dentro do seu próprio app, no mesmo projeto. O app provedor deve declarar o seu app na seção consumes: do seu data-template.yml, e o usuário do projeto deve conceder o acesso. O acesso é somente leitura: você pode consultar o histórico e se inscrever em tempo real nas linhas das tabelas e transforms que o provedor compartilha, mas não pode escrever nelas. As conexões com apps consumidos são armazenadas em cache por app e fechadas automaticamente quando a sua instância para.

Se o seu app possuir a concessão curinga (consumes: [{ app: "*" }]), você pode descobrir e abrir provedores dinamicamente com listConsumableApps / list_consumable_apps e connectToAllApps / connect_to_all_apps (abaixo).

connectToApp / connect_to_app

Abre uma conexão somente leitura ao backend de dados de outro app e retorna um handle. O handle expõe getHistory / get_history, subscribeToTable / subscribe_to_table e getSeriesHistory / get_series_history (apenas tabelas) — as mesmas consultas e inscrições que você usa nas suas próprias tabelas — além de close e dos catálogos compartilhados 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âmetroTipoDescrição
app_name / appNamestr / stringNome do app provedor, conforme declarado na sua seção consumes:
stagestr / string, opcionalStage do provedor: "dev" ou "prod". O padrão é o stage do seu próprio app
on_error / onErrorcallable, opcionalChamado com um CrossAppAccessError se o acesso for negado após a conexão ter sido estabelecida (por exemplo, se a concessão for revogada mais tarde)

Se o acesso for negado ou usado incorretamente, um CrossAppAccessError é levantado (Python) / lançado (JavaScript) com um campo code: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE ou NOT_AUTHORIZED.

Em Python, stage e on_error são argumentos nomeados; em JavaScript eles são passados via um objeto de opções (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Lista todos os provedores não privados do projeto — a primitiva de descoberta para apps que possuem a concessão de consumo curinga (consumes: [{ app: "*" }] no seu data-template.yml, concedida pelo usuário do projeto). Realiza uma única chamada e não abre nenhuma conexão: renderize os catálogos retornados em um seletor, então chame connectToApp / connect_to_app para os que você deseja — ou connectToAllApps / connect_to_all_apps para abrir todos de uma vez.

Nota: Declare a concessão no data-template.yml do seu app, e coloque o * entre aspas — um * sem aspas é um alias YAML e não será interpretado:

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 descreve um provedor:

CampoTipoDescrição
appstr / stringNome do app provedor
provider_app_keyint / numberA chave de app do provedor
stagesdict / objectCatálogo por stage { dev?, prod? }; um stage está presente apenas se o provedor tiver um backend de dados para ele. Cada catálogo contém as tables e transforms não privadas que ele compartilha

Levanta (Python) / lança (JavaScript) um CrossAppAccessError com code: NO_GRANT se o seu app não possuir a concessão curinga.

connectToAllApps / connect_to_all_apps

Abre handles somente leitura para todos os provedores não privados do projeto em uma única chamada (apenas consumidores curinga). Enumera os provedores via listConsumableApps / list_consumable_apps e abre cada um, ignorando qualquer um sem um backend de dados para o stage solicitado. Cada handle é armazenado em cache sob a mesma chave que connectToApp / connect_to_app, de modo que uma chamada posterior a connectToApp(name) retorna o handle já aquecido. Os handles retornados são fechados em conjunto quando a sua instância para.

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âmetroTipoDescrição
stagestr / string, opcionalStage do provedor: "dev" ou "prod". O padrão é o stage do seu próprio app
continue_on_error / continueOnErrorbool / boolean, opcionalQuando true (o padrão), um provedor que falha ao abrir é reportado para on_error / onError e omitido do resultado. Quando false, a primeira falha é levantada/lançada
on_error / onErrorcallable, opcionalChamado com cada provedor que não pôde ser aberto (enquanto continue_on_error / continueOnError for true), e com um CrossAppAccessError se uma conexão já aberta for negada mais tarde (por exemplo, se a concessão for revogada)

Retorna os handles de provedor abertos com sucesso (o mesmo tipo de handle que connectToApp / connect_to_app). Levanta (Python) / lança (JavaScript) um CrossAppAccessError com code: NO_GRANT se o seu app não possuir a concessão curinga.

Em Python, stage, on_error e continue_on_error são argumentos nomeados; em JavaScript eles são passados via um objeto de opções (connectToAllApps({ stage, onError, continueOnError })).

Armazenamento Gerenciado de Arquivos

Todo backend de dados de app recebe um armazenamento de objetos privado ao lado das suas tabelas, acessível através da propriedade files. Use-o para imagens, PDFs, frames de câmera, blobs de firmware — qualquer coisa que não pertença a uma linha de tabela. Nenhuma configuração é necessária: um app sem uma seção files: no seu data template ainda recebe um namespace chamado default.

A ideia central é que armazenar um objeto lhe devolve uma URL permanente que você pode escrever diretamente em uma coluna de tabela, de modo que um widget de dashboard consiga renderizá-la sem nenhum trabalho 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)

Essa URL nunca expira, mas ela não é um link público: ela permanece legível apenas para um solicitante autenticado que possua acesso READ neste backend de dados, e um proxy de autenticação reverifica isso a cada requisição. Por isso, ela é segura para armazenar no banco de dados.

Namespaces

Um namespace é um prefixo de chave que carrega política — retenção, regras de compartilhamento, tipos de conteúdo permitidos. Ele não é um bucket separado; todo namespace de um app vive dentro da única área de armazenamento daquele app. Declare um apenas quando um conjunto de objetos precisar de regras diferentes; caso contrário, permaneça em default e organize os seus objetos com caminhos de chave como 2026/03/part-1.jpg.

Declare namespaces adicionais no 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 }

Note que o orçamento é declarado uma única vez para o app inteiro, e não por namespace. Um namespace é apenas um prefixo de chave dentro da única área de armazenamento do app, então não há nada contra o que um orçamento por prefixo pudesse ser imposto. maxObjectBytes é por namespace — ele limita um único objeto, não um total.

Todos os métodos abaixo aceitam o namespace como um argumento opcional e usam default como padrão.

Armazenando e lendo 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étodoDescrição
put(key, data, …)Armazena um objeto (bytes em Python, Uint8Array em JavaScript). Retorna os metadados do objeto, incluindo a sua url
get(key, namespace?)Retorna o conteúdo do objeto
put_file(key, path, …) / get_to_file(key, path, …)Apenas Python. Armazena a partir de, ou escreve para, um arquivo local. Faz streaming no caminho de objetos grandes
delete(key, namespace?)Exclui um objeto
copy(key, to, …)Copia um objeto, opcionalmente para outro namespace
move(key, to, …)Copiar e depois excluir. Não é atômico — o serviço não tem um verbo de movimentação, então uma exclusão que falhe deixa as duas cópias

O JavaScript não possui helpers de caminho de arquivo porque o pacote entrega um único build tanto para o Node quanto para o navegador — leia e escreva os arquivos locais você mesmo com fs.

put aceita: content_type / contentType (o tipo MIME; o namespace pode restringir quais são permitidos) e namespace. Em Python estes são argumentos nomeados; em JavaScript eles vão em um objeto de opções.

Listando e inspecionando

MétodoDescrição
list(…)Uma página de objetos. Retorna objects, prefixes, is_truncated / isTruncated e um cursor para ser passado de volta na próxima página
iter(…) / iterate(…)Iterador assíncrono sobre todos os objetos sob um prefixo, paginando automaticamente. Chamado iter em Python e iterate em JavaScript
stat(key, namespace?)Metadados de um objeto sem transferir o seu conteúdo
exists(key, namespace?)Se um objeto existe
namespaces()Os namespaces que este app pode usar
usage(…)Quanto armazenamento o app está usando — veja abaixo
catalog()Namespaces mais os limites e as cotas emitidos pelo servidor. Armazenado em cache após a primeira chamada

Os objetos são descritos pelos mesmos campos em ambos os SDKs, no estilo de nomenclatura de cada linguagem: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 e url.

Uso de armazenamento e cota

usage responde a partir do object store em uma única chamada, então os totais são exatos em vez de somados pelo 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 atualmente armazenados
object_count / objectCountNúmero de objetos armazenados
quota_bytes / quotaBytesO orçamento imposto. 0 significa ilimitado
free_bytes / freeBytesBytes restantes. -1 significa ilimitado — reportar 0 aí seria lido como “cheio”
per_namespace / perNamespaceBytes por namespace. Presente apenas quando você pede o detalhamento por namespace

O detalhamento por namespace vem desligado por padrão porque o store não consegue respondê-lo diretamente: ele contabiliza por área de armazenamento, e um namespace é apenas um prefixo, então o SDK precisa listar cada namespace para somar os tamanhos. Peça-o quando você quiser, não em um caminho crítico de desempenho.

Duas cotas diferentes aparecem, e vale a pena mantê-las separadas. catalog() reporta as duas:

CampoSignificado
quota_bytes / quotaBytesO que é de fato imposto, lido do object store — a configuração do usuário do projeto
suggested_quota_bytes / suggestedQuotaBytesO que o data template do app pediu. 0 se ele não pediu nada

Elas divergem sempre que um usuário aumentou ou diminuiu o orçamento do app, e é por isso que o valor imposto é lido do store em vez do template — reimplantar o app não pode redefinir silenciosamente a escolha de um usuário. Uma UI pode mostrar ambos (“o app sugere X, você definiu Y”). A imposição sempre usa o primeiro.

Compartilhando objetos

Existem dois tipos de link, e a diferença importa:

MétodoTempo de vidaQuem pode lê-lo
url(key, …)PermanenteApenas um solicitante autenticado com READ neste backend de dados — reverificado a cada requisição. Seguro para armazenar em uma coluna de tabela
share_url / shareUrlExpirável (padrão de 15 min, limitado pelo servidor)Qualquer pessoa que possua o link. Nada reverifica a autorização quando ele é usado

share_url / shareUrl é uma capacidade ao portador (bearer): entregue-o a uma pessoa que precise de acesso temporário, e não o armazene no banco de dados. Use url para qualquer coisa que um dashboard renderize.

url retorna None / undefined onde a implantação não possui uma borda HTTP (por exemplo, um appliance em HTTP puro) — esse é o sinal para recorrer a get. Passar o etag de um objeto como argumento version permite que os navegadores armazenem a resposta em cache de forma imutável.

upload_url / uploadUrl gera uma URL expirável que aceita um upload direto, retornando url, method, headers e expires_in / expiresIn. Envie exatamente os headers que ela retorna, ou a assinatura não será verificada.

Objetos grandes

O SDK escolhe o transporte pelo tamanho, automaticamente — não há nada para configurar:

Tamanho do objetoComo ele trafega
Até o limite inline (atualmente 6 MiB)Uma única chamada através do roteador de mensagens
MaiorDiretamente para o armazenamento de objetos via HTTPS, contornando o roteador

O limite exato é reportado pelo servidor em tempo de execução como inline_max_bytes / inlineMaxBytes em catalog(), de modo que ele pode ser elevado sem um novo release do SDK.

Dois tetos permanecem, e ambos reportam TOO_LARGE com um motivo que nomeia qual deles você atingiu:

  • 5 GiB — o limite de upload único do armazenamento de objetos. O upload multipart ainda não foi implementado.
  • O limite inline, onde não há endpoint direto — um appliance isolado (air-gapped) não consegue transferir um objeto grande de forma alguma. Nenhuma nova tentativa nem chunk menor vai ajudar, e a mensagem diz isso.

O caminho direto exige que o dispositivo alcance o host do armazenamento de objetos, não apenas o roteador. Duas falhas comuns em campo recebem os seus próprios códigos em vez de parecerem problemas de autorização: PRESIGN_UNREACHABLE (um proxy que permite apenas o roteador) e CLOCK_SKEW (o armazenamento de objetos rejeita requisições com mais de 15 minutos de defasagem — verifique o NTP no dispositivo).

Erros de armazenamento de arquivos

Toda operação de arquivo levanta (Python) / lança (JavaScript) um FileStoreError que carrega um code estável e um reason legível por humanos. Ramifique pelo code, nunca pelo 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_AUTHORIZEDO chamador não pode realizar esta operação
NO_SUCH_NAMESPACEO namespace não está declarado no data template
NO_SUCH_OBJECTA chave não existe
TOO_LARGEExcede o limite de transferência em uma única chamada
OBJECT_TOO_LARGEExcede o maxObjectBytes do próprio namespace
QUOTA_EXCEEDEDO filestore está cheio
CONTENT_TYPE_NOT_ALLOWEDO namespace restringe os contentTypes
NOT_SUPPORTEDO backend não consegue fazer isto
NOT_AVAILABLEEsta implantação não possui serviço de arquivos
PRESIGN_UNREACHABLEO armazenamento de objetos não é alcançável diretamente (um proxy?)
CLOCK_SKEWO relógio do dispositivo está defasado demais
INTERNALQualquer outra coisa

Um servidor mais novo pode introduzir códigos que este release do SDK não conhece. Eles são repassados como code em vez de serem colapsados, portanto trate um valor não reconhecido como uma falha genérica.

Em Python, FileStoreError é importado de ironflock.filestore; em JavaScript ele é exportado a partir da raiz do pacote (import { FileStoreError } from "ironflock").

Comunicação Entre Dispositivos

registerDeviceFunction / register_device_function

Registra um procedimento que outros dispositivos no mesmo projeto podem chamar. O SDK automaticamente atribui um namespace ao procedimento para o dispositivo atual.

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

register() é um alias para register_device_function().

callDeviceFunction / call_device_function

Chama um procedimento registrado por outro dispositivo. O SDK monta o tópico WAMP completo automaticamente usando a chave do 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

Chama um procedimento remoto usando um URI WAMP completo. Use isto para chamadas diretas quando você conhece o tópico exato.

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

Metadados do Dispositivo

setDeviceLocation / set_device_location

Atualiza a localização GPS do dispositivo na plataforma. As alterações são refletidas em tempo real nos mapas do IronFlock.

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

O histórico de localização não é armazenado. Para rastrear a localização ao longo do tempo, crie uma tabela dedicada e use publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Retorna a URL pública de acesso remoto para uma determinada porta no dispositivo.

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

Propriedades de Conexão e Ciclo de Vida

PropriedadeTipoDescrição
is_connectedboolSe a conexão com a plataforma está ativa
connectionCrossbarConnectionA instância de conexão subjacente (uso avançado)
MétodoDescrição
run()Inicia a conexão e executa mainFunc (bloqueante)
await start()Inicia a conexão de forma assíncrona
await stop()Encerra a conexão e cancela as tarefas em execução
await run_async()Inicia e mantém a conexão em execução de forma assíncrona

Tratamento de Erros

Todo método do SDK falha de forma explícita: diante de argumentos inválidos, de uma conexão perdida ou de uma rejeição da plataforma, ele levanta uma exceção (Python) ou rejeita (JavaScript) com uma mensagem que nomeia a operação, o tópico e o motivo. Nada é silenciosamente engolido, portanto envolva em um bloco try as chamadas que você deseja que sobrevivam.

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}")

Em JavaScript, as falhas provenientes da plataforma são instâncias de WampError — uma subclasse normal de Error que adicionalmente carrega o URI de erro WAMP em error e a carga do erro em args / kwargs. Todo o restante (parâmetros inválidos, ausência de conexão) é um Error comum.

Migrando: versões mais antigas do SDK registravam uma mensagem e retornavam None / null quando uma chamada falhava. Agora elas levantam o erro, de modo que código no formato if result is None: não detecta mais as falhas — use try / except (ou try / catch).

Uso no Navegador (apenas JavaScript)

O SDK em JavaScript funciona em navegadores modernos. Como os navegadores não possuem variáveis de ambiente, passe toda a configuração pelo construtor:

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 }]);

Use IronFlock.fromServer() para buscar a configuração do seu backend em vez de embutir as credenciais no código:

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

O endpoint do seu backend deve retornar um objeto JSON com as opções de conexão (serialNumber, deviceKey, appName, swarmKey, appKey, env).

Registrando Funções para Agentes de IA

O SDK pode registrar funções que podem ser chamadas por agentes de IA. Registre um procedimento e referencie o seu tópico no seu 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)

O agente de IA pode então chamar esta função quando um usuário fizer uma pergunta que exija dados de sensor ao vivo.

Para conectar o tópico WAMP registrado a um agente de IA, referencie-o no .ironflock/ai-template.yml da sua aplicação:

sensor_agent: tool_description: | Delegate to this agent when the user asks about sensor readings, live device data, or current environmental conditions. system_prompt: | You are a sensor data specialist. Use get_current to retrieve the latest reading from any sensor. Always include the unit in your response. main: true max_context_tokens: 30000 max_iterations: 5 tools: get_current: description: Returns the latest reading from a sensor. topic: sensors.get_latest parameters: sensor_id: type: string description: The sensor identifier to query. required: true

O valor de topic (sensors.get_latest) deve corresponder ao nome passado para register_device_function / registerDeviceFunction no seu código de borda. O IronFlock roteia automaticamente a chamada para o dispositivo onde a função está registrada.

Para a referência completa do ai-template.yml, consulte Definindo Agentes e Ferramentas.

Variáveis de Ambiente

Estas variáveis são definidas automaticamente pelo runtime do IronFlock dentro dos contêineres de aplicação:

VariávelDescrição
DEVICE_NAMENome de exibição do dispositivo
DEVICE_SERIAL_NUMBERIdentificador único e imutável do dispositivo
DEVICE_KEYChave do dispositivo para autenticação
SWARM_KEYIdentificador do projeto
APP_KEYIdentificador da aplicação
APP_NAMENome da aplicação
ENVAmbiente: DEV ou 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