Diese Routen verwalten den Asset-Baum von Hermetry Twin: Standorte, Gebäude, Etagen, Räume, Linien, Maschinen und Ihre eigenen Arten, wo jedes Gerät platziert ist, sowie die Beziehungen zwischen Assets und Geräten. Die Konzepte sind unter Asset-Hierarchie beschrieben. Pfade liegen unter https://<your host>/tenant/<tenantId>.
Routen
| Methode | Pfad | Berechtigung | Funktion |
|---|---|---|---|
| GET | /api/iot/assets | iot_asset.assets.read | Auflistung als flache Seite, als Baum, als Tabelle „Standorte & Assets“ oder deren Zusammenfassung |
| POST | /api/iot/assets | iot_asset.assets.manage | Asset anlegen |
| GET | /api/iot/assets/{assetId} | iot_asset.assets.read | Ein Asset mit seinen Vorfahren und Zählern |
| PATCH | /api/iot/assets/{assetId} | iot_asset.assets.manage | Asset ändern |
| DELETE | /api/iot/assets/{assetId} | iot_asset.assets.manage | Asset löschen |
| POST | /api/iot/assets/{assetId}/move | iot_asset.assets.manage | Unter einen anderen übergeordneten Eintrag verschieben |
| GET | /api/iot/assets/{assetId}/devices | iot_asset.assets.read | Darauf platzierte Geräte (und darunter) |
| POST | /api/iot/assets/{assetId}/devices | iot_asset.relations.manage | Bis zu 100 Geräte hier platzieren |
| GET | /api/iot/devices/{deviceId}/placement | iot_asset.assets.read | Wo ein Gerät steht |
| PUT | /api/iot/devices/{deviceId}/placement | iot_asset.relations.manage | Ein Gerät platzieren oder dessen Platzierung entfernen |
| GET | /api/iot/relations | iot_asset.assets.read | Beziehungen auflisten |
| POST | /api/iot/relations | iot_asset.relations.manage | Beziehung anlegen |
| DELETE | /api/iot/relations/{relationId} | iot_asset.relations.manage | Beziehung löschen |
Standardrollen: Lesen ist eine Benutzerberechtigung; Assets, Platzierungen und Beziehungen zu ändern ist eine Administratorberechtigung. Jede Änderung wird im Audit-Log protokolliert (die geänderten Feldnamen, niemals die Werte).
Das Asset
| Feld | Regeln |
|---|---|
name | erforderlich, 1 bis 255 Zeichen |
kind | erforderlich; Buchstaben, Ziffern und _, 1 bis 24, wird in Großbuchstaben gespeichert. Empfohlen: SITE, BUILDING, FLOOR, ROOM, AREA, LINE, MACHINE, OTHER; jeder andere Wert ist erlaubt |
code | Buchstaben, Ziffern, ., _, -; 1 bis 32; wird in Großbuchstaben gespeichert; eindeutig unter Ihren aktiven Assets. Wird es weggelassen, wird es aus dem Namen gebildet. |
parentId | nur beim Anlegen; null oder fehlend für ein Asset auf oberster Ebene. Später ändern Sie es mit move. |
description | bis zu 1.000 Zeichen |
lat, lng | −90 bis 90, −180 bis 180 |
timezone | ein IANA-Name wie Europe/Istanbul |
addressJson | bis zu 12 Textfelder |
attributes | bis zu 100 Schlüssel und 16 KiB; Schlüssel beginnen mit einem Buchstaben, danach Buchstaben, Ziffern, ., _, - (bis zu 64) |
defaultDashboardId | ein Dashboard, das der Schaltfläche Dashboard öffnen des Assets öffnet |
Schreibgeschützte Felder in Antworten: iotAssetId, path (die Codes von oben, zum Beispiel /IST/BLD-A/GF), depth, createdAt, updatedAt.
POST/tenant/{tenantId}/api/iot/assets
Asset anlegen. Unbekannte Felder werden abgelehnt.
Anfragetext application/json
{
"name": "Compressor room",
"kind": "ROOM",
"code": "CR1",
"parentId": "e5f6…",
"timezone": "Europe/Istanbul",
"attributes": {
"area_m2": 42
}
}
Beispiel curl
curl -X POST 'https://<your-host>/tenant/{tenantId}/api/iot/assets' \
-H 'Content-Type: application/json' \
-d '{"name":"Compressor room","kind":"ROOM","code":"CR1","parentId":"e5f6…","timezone":"Europe/Istanbul","attributes":{"area_m2":42}}'
Antworten
201Created
{
"asset": {
"iotAssetId": "77aa…",
"parentId": "e5f6…",
"path": "/IST/BLD-A/GF/CR1",
"depth": 4,
"code": "CR1",
"name": "Compressor room",
"kind": "ROOM",
"timezone": "Europe/Istanbul",
"attributes": {
"area_m2": 42
},
"…": "…"
}
}
409Conflict
{
"message": "Another asset already uses this code.",
"code": "CONFLICT",
"details": {
"reason": "code_taken"
}
}
PATCH nimmt dieselben Felder außer parentId entgegen. Ein neuer code schreibt in demselben Schritt die Pfade aller darunterliegenden Assets neu.
GET /api/iot/assets/{assetId} ergänzt ancestors (oberste zuerst, jeweils mit id, code, name, kind), effectiveTimezone (die eigene, sonst die des nächsten Vorfahren), childCount und deviceCount.
Liste und Baum
GET /api/iot/assets mit view:
view | Antwort | Parameter |
|---|---|---|
list (Standard) | { assets, total, page, pageSize }, eine flache Seite | parentId (eine UUID oder root), subtreeOf, kind, search, includeDeleted, page ab 1, pageSize bis 200 (Standard 50) |
tree | Der verschachtelte Baum | rootId, depth (Standard 6, höchstens 12); höchstens 5.000 Assets |
table | Eine Seite der Tabelle Standorte & Assets: jedes Asset mit seinem Ort, seinen Untergeordneten, Geräten und aktiven Alarmen | search, kind, site (ein Teilbaum), parentId, alarmed=true, sort (name, kind, location, children, devices, alarms mit :asc oder :desc), page, pageSize |
summary | Die Zähler und die Standorte mit ihren Alarmen je Schweregrad |
Alarmwerte sind nur enthalten, wenn der Aufrufer Alarme lesen darf.
Verschieben
POST /api/iot/assets/{assetId}/move
{ "parentId": "c3d4…" }
null macht es zu einem Asset auf oberster Ebene. Alles darunter wird mit verschoben. Ein Verschieben unter sich selbst oder einen seiner Nachfahren wird abgelehnt: „An asset cannot be moved under itself or one of its own descendants.“ (400, cycle). Ein Pfad darf höchstens 1.024 Zeichen lang sein (too_deep).
Löschen
Ein Asset mit untergeordneten Assets wird abgelehnt: „This asset still has child assets: move or delete them first.“ (409, has_children). Meldet ein anderes Modul, dass es das Asset noch verwendet, wird das Löschen mit 409 unter Nennung dieses Moduls abgelehnt. Das Löschen eines Assets beendet dessen Gerätezuordnungen und Beziehungen; die Geräte selbst bleiben erhalten.
Geräte platzieren
POST /api/iot/assets/{assetId}/devices
{ "deviceIds": ["6f1c…", "8b2d…"] }
Jedes Gerät hat höchstens einen Ort: Wird es hier platziert, wird es von seinem bisherigen Ort verschoben. Die Antwort { "changes": [...] } listet auf, was verschoben wurde. Jeweils ein Gerät:
PUT /api/iot/devices/{deviceId}/placement
{ "iotAssetId": "77aa…" } // or null to remove the placement
Sowohl GET als auch PUT von .../placement antworten mit { "placement": { "iotAssetId": "77aa…", "path": [ { "iotAssetId": "…", "code": "IST", "name": "Istanbul plant", "kind": "SITE" }, … ] } }, oberste zuerst. Ein Gerät ohne Ort hat iotAssetId: null.
GET /api/iot/assets/{assetId}/devices listet die auf dem Asset platzierten Geräte und standardmäßig auch die darunter (recursive=false nur für das Asset allein): search, online=true|false, sort=name|connection:asc|desc, page ab 1, pageSize bis 200. Die Antwort lautet { items, total, page, pageSize }.
Beziehungen
Eine Beziehung verbindet zwei Enden, jeweils ein ASSET oder ein DEVICE, mit einem Typ: CONTAINS, MANAGES, FEEDS, MONITORS oder eigene Typen (Buchstaben, Ziffern, _, bis zu 32, in Großbuchstaben gespeichert).
POST/tenant/{tenantId}/api/iot/relations
Beziehung anlegen. additionalInfo enthält bis zu 20 Schlüssel und 4 KiB.
Anfragetext application/json
{
"fromType": "DEVICE",
"fromId": "8b2d…",
"toType": "DEVICE",
"toId": "6f1c…",
"type": "FEEDS",
"additionalInfo": {
"line": "DN150"
}
}
Beispiel curl
curl -X POST 'https://<your-host>/tenant/{tenantId}/api/iot/relations' \
-H 'Content-Type: application/json' \
-d '{"fromType":"DEVICE","fromId":"8b2d…","toType":"DEVICE","toId":"6f1c…","type":"FEEDS","additionalInfo":{"line":"DN150"}}'
Antworten
201Created
{
"relation": {
"relationId": "…",
"fromType": "DEVICE",
"fromId": "8b2d…",
"toType": "DEVICE",
"toId": "6f1c…",
"type": "FEEDS",
"…": "…"
}
}
409Conflict
{
"message": "This relation already exists.",
"code": "CONFLICT",
"details": {
"reason": "relation_exists"
}
}
Regeln:
CONTAINSvon einem Asset zu einem Gerät ist die Platzierung des Geräts (verwenden Sie dafür die Platzierungsrouten).CONTAINSin jeder anderen Form wird abgelehnt (placement_shape), und ein Asset innerhalb eines anderen Assets wird durch Verschieben festgelegt, nicht durch eine Beziehung (parentage_is_move).- Eine Beziehung braucht zwei verschiedene Enden (
self_relation). - Ein fehlendes Ende ergibt
404(end_not_found).
GET /api/iot/relations?entityType=ASSET&entityId={assetId} listet die Beziehungen eines Endes in beide Richtungen auf; alternativ filtern Sie nach fromType, fromId, toType, toId, type. limit 1 bis 500 (Standard 200). Die Antwort lautet { "relations": [...] }.
Geräte für eine Auswahlliste suchen
GET /api/iot/devices/search?q=pump&limit=20&near=iot_asset:{assetId} liefert { "devices": [{ "id", "name", "code", "hint" }] }: höchstens 20 Geräte nach Name, wobei die des angegebenen Geräts oder Assets zuerst erscheinen, mit hint, der angibt, wo jedes platziert ist. Dafür werden sowohl iot_asset.assets.read als auch iot_device.devices.read benötigt.

