Skip to Content
IoT-App-EntwicklungIronFlock SDK

IronFlock SDK

Das IronFlock SDK ermöglicht Ihren Edge-Anwendungen die Interaktion mit der IronFlock-Plattform. Es übernimmt die Authentifizierung automatisch, wenn es auf einem registrierten Gerät ausgeführt wird, und bietet Funktionen zum Veröffentlichen von Daten, Abfragen von Verlaufsdaten, Aufrufen von Remote-Prozeduren über Geräte hinweg sowie zum Aktualisieren von Gerätemetadaten.

SDKPaketVoraussetzung
Pythonironflock auf PyPIPython 3.8+
JavaScriptironflock auf npmNode.js 18+ oder moderner Browser

Installation

pip install ironflock

Oder fügen Sie ironflock zur requirements.txt Ihrer App hinzu.

Schnellstart

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()

Bei Verwendung innerhalb eines IronFlock-App-Containers liest das SDK die Verbindungsdaten automatisch aus der Umgebung — keine manuelle Konfiguration erforderlich.

Konstruktor-Optionen

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParameterBeschreibung
mainFuncEine asynchrone Funktion, die ausgeführt wird, sobald die Verbindung hergestellt ist
serial_numberÜberschreibt die Geräte-Seriennummer. Standardmäßig wird die Umgebungsvariable DEVICE_SERIAL_NUMBER verwendet

Daten veröffentlichen

publishToTable / publish_to_table

Veröffentlicht einen Datensatz in einer Fleet-Tabelle. Der Tabellenname muss einer in der data-template.yml Ihrer App definierten Tabelle entsprechen. Das SDK leitet die Daten automatisch an die richtige Projektdatenbank weiter.

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

appendToTable / append_to_table

Fügt Daten über einen Remote Procedure Call anstelle von Pub/Sub an eine Fleet-Tabelle an. Verwenden Sie dies, wenn Sie eine Bestätigung benötigen, dass die Daten gespeichert wurden.

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

publishRowsToTable / publish_rows_to_table

Veröffentlicht mehrere Zeilen in einer einzigen Nachricht (Bulk-Insert) in einer Fleet-Tabelle. Die Plattform fügt den gesamten Stapel atomar (Alles-oder-nichts) in einem einzigen Vorgang ein. Verwenden Sie dies für hochfrequente Daten, bei denen ein Roundtrip pro Zeile zu kostspielig wäre. Wie publishToTable erfolgt dies nach dem Fire-and-Forget-Prinzip — die Bestätigung bestätigt die Zustellung an den Router, nicht den Datenbank-Insert.

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

Das zweite Argument ist eine nicht leere Liste von einzufügenden Zeilenobjekten.

appendRowsToTable / append_rows_to_table

Hängt mehrere Zeilen in einem einzigen Remote Procedure Call (Bulk-Insert) an eine Fleet-Tabelle an. Die Plattform fügt den gesamten Stapel atomar (Alles-oder-nichts) ein: Ist eine Zeile ungültig, wird der gesamte Stapel abgelehnt und nichts wird gespeichert. Bevorzugen Sie dies gegenüber publishRowsToTable / publish_rows_to_table, wenn Sie das Ergebnis des Inserts benötigen.

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

Meldet einen Anwendungsfehler in die error-logs-Tabelle Ihrer Fleet. Dies ist ein komfortabler Wrapper über publishToTable / appendToTable: Er versieht die Zeile mit source: "app", einem Schweregrad level und einem Zeitstempel und schreibt sie anschließend wie jede normale Tabellenzeile. Der Fehler landet in derselben error-logs-Tabelle, die auch fleetdb-Systemfehler verwenden (gekennzeichnet mit source: "system"), und ist somit mit getHistory abfragbar, mit subscribeToTable / subscribe_to_table streambar, in Board-Templates nutzbar und wird in Echtzeit auf transformed.error-logs zugestellt — ohne den System-Fehler-Toast der Plattform auszulösen.

# 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)

Parameter:

ParameterTypBeschreibung
errorstr / string oder Exception / ErrorDie Fehlermeldung oder eine Exception, deren Traceback/Stack (oder Meldung) aufgezeichnet wird
levelstr / string, optionalSchweregrad: "error", "warn", "info" oder "debug". Standardwert ist "error"
appendbool / boolean, optionalBei true wird der Append-RPC verwendet (gibt das Ergebnis des Inserts zurück). Standardwert ist false (Fire-and-Forget-Publish)
tspstr / string, optionalÜberschreibt den Zeitstempel im ISO-8601-Format. Standardwert ist die aktuelle Zeit

In Python werden die Optionen als Keyword-Argumente übergeben (report_error(error, level=..., append=..., tsp=...)); in JavaScript werden sie über ein Optionsobjekt übergeben (reportError(error, { level, append, tsp })).

publish

Veröffentlicht eine Nachricht an ein beliebiges WAMP-Topic. Verwenden Sie dies für benutzerdefinierte Nachrichten oder Ereignisse, die keiner Datenbanktabelle zugeordnet sind.

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

Historische Daten abfragen

getHistory

Ruft historische Daten aus einer Fleet-Tabelle ab. Unterstützt Filterung, Zeitbereiche und Paginierung.

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

Abfrageparameter:

FeldTypBeschreibung
limitint / numberMaximale Anzahl zurückgegebener Zeilen (1–10.000, erforderlich)
offsetint / numberOffset für Paginierung
timeRangedict / object{"start": "<ISO-Datum>", "end": "<ISO-Datum>"}
filterAndlist / arrayUND-Filterbedingungen und/oder der latest-Marker (siehe unten)
columnslist / arrayZurückzugebende Spalten (optional). tsp, device_key und authid sind immer enthalten; weglassen für alle Spalten

Filteroperatoren: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

Jeder Filter ist ein Objekt mit den Schlüsseln column, operator und value.

Aktuelle Werte lesen. Ein Eintrag {"latest": true} in filterAnd ist keine Filterbedingung, sondern ein Modusschalter: Das Daten-Backend gibt nur die neueste Zeile pro Entität zurück, in SQL abgeleitet aus dem Entitätsschlüssel, den die Tabelle mit maintainLatestFlagFor deklariert. Eine Tabelle ohne Entitätsschlüssel gibt ihre einzelne aktuellste Zeile zurück.

Andere Bedingungen lassen sich wie erwartet mit dem Marker kombinieren: Bedingungen auf Spalten des Entitätsschlüssels schränken ein, welche Entitäten zurückgegeben werden, während alle übrigen Bedingungen und timeRange auf die resultierenden neuesten Zeilen angewendet werden. Die Kombination von {"latest": true} mit einem deleted-Filter blendet gelöschte Entitäten also aus, statt deren vorherige Zeile wieder auftauchen zu lassen.

Frühere Versionen von IronFlock speicherten eine physische Spalte latest_flag. Diese existiert nicht mehr — ein alter Filter latest_flag = true wird weiterhin akzeptiert und als Marker behandelt, neuer Code sollte aber {"latest": true} verwenden. Der latest-Marker steht in getSeriesHistory nicht zur Verfügung.

getSeriesHistory / get_series_history

Ruft heruntergerechnete Zeitreihendaten aus einer Fleet-Tabelle ab: numerische Spalten, die in Zeitfenster aggregiert werden (z. B. stündliche Durchschnittswerte). Ideal für Diagramme über lange Zeiträume. Für Tabellen verfügbar (nicht für 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"] })

Abfrageparameter:

FeldTypBeschreibung
metricslist / arrayNumerische Spalten, die heruntergerechnet werden
methodstr / stringAggregation pro Zeitfenster: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" oder "LAST"
limitint / numberMaximale Anzahl von Zeitfenstern (1–10.000)
timeRangelist / array[start, end] — ISO-Datetime-Strings oder Epoch-ms-Zahlen; null = offenes Ende (erforderlich)
groupBylist / arraySpalten, nach denen die Serie gruppiert wird (optional)
filterAndlist / arrayUND-Filterbedingungen (optional). Nur Filterbedingungen — der latest-Marker wird hier nicht unterstützt; verwenden Sie getHistory, um aktuelle Werte zu lesen

Daten abonnieren

subscribeToTable / subscribe_to_table

Abonniert Echtzeit-Updates einer Fleet-Tabelle. Der Handler wird aufgerufen, wenn neue Daten in der Tabelle veröffentlicht werden. Zeilen, die über den Bulk-Insert-Pfad (publishRowsToTable / appendRowsToTable) geschrieben werden, werden Ihrem Handler einzeln zugestellt, sodass der Handler-Code unabhängig davon, wie die Daten geschrieben wurden, gleich bleibt.

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

subscribe

Abonniert ein beliebiges WAMP-Topic für benutzerdefinierte Echtzeit-Kommunikation.

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

App-übergreifender Datenzugriff

Lesen Sie die Fleet-Daten einer anderen App aus Ihrer eigenen App heraus – innerhalb desselben Projekts. Die bereitstellende App muss Ihre App in ihrer data-template.yml im Abschnitt consumes: aufführen, und der Projektbenutzer muss den Zugriff gewähren. Der Zugriff ist schreibgeschützt: Sie können den Verlauf abfragen und Zeilen der vom Anbieter freigegebenen Tabellen und Transforms in Echtzeit abonnieren, aber nicht in sie schreiben. Verbindungen zu konsumierten Apps werden pro App zwischengespeichert und automatisch geschlossen, wenn Ihre Instanz stoppt.

Wenn Ihre App die Wildcard-Berechtigung besitzt (consumes: [{ app: "*" }]), können Sie Anbieter dynamisch mit listConsumableApps / list_consumable_apps und connectToAllApps / connect_to_all_apps (siehe unten) auffinden und öffnen.

connectToApp / connect_to_app

Öffnet eine schreibgeschützte Verbindung zum Data Backend einer anderen App und gibt ein Handle zurück. Das Handle stellt getHistory / get_history, subscribeToTable / subscribe_to_table und getSeriesHistory / get_series_history (nur Tabellen) bereit – dasselbe Abfragen und Abonnieren wie bei Ihren eigenen Tabellen – sowie close und die freigegebenen Kataloge 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)

Parameter:

ParameterTypBeschreibung
app_name / appNamestr / stringName der bereitstellenden App, wie in Ihrem consumes:-Abschnitt deklariert
stagestr / string, optionalStage des Anbieters: "dev" oder "prod". Standardmäßig die Stage Ihrer eigenen App
on_error / onErrorCallback, optionalWird mit einem CrossAppAccessError aufgerufen, wenn der Zugriff verweigert wird, nachdem die Verbindung hergestellt wurde (z. B. wenn die Berechtigung später widerrufen wird)

Bei verweigertem oder falsch verwendetem Zugriff wird ein CrossAppAccessError ausgelöst (Python) bzw. geworfen (JavaScript) mit einem code-Feld: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE oder NOT_AUTHORIZED.

In Python sind stage und on_error Keyword-Argumente; in JavaScript werden sie über ein Optionsobjekt übergeben (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Listet jeden nicht privaten Anbieter im Projekt auf – das Discovery-Primitiv für Apps, die die Wildcard-Consume-Berechtigung besitzen (consumes: [{ app: "*" }] in Ihrer data-template.yml, gewährt durch den Projektbenutzer). Sie führt einen einzigen Aufruf aus und öffnet keine Verbindungen: Stellen Sie die zurückgegebenen Kataloge in einem Auswahlmenü dar und rufen Sie dann connectToApp / connect_to_app für die gewünschten auf – oder connectToAllApps / connect_to_all_apps, um alle auf einmal zu öffnen.

Hinweis: Deklarieren Sie die Berechtigung in der data-template.yml Ihrer App und setzen Sie das * in Anführungszeichen – ein bloßes * ist ein YAML-Alias und kann nicht geparst werden:

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"]

Jeder Eintrag beschreibt einen Anbieter:

FeldTypBeschreibung
appstr / stringName der bereitstellenden App
provider_app_keyint / numberDer App-Schlüssel des Anbieters
stagesdict / objectKatalog pro Stage { dev?, prod? }; eine Stage ist nur vorhanden, wenn der Anbieter dafür ein Data Backend besitzt. Jeder Katalog enthält die nicht privaten tables und transforms, die er freigibt

Es wird ein CrossAppAccessError mit code: NO_GRANT ausgelöst (Python) bzw. geworfen (JavaScript), wenn Ihre App keine Wildcard-Berechtigung besitzt.

connectToAllApps / connect_to_all_apps

Öffnet in einem einzigen Aufruf schreibgeschützte Handles zu jedem nicht privaten Anbieter im Projekt (nur für Wildcard-Consumer). Zählt die Anbieter über listConsumableApps / list_consumable_apps auf und öffnet jeden einzelnen, wobei alle ohne Data Backend für die angeforderte Stage übersprungen werden. Jedes Handle wird unter demselben Schlüssel wie bei connectToApp / connect_to_app zwischengespeichert, sodass ein späteres connectToApp(name) das bereits vorgewärmte Handle zurückgibt. Die zurückgegebenen Handles werden gemeinsam geschlossen, wenn Ihre Instanz stoppt.

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)

Parameter:

ParameterTypBeschreibung
stagestr / string, optionalStage des Anbieters: "dev" oder "prod". Standardmäßig die Stage Ihrer eigenen App
continue_on_error / continueOnErrorbool / boolean, optionalBei true (Standardwert) wird ein Anbieter, der nicht geöffnet werden kann, an on_error / onError gemeldet und aus dem Ergebnis ausgelassen. Bei false wird der erste Fehler ausgelöst/geworfen
on_error / onErrorcallable, optionalWird mit jedem Anbieter aufgerufen, der nicht geöffnet werden konnte (solange continue_on_error / continueOnError true ist), und mit einem CrossAppAccessError, wenn einer bereits geöffneten Verbindung später der Zugriff verweigert wird (z. B. wenn die Berechtigung widerrufen wird)

Gibt die erfolgreich geöffneten Anbieter-Handles zurück (derselbe Handle-Typ wie bei connectToApp / connect_to_app). Es wird ein CrossAppAccessError mit code: NO_GRANT ausgelöst (Python) bzw. geworfen (JavaScript), wenn Ihre App keine Wildcard-Berechtigung besitzt.

In Python sind stage, on_error und continue_on_error Keyword-Argumente; in JavaScript werden sie über ein Optionsobjekt übergeben (connectToAllApps({ stage, onError, continueOnError })).

Verwalteter Dateispeicher

Jedes App-Data-Backend erhält neben seinen Tabellen einen privaten Objektspeicher, erreichbar über die Eigenschaft files. Verwenden Sie ihn für Bilder, PDFs, Kamerabilder, Firmware-Blobs — alles, was nicht in eine Tabellenzeile gehört. Es ist keine Einrichtung nötig: Auch eine App ohne files:-Abschnitt in ihrer data-template.yml erhält einen Namespace mit dem Namen default.

Der zentrale Gedanke: Das Ablegen eines Objekts liefert Ihnen eine permanente URL zurück, die Sie direkt in eine Tabellenspalte schreiben können, sodass ein Dashboard-Widget sie ohne weiteres Zutun darstellen kann:

# 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)

Diese URL läuft nie ab, ist aber kein öffentlicher Link: Sie bleibt nur für einen authentifizierten Anfragenden lesbar, der READ-Zugriff auf dieses Data Backend besitzt, und ein Auth-Proxy prüft das bei jeder Anfrage erneut. Sie kann daher gefahrlos in der Datenbank gespeichert werden.

Namespaces

Ein Namespace ist ein Schlüsselpräfix, das Richtlinien mitführt — Aufbewahrung, Freigaberegeln, erlaubte Content-Types. Er ist kein separater Bucket; jeder Namespace einer App liegt innerhalb des einen Speicherbereichs dieser App. Deklarieren Sie einen eigenen Namespace nur, wenn eine Menge von Objekten andere Regeln benötigt; bleiben Sie andernfalls in default und organisieren Sie Ihre Objekte über Schlüsselpfade wie 2026/03/part-1.jpg.

Zusätzliche Namespaces deklarieren Sie in der 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 }

Beachten Sie, dass das Budget einmal für die gesamte App deklariert wird, nicht pro Namespace. Ein Namespace ist lediglich ein Schlüsselpräfix innerhalb des einen Speicherbereichs der App, sodass es nichts gibt, wogegen sich ein Budget pro Präfix durchsetzen ließe. maxObjectBytes gilt dagegen pro Namespace — es begrenzt ein einzelnes Objekt, keine Summe.

Jede der folgenden Methoden nimmt den Namespace als optionales Argument entgegen und verwendet standardmäßig default.

Objekte ablegen und lesen

# 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")
MethodeBeschreibung
put(key, data, …)Legt ein Objekt ab (bytes in Python, Uint8Array in JavaScript). Gibt die Metadaten des Objekts inklusive seiner url zurück
get(key, namespace?)Gibt den Inhalt des Objekts zurück
put_file(key, path, …) / get_to_file(key, path, …)Nur Python. Aus einer lokalen Datei ablegen bzw. in eine lokale Datei schreiben. Streamt auf dem Pfad für große Objekte
delete(key, namespace?)Löscht ein Objekt
copy(key, to, …)Kopiert ein Objekt, optional in einen anderen Namespace
move(key, to, …)Kopieren und anschließend löschen. Nicht atomar — der Dienst kennt kein Move-Verb, daher hinterlässt ein fehlgeschlagenes Löschen beide Kopien

JavaScript besitzt keine Helfer für Dateipfade, weil das Paket einen einzigen Build für Node und den Browser ausliefert — lesen und schreiben Sie lokale Dateien selbst mit fs.

put akzeptiert: content_type / contentType (den MIME-Typ; der Namespace kann einschränken, welche erlaubt sind) und namespace. In Python sind dies Keyword-Argumente; in JavaScript stehen sie in einem Optionsobjekt.

Auflisten und inspizieren

MethodeBeschreibung
list(…)Eine Seite von Objekten. Gibt objects, prefixes, is_truncated / isTruncated und einen cursor zurück, den Sie für die nächste Seite wieder übergeben
iter(…) / iterate(…)Asynchroner Iterator über jedes Objekt unterhalb eines Präfixes, mit automatischer Paginierung. In Python heißt er iter, in JavaScript iterate
stat(key, namespace?)Metadaten zu einem Objekt, ohne dessen Inhalt zu übertragen
exists(key, namespace?)Ob ein Objekt existiert
namespaces()Die Namespaces, die diese App verwenden darf
usage(…)Wie viel Speicher die App belegt — siehe unten
catalog()Namespaces sowie die vom Server vorgegebenen Limits und Kontingente. Wird nach dem ersten Aufruf zwischengespeichert

Objekte werden in beiden SDKs durch dieselben Felder beschrieben, jeweils in der Namenskonvention der Sprache: namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 und url.

Speicherbelegung und Kontingent

usage beantwortet die Frage in einem einzigen Aufruf direkt aus dem Objektspeicher, sodass die Summen exakt sind und nicht vom SDK zusammengezählt werden:

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}
FeldBedeutung
size_bytes / sizeBytesAktuell gespeicherte Bytes
object_count / objectCountAnzahl der gespeicherten Objekte
quota_bytes / quotaBytesDas durchgesetzte Budget. 0 bedeutet unbegrenzt
free_bytes / freeBytesVerbleibende Bytes. -1 bedeutet unbegrenzt — eine 0 würde hier als „voll“ gelesen
per_namespace / perNamespaceBytes pro Namespace. Nur vorhanden, wenn Sie die detaillierte Aufschlüsselung anfordern

Die Aufschlüsselung pro Namespace ist standardmäßig abgeschaltet, weil der Speicher sie nicht direkt beantworten kann: Er rechnet pro Speicherbereich ab, und ein Namespace ist nur ein Präfix, sodass das SDK jeden Namespace auflisten muss, um die Größen aufzusummieren. Fordern Sie sie an, wenn Sie sie brauchen, aber nicht auf einem heißen Pfad.

Es tauchen zwei verschiedene Kontingente auf, und es lohnt sich, sie auseinanderzuhalten. catalog() meldet beide:

FeldBedeutung
quota_bytes / quotaBytesWas tatsächlich durchgesetzt wird, aus dem Objektspeicher gelesen — die Einstellung des Projektnutzers
suggested_quota_bytes / suggestedQuotaBytesWas das Data Template der App angefordert hat. 0, wenn es nichts angefordert hat

Sie unterscheiden sich immer dann, wenn ein Nutzer das Budget der App erhöht oder gesenkt hat. Genau deshalb wird der durchgesetzte Wert aus dem Speicher gelesen und nicht aus dem Template — ein erneutes Deployment der App darf die Entscheidung eines Nutzers nicht stillschweigend zurücksetzen. Eine Oberfläche kann beide anzeigen („die App schlägt X vor, Sie haben Y eingestellt“). Durchgesetzt wird immer der erste Wert.

Objekte freigeben

Es gibt zwei Arten von Links, und der Unterschied ist wesentlich:

MethodeLebensdauerWer kann ihn lesen
url(key, …)PermanentNur ein authentifizierter Anfragender mit READ auf diesem Data Backend — bei jeder Anfrage erneut geprüft. Kann gefahrlos in einer Tabellenspalte gespeichert werden
share_url / shareUrlAblaufend (standardmäßig 15 Minuten, serverseitig begrenzt)Jeder, der den Link besitzt. Bei seiner Verwendung prüft nichts die Autorisierung erneut

share_url / shareUrl ist eine Bearer-Berechtigung: Geben Sie den Link an eine Person weiter, die vorübergehend Zugriff benötigt, und speichern Sie ihn nicht in der Datenbank. Verwenden Sie url für alles, was ein Dashboard darstellt.

url gibt None / undefined zurück, wenn das Deployment keinen HTTP-Edge besitzt (zum Beispiel eine Appliance mit reinem HTTP) — das ist das Signal, auf get zurückzufallen. Wird der etag eines Objekts als version-Argument übergeben, können Browser die Antwort unveränderlich zwischenspeichern.

upload_url / uploadUrl erzeugt eine ablaufende URL, die einen direkten Upload entgegennimmt, und gibt url, method, headers sowie expires_in / expiresIn zurück. Senden Sie genau die Header, die sie zurückgibt, sonst lässt sich die Signatur nicht verifizieren.

Große Objekte

Das SDK wählt den Transportweg automatisch anhand der Größe — es gibt nichts zu konfigurieren:

ObjektgrößeÜbertragungsweg
Bis zum Inline-Limit (derzeit 6 MiB)Ein einzelner Aufruf über den Message Router
DarüberDirekt in den Objektspeicher über HTTPS, unter Umgehung des Routers

Das genaue Limit meldet der Server zur Laufzeit als inline_max_bytes / inlineMaxBytes in catalog(), sodass es ohne ein SDK-Release angehoben werden kann.

Zwei Obergrenzen bleiben bestehen, und beide melden TOO_LARGE mit einem Grund, der benennt, welche davon Sie erreicht haben:

  • 5 GiB — das Limit des Objektspeichers für einen einzelnen Upload. Multipart-Upload ist noch nicht implementiert.
  • Das Inline-Limit, wo es keinen direkten Endpunkt gibt — eine Air-Gap-Appliance kann ein großes Objekt überhaupt nicht übertragen. Kein Retry und keine kleinere Chunk-Größe hilft, und die Meldung sagt das auch.

Der direkte Pfad setzt voraus, dass das Gerät den Host des Objektspeichers erreicht, nicht nur den Router. Zwei im Feld häufige Fehler erhalten eigene Codes, statt wie Autorisierungsprobleme auszusehen: PRESIGN_UNREACHABLE (ein Proxy, der nur den Router zulässt) und CLOCK_SKEW (der Objektspeicher weist Anfragen zurück, die mehr als 15 Minuten abweichen — prüfen Sie NTP auf dem Gerät).

Fehler beim Dateispeicher

Jede Dateioperation löst einen FileStoreError aus (Python) bzw. wirft ihn (JavaScript), der einen stabilen code und einen menschenlesbaren reason trägt. Verzweigen Sie über code, niemals über 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
CodeBedeutung
NOT_AUTHORIZEDDer Aufrufer darf diese Operation nicht ausführen
NO_SUCH_NAMESPACEDer Namespace ist in der data-template.yml nicht deklariert
NO_SUCH_OBJECTDer Schlüssel existiert nicht
TOO_LARGEÜberschreitet das Übertragungslimit für einen einzelnen Aufruf
OBJECT_TOO_LARGEÜberschreitet das maxObjectBytes des jeweiligen Namespace
QUOTA_EXCEEDEDDer Dateispeicher ist voll
CONTENT_TYPE_NOT_ALLOWEDDer Namespace schränkt contentTypes ein
NOT_SUPPORTEDDas Backend kann dies nicht
NOT_AVAILABLEDieses Deployment besitzt keinen Dateidienst
PRESIGN_UNREACHABLEDer Objektspeicher ist nicht direkt erreichbar (ein Proxy?)
CLOCK_SKEWDie Geräteuhr weicht zu stark ab
INTERNALAlles Übrige

Ein neuerer Server kann Codes einführen, die dieses SDK-Release noch nicht kennt. Sie werden unverändert als code durchgereicht statt zusammengefasst zu werden; behandeln Sie einen unbekannten Wert daher als generischen Fehler.

In Python wird FileStoreError aus ironflock.filestore importiert; in JavaScript wird er aus dem Paket-Root exportiert (import { FileStoreError } from "ironflock").

Geräteübergreifende Kommunikation

registerDeviceFunction / register_device_function

Registriert eine Prozedur, die andere Geräte im selben Projekt aufrufen können. Das SDK versieht die Prozedur automatisch mit einem Namespace für das aktuelle Gerät.

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

register() ist ein Alias für register_device_function().

callDeviceFunction / call_device_function

Ruft eine von einem anderen Gerät registrierte Prozedur auf. Das SDK erstellt das vollständige WAMP-Topic automatisch anhand des Schlüssels des Zielgeräts.

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

call

Ruft eine Remote-Prozedur über eine vollständige WAMP-URI auf. Verwenden Sie dies für direkte Aufrufe, wenn Sie das genaue Topic kennen.

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

Gerätemetadaten

setDeviceLocation / set_device_location

Aktualisiert den GPS-Standort des Geräts in der Plattform. Änderungen werden in Echtzeit auf IronFlock-Karten angezeigt.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
ParameterBereich
long-180 bis 180
lat-90 bis 90

Der Standortverlauf wird nicht gespeichert. Um den Standort über die Zeit zu verfolgen, erstellen Sie eine eigene Tabelle und verwenden Sie publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Gibt die öffentliche Remote-Zugriffs-URL für einen bestimmten Port auf dem Gerät zurück.

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

Verbindungseigenschaften & Lebenszyklus

EigenschaftTypBeschreibung
is_connectedboolOb die Plattformverbindung aktiv ist
connectionCrossbarConnectionDie zugrunde liegende Verbindungsinstanz (für fortgeschrittene Nutzung)
MethodeBeschreibung
run()Startet die Verbindung und führt mainFunc aus (blockierend)
await start()Startet die Verbindung asynchron
await stop()Stoppt die Verbindung und bricht laufende Aufgaben ab
await run_async()Startet die Verbindung und hält sie asynchron aufrecht

Fehlerbehandlung

Jede SDK-Methode schlägt lautstark fehl: Bei ungültigen Argumenten, einer abgerissenen Verbindung oder einer Ablehnung durch die Plattform löst sie eine Exception aus (Python) bzw. wird das Promise abgelehnt (JavaScript) — mit einer Meldung, die die Operation, das Topic und den Grund nennt. Nichts wird stillschweigend verschluckt; verpacken Sie Aufrufe, die überleben sollen, daher in einen try-Block.

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

In JavaScript sind Fehler, die von der Plattform stammen, WampError-Instanzen — eine gewöhnliche Unterklasse von Error, die zusätzlich die WAMP-Fehler-URI in error und die Fehlerdaten in args / kwargs transportiert. Alles andere (fehlerhafte Parameter, keine Verbindung) ist ein einfacher Error.

Migration: Ältere SDK-Versionen protokollierten eine Meldung und gaben None / null zurück, wenn ein Aufruf fehlschlug. Sie lösen jetzt stattdessen einen Fehler aus, sodass Code der Form if result is None: Fehlschläge nicht mehr erkennt — verwenden Sie try / except (bzw. try / catch).

Browser-Nutzung (nur JavaScript)

Das JavaScript SDK funktioniert in modernen Browsern. Da Browser keine Umgebungsvariablen haben, übergeben Sie alle Konfigurationsoptionen über den Konstruktor:

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

Verwenden Sie IronFlock.fromServer(), um die Konfiguration von Ihrem Backend abzurufen, anstatt Zugangsdaten fest einzuprogrammieren:

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

Ihr Backend-Endpunkt sollte ein JSON-Objekt mit den Verbindungsoptionen (serialNumber, deviceKey, appName, swarmKey, appKey, env) zurückgeben.

AI-Agent-Funktionen registrieren

Das SDK kann Funktionen registrieren, die von AI-Agents aufgerufen werden können. Registrieren Sie eine Prozedur und referenzieren Sie deren Topic in Ihrer 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)

Der AI-Agent kann diese Funktion dann aufrufen, wenn ein Benutzer eine Frage stellt, die aktuelle Sensordaten erfordert.

Um das registrierte WAMP-Topic mit einem AI-Agenten zu verbinden, referenzieren Sie es in der .ironflock/ai-template.yml Ihrer App:

sensor_agent: tool_description: | Delegiere an diesen Agenten, wenn der Benutzer nach Sensormesswerten, Live-Gerätedaten oder aktuellen Umgebungsbedingungen fragt. system_prompt: | Du bist ein Sensordaten-Spezialist. Verwende get_current, um den aktuellen Messwert eines beliebigen Sensors abzurufen. Gib in deiner Antwort immer die Einheit an. main: true max_context_tokens: 30000 max_iterations: 5 tools: get_current: description: Gibt den aktuellen Messwert eines Sensors zurück. topic: sensors.get_latest parameters: sensor_id: type: string description: Der abzufragende Sensor-Bezeichner. required: true

Der Wert von topic (sensors.get_latest) muss mit dem Namen übereinstimmen, der bei register_device_function / registerDeviceFunction im Edge-Code angegeben wurde. IronFlock leitet den Aufruf automatisch an das Gerät weiter, auf dem die Funktion registriert ist.

Die vollständige Referenz für ai-template.yml finden Sie unter Agents & Tools definieren.

Umgebungsvariablen

Diese Variablen werden von der IronFlock-Laufzeitumgebung automatisch innerhalb von App-Containern gesetzt:

VariableBeschreibung
DEVICE_NAMEAnzeigename des Geräts
DEVICE_SERIAL_NUMBEREindeutiger, unveränderlicher Gerätebezeichner
DEVICE_KEYGeräteschlüssel für die Authentifizierung
SWARM_KEYProjektbezeichner
APP_KEYApp-Bezeichner
APP_NAMEApp-Name
ENVUmgebung: DEV oder 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