IoT-Profile verwalten
Um IoT-Profile zu verwalten, wechseln Sie zunächst zur Geräteverwaltung, indem Sie im Benutzermenü oben rechts auf Geräte klicken. Wechseln Sie anschließend auf die Registerkarte IoT-Profile.
In der Seitenleiste links werden nun alle IoT-Profile aufgelistet, die Sie bereits eingerichtet haben. Um ein neues Profil zu erstellen, klicken Sie oberhalb dieser Seitenleiste auf Profil hinzufügen.
In der Hauptansicht können Sie nun das IoT-Profil konfigurieren.
Bemerkung
Wenn Sie ein IoT-Profil erstellen oder bearbeiten, hat dies zunächst keine Auswirkung auf existierende Geräte. Erst wenn Sie das Profil auf ein Gerät anwenden (siehe IoT-Profile anwenden), werden die Datenpunkte auf dem Gerät angelegt und der Dekodierer registriert.
Profilname
Geben Sie dem IoT-Profil einen aussagekräftigen Namen, beispielsweise den Modellnamen des IoT-Geräts oder dessen Anwendungszweck.
Gerätetyp
Wählen Sie hier den Typ des IoT-Geräts aus, für den dieses Profil bestimmt ist.
Datenpunkte
In dieser Tabelle können Sie die Datenpunkte konfigurieren, die automatisch angelegt werden sollen, wenn das IoT-Profil einem Gerät zugewiesen wird.
- Schlüssel
Ein Schlüssel, der in der gesamten Tabelle eindeutig ist, und mit dem der entsprechende Datenpunkt im Dekodierer identifiziert werden kann. Der Schlüssel kann sehr kurz sein, solange er eindeutig ist und den Datenpunkt verständlich identifiziert, beispielsweise “temperatur” oder “zaehler01”.
Bemerkung
Um einen Batterie-Watchdog auf dem Gerät einrichten zu können, der bei niedrigem Batteriestand alarmiert, müssen Sie einen der Datenpunkte mit dem Schlüssel
battery
kennzeichnen. Dieser Datenpunkt darf nur Werte von 0 bis 100 annehmen. Vergessen Sie nicht, den aktuellen Batteriestand in Ihrem Dekodierer auszulesen und diesem Datenpunkt zuzuweisen. Nachdem Sie das Profil einem Gerät zugewiesen haben, können Sie den Batteriealarm auf der Konfigurationsseite des betreffenden Geräts im Abschnitt Alarmierung einstellen.Bemerkung
ThermoValve-Geräten kann ein Temperatursensor (z. B. ein Wisely oder ein Mein LoRa-Gerät) zugewiesen werden, der die Raumtemperatur für den Thermostat zur Verfügung stellt. Wenn Sie ein IoT-Profil für ein eigenes LoRa-Gerät erstellen, welches Sie zusammen mit einem ThermoValve-Gerät verwenden möchten, muss der Schlüssel des Temperaturdatenpunkts im Profil auf
temperature
gesetzt werden.- Name
Der Name des Datenpunkts, wie er später in der Datenpunktliste, auf Diagrammen oder Anlagebildern angezeigt werden soll.
- Systemname
Der Systemname des Datenpunkts, der ihn im System eindeutig identifiziert.
Um sicherzustellen, dass Datenpunkte einen eindeutigen Systemnamen erhalten, selbst wenn Sie das IoT-Profil auf mehrere Geräte anwenden, können Sie einen der folgenden Platzhalter verwenden:
%DEVID%
wird automatisch mit der Geräteidentifikation (in der Regel die EUI des Geräts) ersetzt.%DEVNAME%
wird automatisch mit dem Gerätenamen ersetzt. Da der Gerätename lokalisierbar ist, der Systemname hingegen nicht, wird das System den Gerätenamen in der Sprache des Benutzers verwenden, der das IoT-Profil dem Datenpunkt zuweist.
- Beschreibung
Eine zusätzliche Beschreibung des Datenpunkts, beispielsweise dessen Verwendungszeck oder genaue Standort- oder Bauteilbezeichnung.
- Typ
Der Datentyp des Datenpunkts.
- Einheit
Die Einheit, in der die Messwertaufzeichnung auf diesem Datenpunkt erfolgt.
- Auf der öffentlichen Web-App verfügbar machen
Wenn diese Option aktiviert ist, wird der Datenpunkt in der öffentlichen Web-App angezeigt, die durch Scannen des QR-Codes auf dem entsprechenden Gerät geöffnet werden kann. Beachten Sie, dass der Datenpunkt und dessen letzte Messwerte in der öffentlichen Web-App ohne Login zugänglich sind. Durch Deaktivieren der Option sind der Datenpunkt und seine Messwerte nur noch auf der normalen Avelon-Benutzeroberfläche über ein gültiges Login zugänglich.
Dekodierer
In diesem Eingabefeld definieren Sie den Dekodieralgorithmus, mit dem der Datenstrom des IoT-Geräts in die Messwerte oder Alarme der einzelnen Datenpunkte umgewandelt wird.
Der Algorithmus muss als JavaScript-Funktion decode
geschrieben werden, die die folgenden Parameter entgegennimmt:
function decode(bytes, port, date) {
var decoded = [];
// Place your code here and push the results to 'decoded'.
return decoded;
}
- bytes
Der Datenstrom als Bytearray.
- port
Die Portnummer, auf der der Datenstrom übertragen wird. Dieser kann bei einigen Geräten beispielsweise zur Identifikation des Meldungstyps verwendet werden.
- date
Ein Datumsobjekt des Zeitpunkts, an dem die Meldung vom IoT-Gerät versandt wurde. Falls diese Information nicht zur Verfügung steht, wird stattdessen das Empfangsdatum der Meldung übergeben.
Grundsätzlich muss die Funktion decode
ein Array mit Objekten der folgenden Form zurückgeben:
Messwerte
{
type: 'measurement',
key: 'data_point_key_xyz',
datetime: 1539093694, // milliseconds since Jan 1, 1970 or Date.prototype.toISOString()
value: 23.5
}
- type
Muss bei normalen Messwerten auf
'measurement'
gesetzt werden.- key
Der Schlüssel des entsprechenden Datenpunkts, wie er oben in der Tabelle Datenpunkte in der Spalte Schlüssel definiert wurde.
- datetime
Datum und Uhrzeit des Messwertes. Kann beispielsweise auf
Date.now()
gesetzt oder aus dem Funktionsparameter date abgeleitet werden. Gültige Werte sind entweder die Anzahl Millisekunden seit 1. Januar 1970 (Date.prototype.getTime()
) oder eine Zeichenfolge gemäss ISO 8601 (Date.prototype.toISOString()
).- value
Der effektiv aufgezeichnete Messwert als Ganzzahl, Fließkommazahl oder Wahrheitswert.
Alarme
{
type: 'alarm',
key: 'data_point_key_xyz',
datetime: 1539093694, // milliseconds since Jan 1, 1970 or Date.prototype.toISOString()
value: 23.5,
toState: 'offNormal', // or 'normal' if alarm is gone
message: 'The alarm message'
}
- type
Muss bei Alarmen auf
'alarm'
gesetzt werden.- key
Der Schlüssel des entsprechenden Datenpunkts, wie er oben in der Tabelle Datenpunkte in der Spalte Schlüssel definiert wurde.
- datetime
Datum und Uhrzeit des Alarms. Kann beispielsweise auf
Date.now()
gesetzt oder aus dem Funktionsparameter date abgeleitet werden. Gültige Werte sind entweder die Anzahl Millisekunden seit 1. Januar 1970 (Date.prototype.getTime()
) oder eine Zeichenfolge gemäss ISO 8601 (Date.prototype.toISOString()
).- value
Der anstehende Alarmwert.
- toState
'offNormal'
im Alarmfall oder'normal'
im Fall einer Gutmeldung. Falls der Alarm sich nicht auf den aktuellen Messwert, sondern auf ein anderes Problem bezieht (z. B. dass der Wert nicht richtig ausgelesen werden konnte), können Sie auch den Status'fault'
verwenden.- message
Der Alarmtext, wie er in einem Alarmticket und in eventuellen Benachrichtigungen angezeigt werden soll.
Hilfsfunktionen
In diesem Abschnitt finden Sie einige Hilfsfunktionen, die die Manipulation von Bytearrays und das Erstellen der Ausgabeobjekte vereinfachen können.
Ausgabeobjekt für Messwerte erstellen
// Helper function to generate a measurement object.
function createMeasurement(key, datetime, value) {
return {
type: 'measurement',
key: key,
datetime: datetime,
value: value
};
}
Messwertobjekt erstellen:
decoded.push(createMeasurement('data_point_key_xyz', 1539093694, 24.5));
Ausgabeobjekt für Alarme erstellen
// Helper function to generate an alarm object.
function createAlarm(key, datetime, value, isGone, message) {
return {
type: 'alarm',
key: key,
datetime: datetime,
value: value,
toState: isGone ? 'normal' : 'offNormal',
message: message
};
}
Alarmobjekt erstellen:
decoded.push(createAlarm('data_point_key_xyz', 1539093694, 24.5, false, 'Alarm'));
Float32 parsen
// Helper function to generate a float32 from an array of bytes at the given offset.
function getFloat32(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(4);
var view = new DataView(buffer);
for (var i = 0; i < 4; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getFloat32(0, littleEndian));
}
Int16 parsen
// Helper function to generate an int16 from an array of bytes at the given offset.
function getInt16(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(2);
var view = new DataView(buffer);
for (var i = 0; i < 2; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getInt16(0, littleEndian));
}
Uint16 parsen
// Helper function to generate a uint16 from an array of bytes at the given offset.
function getUint16(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(2);
var view = new DataView(buffer);
for (var i = 0; i < 2; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getUint16(0, littleEndian));
}
Uint8 parsen
// Helper function to generate a uint8 from an array of bytes at the given offset.
function getUint8(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(1);
var view = new DataView(buffer);
view.setUint8(0, bytes[offset]);
return Number(view.getUint8(0));
}
Einzelnes Bit parsen
// Helper function to extract a single bit from a byte in an array of bytes.
// Bit index 0 is the least significant bit, 7 is the most significant bit.
function getBit(bytes, offset, bitIndex) {
return (bytes[offset] & (1 << bitIndex)) >> bitIndex;
}
Anzahl der Dezimalstellen einer Zahl festlegen
// Helper function to set the number of decimals of a number.
function getFixed(number, decimals) {
return Number(number.toFixed(decimals));
}
Payload testen
Um den Dekodieralgorithmus zu testen, geben Sie im Feld Payload bitte die erwartete Nutzlast im Hexadezimalformat ein (z. B. 007B42F600003039
) und klicken Sie anschließend auf die Schaltfläche Test.
Falls keine Fehler aufgetreten sind, erscheint das dekodierte Ausgabeobjekt im Feld Resultat. Im Fehlerfall erscheinen Toast-Benachrichtigungen mit einem kurzen Beschrieb des Problems und allenfalls der fehlerhaften Zeile im Quellcode.
Vollständiges Beispiel
function decode(bytes, port, date) {
// Decode an uplink message from a buffer (array) of bytes to an object.
// Helper function to generate a measurement object.
function createMeasurement(key, datetime, value) {
return {
type: 'measurement',
key: key,
datetime: datetime,
value: value
};
}
// Helper function to generate an alarm object.
function createAlarm(key, datetime, value, isGone, message) {
return {
type: 'alarm',
key: key,
datetime: datetime,
value: value,
toState: isGone ? 'normal' : 'offNormal',
message: message
};
}
// Helper function to generate a float32 from an array of bytes at the given offset.
function getFloat32(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(4);
var view = new DataView(buffer);
for (var i = 0; i < 4; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getFloat32(0, littleEndian));
}
// Helper function to generate an int16 from an array of bytes at the given offset.
function getInt16(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(2);
var view = new DataView(buffer);
for (var i = 0; i < 2; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getInt16(0, littleEndian));
}
// Helper function to generate a uint16 from an array of bytes at the given offset.
function getUint16(bytes, offset, littleEndian) {
var buffer = new ArrayBuffer(2);
var view = new DataView(buffer);
for (var i = 0; i < 2; i++) {
view.setUint8(i, bytes[offset + i]);
}
return Number(view.getUint16(0, littleEndian));
}
// Helper function to generate a uint8 from an array of bytes at the given offset.
function getUint8(bytes, offset) {
var buffer = new ArrayBuffer(1);
var view = new DataView(buffer);
view.setUint8(0, bytes[offset]);
return Number(view.getUint8(0));
}
// Helper function to extract a single bit from a byte in an array of bytes.
// Bit index 0 is the least significant bit, 7 is the most significant bit.
function getBit(bytes, offset, bitIndex) {
return (bytes[offset] & (1 << bitIndex)) >> bitIndex;
}
// Helper function to set the number of decimals of a number.
function getFixed(number, decimals) {
return Number(number.toFixed(decimals));
}
var decoded = [];
var now = Date.now();
// Add the decoded values to an array.
// Example:
// Decode byte 1 (at index 0) as a battery capacity with a value between 0 and 100.
decoded.push(createMeasurement('battery', now, getFixed(getUint8(bytes, 0) * 100 / 255, 2)));
// Decode bytes 2 to 3 (starting at index 1) as an unsigned 16-bit integer
// for the data point with key 'key_1'.
decoded.push(createMeasurement('key_1', now, getUint16(bytes, 1)));
// Decode bytes 4 to 7 (starting at index 3) as a 32-bit float
// for the data point with key 'key_2'.
decoded.push(createMeasurement('key_2', now, getFloat32(bytes, 3)));
// Decode bytes 8 to 9 (starting at index 7) as a signed 16-bit integer
// for the data point with key 'key_3'.
decoded.push(createMeasurement('key_3', now, getInt16(bytes, 7)));
// Decode the least significant bit in byte 10 as a binary value (0 or 1).
// for the data point with key 'key_4'.
decoded.push(createMeasurement('key_4', now, getBit(bytes, 9, 0)));
// Decode byte 11 (at index 10) as a binary alarm value (0 or 1)
// for the data point with key 'key_1' and provide a specific alarm message.
decoded.push(createAlarm('key_1', now, getBit(bytes, 10, 0), false, 'Add alarm text here'));
return decoded;
}