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.
| SDK | Paket | Voraussetzung |
|---|---|---|
| Python | ironflock auf PyPI | Python 3.8+ |
| JavaScript | ironflock auf npm | Node.js 18+ oder moderner Browser |
Installation
Python
pip install ironflockOder fügen Sie ironflock zur requirements.txt Ihrer App hinzu.
Schnellstart
Python
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
Python
ironflock = IronFlock(
mainFunc=main, # async function to run after connecting
serial_number="abc123" # override device serial (optional)
)| Parameter | Beschreibung |
|---|---|
mainFunc | Eine 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.
Python
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.
Python
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.
Python
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.
Python
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.
Python
# 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
error | str / string oder Exception / Error | Die Fehlermeldung oder eine Exception, deren Traceback/Stack (oder Meldung) aufgezeichnet wird |
level | str / string, optional | Schweregrad: "error", "warn", "info" oder "debug". Standardwert ist "error" |
append | bool / boolean, optional | Bei true wird der Append-RPC verwendet (gibt das Ergebnis des Inserts zurück). Standardwert ist false (Fire-and-Forget-Publish) |
tsp | str / 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.
Python
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.
Python
# 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:
| Feld | Typ | Beschreibung |
|---|---|---|
limit | int / number | Maximale Anzahl zurückgegebener Zeilen (1–10.000, erforderlich) |
offset | int / number | Offset für Paginierung |
timeRange | dict / object | {"start": "<ISO-Datum>", "end": "<ISO-Datum>"} |
filterAnd | list / array | UND-Filterbedingungen und/oder der latest-Marker (siehe unten) |
columns | list / array | Zurü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).
Python
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:
| Feld | Typ | Beschreibung |
|---|---|---|
metrics | list / array | Numerische Spalten, die heruntergerechnet werden |
method | str / string | Aggregation pro Zeitfenster: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" oder "LAST" |
limit | int / number | Maximale Anzahl von Zeitfenstern (1–10.000) |
timeRange | list / array | [start, end] — ISO-Datetime-Strings oder Epoch-ms-Zahlen; null = offenes Ende (erforderlich) |
groupBy | list / array | Spalten, nach denen die Serie gruppiert wird (optional) |
filterAnd | list / array | UND-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.
Python
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.
Python
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.
Python
# 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
app_name / appName | str / string | Name der bereitstellenden App, wie in Ihrem consumes:-Abschnitt deklariert |
stage | str / string, optional | Stage des Anbieters: "dev" oder "prod". Standardmäßig die Stage Ihrer eigenen App |
on_error / onError | Callback, optional | Wird 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
stageundon_errorKeyword-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.ymlIhrer App und setzen Sie das*in Anführungszeichen – ein bloßes*ist ein YAML-Alias und kann nicht geparst werden:
consumes:
- app: "*"Python
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:
| Feld | Typ | Beschreibung |
|---|---|---|
app | str / string | Name der bereitstellenden App |
provider_app_key | int / number | Der App-Schlüssel des Anbieters |
stages | dict / object | Katalog 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.
Python
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:
| Parameter | Typ | Beschreibung |
|---|---|---|
stage | str / string, optional | Stage des Anbieters: "dev" oder "prod". Standardmäßig die Stage Ihrer eigenen App |
continue_on_error / continueOnError | bool / boolean, optional | Bei 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 / onError | callable, optional | Wird 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_errorundcontinue_on_errorKeyword-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:
Python
# 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
Python
# 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")| Methode | Beschreibung |
|---|---|
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
| Methode | Beschreibung |
|---|---|
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:
Python
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}| Feld | Bedeutung |
|---|---|
size_bytes / sizeBytes | Aktuell gespeicherte Bytes |
object_count / objectCount | Anzahl der gespeicherten Objekte |
quota_bytes / quotaBytes | Das durchgesetzte Budget. 0 bedeutet unbegrenzt |
free_bytes / freeBytes | Verbleibende Bytes. -1 bedeutet unbegrenzt — eine 0 würde hier als „voll“ gelesen |
per_namespace / perNamespace | Bytes 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:
| Feld | Bedeutung |
|---|---|
quota_bytes / quotaBytes | Was tatsächlich durchgesetzt wird, aus dem Objektspeicher gelesen — die Einstellung des Projektnutzers |
suggested_quota_bytes / suggestedQuotaBytes | Was 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:
| Methode | Lebensdauer | Wer kann ihn lesen |
|---|---|---|
url(key, …) | Permanent | Nur ein authentifizierter Anfragender mit READ auf diesem Data Backend — bei jeder Anfrage erneut geprüft. Kann gefahrlos in einer Tabellenspalte gespeichert werden |
share_url / shareUrl | Ablaufend (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über | Direkt 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.
Python
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| Code | Bedeutung |
|---|---|
NOT_AUTHORIZED | Der Aufrufer darf diese Operation nicht ausführen |
NO_SUCH_NAMESPACE | Der Namespace ist in der data-template.yml nicht deklariert |
NO_SUCH_OBJECT | Der Schlüssel existiert nicht |
TOO_LARGE | Überschreitet das Übertragungslimit für einen einzelnen Aufruf |
OBJECT_TOO_LARGE | Überschreitet das maxObjectBytes des jeweiligen Namespace |
QUOTA_EXCEEDED | Der Dateispeicher ist voll |
CONTENT_TYPE_NOT_ALLOWED | Der Namespace schränkt contentTypes ein |
NOT_SUPPORTED | Das Backend kann dies nicht |
NOT_AVAILABLE | Dieses Deployment besitzt keinen Dateidienst |
PRESIGN_UNREACHABLE | Der Objektspeicher ist nicht direkt erreichbar (ein Proxy?) |
CLOCK_SKEW | Die Geräteuhr weicht zu stark ab |
INTERNAL | Alles Ü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
FileStoreErrorausironflock.filestoreimportiert; 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.
Python
def add(a, b):
return a + b
await ironflock.register_device_function("com.myapp.add", add)
register()ist ein Alias fürregister_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.
Python
result = await ironflock.call_device_function(
42, # target device key
"com.myapp.add", # procedure name
args=[3, 5] # arguments
)
print(result) # 8call
Ruft eine Remote-Prozedur über eine vollständige WAMP-URI auf. Verwenden Sie dies für direkte Aufrufe, wenn Sie das genaue Topic kennen.
Python
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.
Python
await ironflock.set_device_location(long=8.6821, lat=50.1109)| Parameter | Bereich |
|---|---|
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.
Python
url = ironflock.getRemoteAccessUrlForPort(8080)
# "https://<device_key>-<app_name>-8080.app.ironflock.com"Verbindungseigenschaften & Lebenszyklus
Python
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
is_connected | bool | Ob die Plattformverbindung aktiv ist |
connection | CrossbarConnection | Die zugrunde liegende Verbindungsinstanz (für fortgeschrittene Nutzung) |
| Methode | Beschreibung |
|---|---|
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.
Python
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/nullzurück, wenn ein Aufruf fehlschlug. Sie lösen jetzt stattdessen einen Fehler aus, sodass Code der Formif result is None:Fehlschläge nicht mehr erkennt — verwenden Sietry/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:
Python
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: trueDer 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:
| Variable | Beschreibung |
|---|---|
DEVICE_NAME | Anzeigename des Geräts |
DEVICE_SERIAL_NUMBER | Eindeutiger, unveränderlicher Gerätebezeichner |
DEVICE_KEY | Geräteschlüssel für die Authentifizierung |
SWARM_KEY | Projektbezeichner |
APP_KEY | App-Bezeichner |
APP_NAME | App-Name |
ENV | Umgebung: DEV oder PROD |
Python
import os
device_name = os.environ.get("DEVICE_NAME")
serial = os.environ.get("DEVICE_SERIAL_NUMBER")
project_key = os.environ.get("SWARM_KEY")