Skip to Content

SDK IronFlock

Le SDK IronFlock permet à vos applications edge d’interagir avec la plateforme IronFlock. Il gère l’authentification automatiquement lorsqu’il s’exécute sur un appareil enregistré et fournit des fonctions pour publier des données, interroger l’historique, appeler des procédures distantes entre appareils et mettre à jour les métadonnées des appareils.

SDKPackagePrérequis
Pythonironflock sur PyPIPython 3.8+
JavaScriptironflock sur npmNode.js 18+ ou navigateur moderne

Installation

pip install ironflock

Ou ajoutez ironflock au requirements.txt de votre application.

Démarrage rapide

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

Lorsqu’il est utilisé à l’intérieur d’un conteneur d’application IronFlock, le SDK lit automatiquement les identifiants de connexion depuis l’environnement — aucune configuration manuelle n’est nécessaire.

Options du constructeur

ironflock = IronFlock( mainFunc=main, # async function to run after connecting serial_number="abc123" # override device serial (optional) )
ParamètreDescription
mainFuncUne fonction asynchrone qui s’exécute une fois la connexion établie
serial_numberRemplace le numéro de série de l’appareil. Par défaut, la variable d’environnement DEVICE_SERIAL_NUMBER

Publier des données

publishToTable / publish_to_table

Publie un enregistrement de données dans une table de flotte. Le nom de la table doit correspondre à une table définie dans le data-template.yml de votre application. Le SDK achemine automatiquement les données vers la bonne base de données du projet.

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

appendToTable / append_to_table

Ajoute des données à une table de flotte via un appel de procédure distante plutôt que via pub/sub. Utilisez ceci lorsque vous avez besoin de confirmation que les données ont été persistées.

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

publishRowsToTable / publish_rows_to_table

Publie plusieurs lignes dans un seul message (insertion en masse) dans une table de flotte. La plateforme insère l’intégralité du lot de manière atomique (tout ou rien) en une seule opération. Utilisez ceci pour les données à haute fréquence, où un aller-retour par ligne serait trop coûteux. Comme publishToTable, ceci fonctionne en mode « fire-and-forget » — l’accusé de réception confirme la livraison au routeur, et non l’insertion en base de données.

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

Le second argument est une liste non vide d’objets de lignes à insérer.

appendRowsToTable / append_rows_to_table

Ajoute plusieurs lignes via un seul appel de procédure distante (insertion en masse) dans une table de flotte. La plateforme insère l’intégralité du lot de manière atomique (tout ou rien) : si une ligne est invalide, le lot entier est rejeté et rien n’est persisté. Préférez ceci à publishRowsToTable / publish_rows_to_table lorsque vous avez besoin de connaître le résultat de l’insertion.

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

Signale une erreur applicative dans la table error-logs de votre flotte. Il s’agit d’un wrapper pratique au-dessus de publishToTable / appendToTable : il estampille la ligne avec source: "app", un niveau de gravité level et un horodatage, puis l’écrit comme n’importe quelle ligne de table normale. L’erreur arrive dans la même table error-logs que celle utilisée par les erreurs système de fleetdb (étiquetées source: "system"), elle est donc interrogeable avec getHistory, diffusable en flux avec subscribeToTable / subscribe_to_table, utilisable dans les modèles de tableaux et livrée en temps réel sur transformed.error-logs — sans déclencher la notification d’erreur système de la plateforme.

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

Paramètres :

ParamètreTypeDescription
errorstr / string ou exception / ErrorLe message d’erreur, ou une exception dont la trace/pile (ou le message) est enregistrée
levelstr / string, optionnelGravité : "error", "warn", "info" ou "debug". Par défaut "error"
appendbool / boolean, optionnelLorsque true, utilise la procédure distante d’ajout (renvoie le résultat de l’insertion). Par défaut false (publication en mode « fire-and-forget »)
tspstr / string, optionnelHorodatage ISO-8601 de remplacement. Par défaut, l’heure actuelle

En Python, les options sont des arguments nommés (report_error(error, level=..., append=..., tsp=...)) ; en JavaScript, elles sont passées via un objet d’options (reportError(error, { level, append, tsp })).

publish

Publie un message sur n’importe quel topic WAMP. Utilisez ceci pour la messagerie personnalisée ou les événements qui ne correspondent pas à une table de base de données.

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

Interroger les données historiques

getHistory

Récupère des données historiques depuis une table de flotte. Supporte le filtrage, les plages temporelles et la pagination.

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

Paramètres de requête :

ChampTypeDescription
limitint / numberNombre maximum de lignes à renvoyer (1–10 000, obligatoire)
offsetint / numberDécalage pour la pagination
timeRangedict / object{"start": "<ISO datetime>", "end": "<ISO datetime>"}
filterAndlist / arrayConditions de filtre AND, et/ou le marqueur latest (voir ci-dessous)
columnslist / arrayColonnes à renvoyer (optionnel). tsp, device_key et authid sont toujours inclus ; omettez ce champ pour obtenir toutes les colonnes

Opérateurs de filtre : =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT

Chaque filtre est un objet avec les clés column, operator et value.

Lire les valeurs actuelles. Une entrée {"latest": true} dans filterAnd n’est pas une condition de filtre mais un changement de mode : le backend de données ne renvoie que la ligne la plus récente de chaque entité, dérivée en SQL à partir de la clé d’entité que la table déclare avec maintainLatestFlagFor. Une table sans clé d’entité renvoie son unique ligne la plus récente.

Les autres conditions se combinent au marqueur comme on peut s’y attendre : les conditions portant sur les colonnes de la clé d’entité restreignent quelles entités sont renvoyées, tandis que toutes les autres conditions et timeRange s’appliquent aux lignes les plus récentes obtenues. Ainsi, combiner {"latest": true} avec un filtre sur deleted masque les entités supprimées au lieu de faire remonter leur ligne précédente.

Les versions précédentes d’IronFlock stockaient une colonne latest_flag physique. Elle n’existe plus — un filtre hérité latest_flag = true est toujours accepté et traité comme le marqueur, mais le nouveau code doit utiliser {"latest": true}. Le marqueur latest n’est pas disponible dans getSeriesHistory.

getSeriesHistory / get_series_history

Récupère des données de séries temporelles sous-échantillonnées d’une table de flotte : des colonnes numériques agrégées par intervalles de temps (par ex. des moyennes horaires). Idéal pour les graphiques couvrant de longues périodes. Disponible pour les tables (pas pour les 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"] })

Paramètres de requête :

ChampTypeDescription
metricslist / arrayColonnes numériques à sous-échantillonner
methodstr / stringAgrégation par intervalle : "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" ou "LAST"
limitint / numberNombre maximum d’intervalles (1–10 000)
timeRangelist / array[start, end] — chaînes ISO datetime ou nombres epoch-ms ; null = borne ouverte (obligatoire)
groupBylist / arrayColonnes selon lesquelles grouper la série (optionnel)
filterAndlist / arrayConditions de filtre AND (optionnel). Conditions de filtre uniquement — le marqueur latest n’est pas pris en charge ici ; utilisez getHistory pour lire les valeurs actuelles

S’abonner aux données

subscribeToTable / subscribe_to_table

S’abonne aux mises à jour en temps réel d’une table de flotte. Le gestionnaire est appelé chaque fois que de nouvelles données sont publiées dans la table. Les lignes écrites via le chemin d’insertion en masse (publishRowsToTable / appendRowsToTable) sont transmises à votre gestionnaire une à la fois, de sorte que le code du gestionnaire reste identique quelle que soit la manière dont les données ont été écrites.

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

subscribe

S’abonne à n’importe quel topic WAMP pour de la messagerie en temps réel personnalisée.

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

Accès aux données inter-apps

Lisez les données de flotte d’une autre app depuis votre propre app, au sein du même projet. L’app fournisseur doit déclarer votre app dans la section consumes: de son data-template.yml, et l’utilisateur du projet doit accorder l’accès. L’accès est en lecture seule : vous pouvez interroger l’historique et vous abonner en temps réel aux lignes des tables et des transforms que le fournisseur partage, mais vous ne pouvez pas y écrire. Les connexions aux apps consommées sont mises en cache par app et fermées automatiquement lorsque votre instance s’arrête.

Si votre app détient l’autorisation wildcard (consumes: [{ app: "*" }]), vous pouvez découvrir et ouvrir dynamiquement les fournisseurs avec listConsumableApps / list_consumable_apps et connectToAllApps / connect_to_all_apps (ci-dessous).

connectToApp / connect_to_app

Ouvre une connexion en lecture seule au backend de données d’une autre app et renvoie un handle. Le handle expose getHistory / get_history, subscribeToTable / subscribe_to_table et getSeriesHistory / get_series_history (tables uniquement) — les mêmes interrogations et abonnements que vous utilisez sur vos propres tables — ainsi que close et les catalogues partagés 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)

Paramètres :

ParamètreTypeDescription
app_name / appNamestr / stringNom de l’app fournisseur, tel que déclaré dans votre section consumes:
stagestr / string, optionnelStage du fournisseur : "dev" ou "prod". Par défaut, le stage de votre propre app
on_error / onErrorcallable, optionnelAppelé avec un CrossAppAccessError si l’accès est refusé après l’établissement de la connexion (par ex. si l’autorisation est révoquée ultérieurement)

Si l’accès est refusé ou mal utilisé, un CrossAppAccessError est levé (Python) / lancé (JavaScript) avec un champ code : NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, PRIVATE_TABLE ou NOT_AUTHORIZED.

En Python, stage et on_error sont des arguments nommés ; en JavaScript, ils sont passés via un objet d’options (connectToApp(appName, { stage, onError })).

listConsumableApps / list_consumable_apps

Liste chaque fournisseur non privé du projet — la primitive de découverte pour les apps qui détiennent l’autorisation de consommation wildcard (consumes: [{ app: "*" }] dans votre data-template.yml, accordée par l’utilisateur du projet). Elle effectue un seul appel et n’ouvre aucune connexion : affichez les catalogues renvoyés dans un sélecteur, puis appelez connectToApp / connect_to_app pour ceux que vous souhaitez — ou connectToAllApps / connect_to_all_apps pour tous les ouvrir en une seule fois.

Remarque : Déclarez l’autorisation dans le data-template.yml de votre app, et mettez le * entre guillemets — un * nu est un alias YAML et ne sera pas analysé :

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

Chaque entrée décrit un fournisseur :

ChampTypeDescription
appstr / stringNom de l’app fournisseur
provider_app_keyint / numberLa clé d’app du fournisseur
stagesdict / objectCatalogue par stage { dev?, prod? } ; un stage n’est présent que si le fournisseur dispose d’un backend de données pour celui-ci. Chaque catalogue contient les tables et transforms non privés qu’il partage

Lève (Python) / lance (JavaScript) un CrossAppAccessError avec code: NO_GRANT si votre app ne détient aucune autorisation wildcard.

connectToAllApps / connect_to_all_apps

Ouvre des handles en lecture seule vers chaque fournisseur non privé du projet en un seul appel (consommateurs wildcard uniquement). Énumère les fournisseurs via listConsumableApps / list_consumable_apps et ouvre chacun d’eux, en ignorant ceux qui n’ont pas de backend de données pour le stage demandé. Chaque handle est mis en cache sous la même clé que connectToApp / connect_to_app, de sorte qu’un connectToApp(name) ultérieur renvoie le handle déjà préchauffé. Les handles renvoyés sont fermés ensemble lorsque votre instance s’arrête.

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)

Paramètres :

ParamètreTypeDescription
stagestr / string, optionnelStage du fournisseur : "dev" ou "prod". Par défaut, le stage de votre propre app
continue_on_error / continueOnErrorbool / boolean, optionnelLorsque true (la valeur par défaut), un fournisseur qui échoue à s’ouvrir est signalé à on_error / onError et omis du résultat. Lorsque false, le premier échec est levé/lancé
on_error / onErrorcallable, optionnelAppelé avec chaque fournisseur qui n’a pas pu être ouvert (tant que continue_on_error / continueOnError vaut true), et avec un CrossAppAccessError si une connexion déjà établie est refusée ultérieurement (par ex. si l’autorisation est révoquée)

Renvoie les handles de fournisseurs ouverts avec succès (même type de handle que connectToApp / connect_to_app). Lève (Python) / lance (JavaScript) un CrossAppAccessError avec code: NO_GRANT si votre app ne détient aucune autorisation wildcard.

En Python, stage, on_error et continue_on_error sont des arguments nommés ; en JavaScript, ils sont passés via un objet d’options (connectToAllApps({ stage, onError, continueOnError })).

Stockage de fichiers managé

Chaque backend de données d’app dispose, à côté de ses tables, d’un stockage d’objets privé accessible via la propriété files. Utilisez-le pour des images, des PDF, des images de caméra, des blobs de firmware — tout ce qui n’a pas sa place dans une ligne de table. Aucune configuration n’est nécessaire : une app dépourvue de section files: dans son data-template obtient malgré tout un espace de noms nommé default.

L’idée clé est que stocker un objet vous renvoie une URL permanente que vous pouvez écrire directement dans une colonne de table, de sorte qu’un widget de tableau de bord puisse l’afficher sans travail supplémentaire :

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

Cette URL n’expire jamais, mais ce n’est pas un lien public : elle ne reste lisible que par un demandeur authentifié disposant de l’accès READ sur ce backend de données, et un proxy d’authentification le revérifie à chaque requête. Elle peut donc être stockée sans risque dans la base de données.

Espaces de noms

Un espace de noms est un préfixe de clé porteur de règles — rétention, règles de partage, types de contenu autorisés. Ce n’est pas un bucket distinct ; tous les espaces de noms d’une app vivent dans l’unique zone de stockage de cette app. N’en déclarez un que lorsqu’un ensemble d’objets a besoin de règles différentes ; sinon, restez dans default et organisez vos objets avec des chemins de clés comme 2026/03/part-1.jpg.

Déclarez des espaces de noms supplémentaires dans 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 }

Notez que le budget est déclaré une seule fois pour toute l’app, et non par espace de noms. Un espace de noms n’est qu’un préfixe de clé à l’intérieur de l’unique zone de stockage de l’app : il n’y a donc rien contre quoi un budget par préfixe pourrait être appliqué. maxObjectBytes, lui, est défini par espace de noms — il plafonne un objet unique, pas un total.

Chaque méthode ci-dessous accepte l’espace de noms comme argument optionnel et utilise default par défaut.

Stocker et lire des objets

# 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éthodeDescription
put(key, data, …)Stocke un objet (bytes en Python, Uint8Array en JavaScript). Renvoie les métadonnées de l’objet, dont son url
get(key, namespace?)Renvoie le contenu de l’objet
put_file(key, path, …) / get_to_file(key, path, …)Python uniquement. Stocke depuis un fichier local, ou écrit dans un fichier local. Transfère en flux sur le chemin des objets volumineux
delete(key, namespace?)Supprime un objet
copy(key, to, …)Copie un objet, éventuellement vers un autre espace de noms
move(key, to, …)Copie puis suppression. Non atomique — le service ne dispose d’aucun verbe move, une suppression échouée laisse donc les deux copies en place

JavaScript n’offre pas d’assistants de chemins de fichiers, car le paquet est livré en un seul build pour Node comme pour le navigateur — lisez et écrivez vous-même les fichiers locaux avec fs.

put accepte : content_type / contentType (le type MIME ; l’espace de noms peut restreindre ceux qui sont autorisés) et namespace. En Python, ce sont des arguments nommés ; en JavaScript, ils sont passés dans un objet d’options.

Lister et inspecter

MéthodeDescription
list(…)Une page d’objets. Renvoie objects, prefixes, is_truncated / isTruncated ainsi qu’un cursor à repasser pour la page suivante
iter(…) / iterate(…)Itérateur asynchrone sur chaque objet situé sous un préfixe, avec pagination automatique. Nommé iter en Python, iterate en JavaScript
stat(key, namespace?)Métadonnées d’un objet sans transférer son contenu
exists(key, namespace?)Indique si un objet existe
namespaces()Les espaces de noms que cette app peut utiliser
usage(…)Quelle quantité de stockage l’app utilise — voir ci-dessous
catalog()Espaces de noms, ainsi que les limites et quotas émis par le serveur. Mis en cache après le premier appel

Les objets sont décrits par les mêmes champs dans les deux SDK, selon le style de nommage propre à chaque langage : namespace, key, size, etag, content_type / contentType, last_modified / lastModified, checksum_sha256 / checksumSha256 et url.

Utilisation du stockage et quota

usage obtient sa réponse du magasin d’objets en un seul appel : les totaux sont donc exacts, et non additionnés par le 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}
ChampSignification
size_bytes / sizeBytesOctets actuellement stockés
object_count / objectCountNombre d’objets stockés
quota_bytes / quotaBytesLe budget appliqué. 0 signifie illimité
free_bytes / freeBytesOctets restants. -1 signifie illimité — indiquer 0 ici se lirait comme « plein »
per_namespace / perNamespaceOctets par espace de noms. Présent uniquement lorsque vous demandez le détail

La ventilation par espace de noms est désactivée par défaut, car le magasin ne sait pas y répondre directement : il comptabilise par zone de stockage, et un espace de noms n’est qu’un préfixe ; le SDK doit donc lister chaque espace de noms pour en additionner les tailles. Demandez-la quand vous en avez besoin, pas sur un chemin critique.

Deux quotas différents apparaissent, et il vaut la peine de ne pas les confondre. catalog() renvoie les deux :

ChampSignification
quota_bytes / quotaBytesCe qui est réellement appliqué, lu depuis le magasin d’objets — le réglage de l’utilisateur du projet
suggested_quota_bytes / suggestedQuotaBytesCe que le data template de l’app a demandé. 0 s’il n’a rien demandé

Ils diffèrent dès qu’un utilisateur a augmenté ou réduit le budget de l’app, et c’est précisément pourquoi la valeur appliquée est lue depuis le magasin plutôt que depuis le template — redéployer l’app ne doit pas réinitialiser silencieusement le choix d’un utilisateur. Une interface peut afficher les deux (« l’app suggère X, vous avez réglé Y »). L’application du quota s’appuie toujours sur la première valeur.

Partager des objets

Il existe deux types de liens, et la différence a son importance :

MéthodeDurée de vieQui peut le lire
url(key, …)PermanenteUniquement un demandeur authentifié disposant de READ sur ce backend de données — revérifié à chaque requête. Peut être stockée sans risque dans une colonne de table
share_url / shareUrlExpirante (15 min par défaut, plafonnée par le serveur)Quiconque détient le lien. Rien ne revérifie l’autorisation au moment de son utilisation

share_url / shareUrl est une capacité au porteur : remettez-la à une personne qui a besoin d’un accès temporaire, et ne la stockez pas dans la base de données. Utilisez url pour tout ce qu’affiche un tableau de bord.

url renvoie None / undefined lorsque le déploiement n’a pas de frontal HTTP (par exemple une appliance en HTTP simple) — c’est le signal qu’il faut se rabattre sur get. Passer l’etag d’un objet comme argument version permet aux navigateurs de mettre la réponse en cache de façon immuable.

upload_url / uploadUrl génère une URL expirante qui accepte un envoi direct, et renvoie url, method, headers et expires_in / expiresIn. Envoyez exactement les en-têtes qu’elle renvoie, sinon la signature ne sera pas vérifiée.

Objets volumineux

Le SDK choisit le transport en fonction de la taille, automatiquement — il n’y a rien à configurer :

Taille de l’objetComment il transite
Jusqu’à la limite inline (actuellement 6 Mio)Un seul appel via le routeur de messages
Au-delàDirectement vers le stockage d’objets en HTTPS, en contournant le routeur

La limite exacte est indiquée à l’exécution par le serveur sous la forme inline_max_bytes / inlineMaxBytes dans catalog() ; elle peut donc être relevée sans nouvelle version du SDK.

Deux plafonds subsistent, et tous deux signalent TOO_LARGE avec une raison précisant lequel a été atteint :

  • 5 Gio — la limite d’envoi en une seule fois du stockage d’objets. L’envoi multipart n’est pas encore implémenté.
  • La limite inline, lorsqu’il n’existe pas de point de terminaison direct — une appliance isolée du réseau ne peut tout simplement pas transférer un objet volumineux. Aucun nouvel essai ni découpage plus fin n’y changera quoi que ce soit, et le message le précise.

Le chemin direct exige que l’appareil puisse joindre l’hôte du stockage d’objets, et pas seulement le routeur. Deux défaillances courantes sur le terrain reçoivent leurs propres codes au lieu de ressembler à des problèmes d’autorisation : PRESIGN_UNREACHABLE (un proxy qui n’autorise que le routeur) et CLOCK_SKEW (le stockage d’objets rejette les requêtes décalées de plus de 15 minutes — vérifiez NTP sur l’appareil).

Erreurs de stockage de fichiers

Chaque opération sur les fichiers lève (Python) / lance (JavaScript) une FileStoreError portant un code stable et une reason lisible par un humain. Faites vos branchements sur code, jamais sur 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
CodeSignification
NOT_AUTHORIZEDL’appelant n’a pas le droit d’effectuer cette opération
NO_SUCH_NAMESPACEL’espace de noms n’est pas déclaré dans le data-template
NO_SUCH_OBJECTLa clé n’existe pas
TOO_LARGEDépasse la limite de transfert en un seul appel
OBJECT_TOO_LARGEDépasse le maxObjectBytes propre à l’espace de noms
QUOTA_EXCEEDEDLe filestore est plein
CONTENT_TYPE_NOT_ALLOWEDL’espace de noms restreint les contentTypes
NOT_SUPPORTEDLe backend ne sait pas faire cela
NOT_AVAILABLECe déploiement n’a pas de service de fichiers
PRESIGN_UNREACHABLELe stockage d’objets n’est pas joignable directement (un proxy ?)
CLOCK_SKEWL’horloge de l’appareil est trop décalée
INTERNALTout le reste

Un serveur plus récent peut introduire des codes que cette version du SDK ne connaît pas. Ils sont transmis tels quels dans code plutôt que ramenés à une valeur générique : traitez donc une valeur non reconnue comme un échec générique.

En Python, FileStoreError est importée depuis ironflock.filestore ; en JavaScript, elle est exportée depuis la racine du paquet (import { FileStoreError } from "ironflock").

Communication inter-appareils

registerDeviceFunction / register_device_function

Enregistre une procédure que d’autres appareils du même projet peuvent appeler. Le SDK ajoute automatiquement un espace de noms à la procédure pour l’appareil actuel.

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

register() est un alias de register_device_function().

callDeviceFunction / call_device_function

Appelle une procédure enregistrée par un autre appareil. Le SDK assemble automatiquement le topic WAMP complet en utilisant la clé de l’appareil cible.

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

call

Appelle une procédure distante en utilisant une URI WAMP complète. Utilisez ceci pour des appels directs lorsque vous connaissez le topic exact.

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

Métadonnées de l’appareil

setDeviceLocation / set_device_location

Met à jour la localisation GPS de l’appareil dans la plateforme. Les modifications sont reflétées en temps réel sur les cartes IronFlock.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
ParamètrePlage
long-180 à 180
lat-90 à 90

L’historique de localisation n’est pas stocké. Pour suivre la localisation dans le temps, créez une table dédiée et utilisez publish_to_table / publishToTable.

getRemoteAccessUrlForPort

Renvoie l’URL publique d’accès distant pour un port donné sur l’appareil.

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

Propriétés & cycle de vie de la connexion

PropriétéTypeDescription
is_connectedboolIndique si la connexion à la plateforme est active
connectionCrossbarConnectionL’instance de connexion sous-jacente (usage avancé)
MéthodeDescription
run()Démarre la connexion et exécute mainFunc (bloquant)
await start()Démarre la connexion de manière asynchrone
await stop()Arrête la connexion et annule les tâches en cours
await run_async()Démarre et maintient la connexion en cours d’exécution de manière asynchrone

Gestion des erreurs

Chaque méthode du SDK échoue de manière explicite : en cas d’arguments invalides, de connexion perdue ou de rejet par la plateforme, elle lève une exception (Python) ou rejette (JavaScript) avec un message indiquant l’opération, le topic et la raison. Rien n’est ignoré silencieusement : encadrez donc d’un bloc try les appels auxquels votre code doit survivre.

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

En JavaScript, les échecs provenant de la plateforme sont des instances de WampError — une sous-classe normale d’Error qui transporte en plus l’URI d’erreur WAMP dans error et la charge utile de l’erreur dans args / kwargs. Tout le reste (paramètres invalides, absence de connexion) est une Error ordinaire.

Migration : les anciennes versions du SDK journalisaient un message et renvoyaient None / null lorsqu’un appel échouait. Elles lèvent désormais une erreur : un code de la forme if result is None: ne détecte donc plus les échecs — utilisez try / except (ou try / catch).

Utilisation dans le navigateur (JavaScript uniquement)

Le SDK JavaScript fonctionne dans les navigateurs modernes. Comme les navigateurs n’ont pas de variables d’environnement, passez toute la configuration via le constructeur :

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

Utilisez IronFlock.fromServer() pour récupérer la configuration depuis votre backend au lieu de coder en dur les identifiants :

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

Votre point de terminaison backend doit renvoyer un objet JSON contenant les options de connexion (serialNumber, deviceKey, appName, swarmKey, appKey, env).

Enregistrement de fonctions pour agents IA

Le SDK peut enregistrer des fonctions appelables par des agents IA. Enregistrez une procédure et référencez son topic dans votre 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)

L’agent IA peut alors appeler cette fonction lorsqu’un utilisateur pose une question nécessitant des données de capteur en temps réel.

Pour relier le topic WAMP enregistré à un agent IA, référencez-le dans le .ironflock/ai-template.yml de votre app :

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

La valeur topic (sensors.get_latest) doit correspondre au nom passé à register_device_function / registerDeviceFunction dans votre code edge. IronFlock achemine automatiquement l’appel vers l’appareil où la fonction est enregistrée.

Pour la référence complète de ai-template.yml, consultez Définir des agents et des outils.

Variables d’environnement

Ces variables sont définies automatiquement par l’environnement d’exécution IronFlock dans les conteneurs d’applications :

VariableDescription
DEVICE_NAMENom d’affichage de l’appareil
DEVICE_SERIAL_NUMBERIdentifiant unique et immuable de l’appareil
DEVICE_KEYClé de l’appareil pour l’authentification
SWARM_KEYIdentifiant du projet
APP_KEYIdentifiant de l’application
APP_NAMENom de l’application
ENVEnvironnement : 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