diff --git a/VENDORS/Decentlab/DL-5TM/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/ChirpStack/uplink/converter.json index 1ac036b1..61d57196 100644 --- a/VENDORS/Decentlab/DL-5TM/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/converter.json index ec5e8e21..77ca3d81 100644 --- a/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-5TM/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/ThingPark/uplink/converter.json index f029da68..84ad0ec8 100644 --- a/VENDORS/Decentlab/DL-5TM/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-5TM/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/ThingParkEnterprise/uplink/converter.json index 48e4d432..9a01af5c 100644 --- a/VENDORS/Decentlab/DL-5TM/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-5TM/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/ThingsStackCommunity/uplink/converter.json index b32f1ef1..0b764908 100644 --- a/VENDORS/Decentlab/DL-5TM/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-5TM/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-5TM/ThingsStackIndustries/uplink/converter.json index 4af3f48b..160b8177 100644 --- a/VENDORS/Decentlab/DL-5TM/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-5TM/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-5TM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/legacy-soil-moisture-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return x[0] / 50; }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return 0.0000043 * Math.pow(x[0]/50, 3) - 0.00055 * Math.pow(x[0]/50, 2) + 0.0292 * (x[0]/50) - 0.053; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 400) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/ChirpStack/uplink/converter.json index d1574d7c..341bf6d6 100644 --- a/VENDORS/Decentlab/DL-ALB/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/converter.json index b400e735..b29416d4 100644 --- a/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/ThingPark/uplink/converter.json index 4616c34c..4b22e73c 100644 --- a/VENDORS/Decentlab/DL-ALB/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/ThingParkEnterprise/uplink/converter.json index 9f3afdc5..d3c9ace6 100644 --- a/VENDORS/Decentlab/DL-ALB/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/ThingsStackCommunity/uplink/converter.json index 820c2df3..8ea62c07 100644 --- a/VENDORS/Decentlab/DL-ALB/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ALB/ThingsStackIndustries/uplink/converter.json index fa5d11ca..450666ba 100644 --- a/VENDORS/Decentlab/DL-ALB/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ALB/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ALB',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/albedometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'incoming_radiation',\n displayName: 'Incoming radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'reflected_radiation',\n displayName: 'Reflected radiation',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: 'W⋅m⁻²'},\n {name: 'albedo',\n displayName: 'albedo',\n convert: function (x) { return ((((x[0] - 32768) > 0) && ((x[1] - 32768) > 0)) ? (x[1] - 32768) / (x[0] - 32768) : 0); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/ChirpStack/uplink/converter.json index d4a2d7ad..3a4b31e8 100644 --- a/VENDORS/Decentlab/DL-ATM22/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/converter.json index 0f2bb5f9..4f0da434 100644 --- a/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/ThingPark/uplink/converter.json index b513d9a5..8ff77aba 100644 --- a/VENDORS/Decentlab/DL-ATM22/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/ThingParkEnterprise/uplink/converter.json index b2b0bf66..b8397486 100644 --- a/VENDORS/Decentlab/DL-ATM22/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/ThingsStackCommunity/uplink/converter.json index fdd5d394..6214dc31 100644 --- a/VENDORS/Decentlab/DL-ATM22/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ATM22/ThingsStackIndustries/uplink/converter.json index 754e3fea..6bf02680 100644 --- a/VENDORS/Decentlab/DL-ATM22/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM22/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM22',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/wind-speed-wind-direction-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 8,\n values: [{name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/ChirpStack/uplink/converter.json index 2bc0f27a..4350d34f 100644 --- a/VENDORS/Decentlab/DL-ATM41/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/converter.json index 1ce99d27..fb6f190c 100644 --- a/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/ThingPark/uplink/converter.json index 71d7a4a3..23c42442 100644 --- a/VENDORS/Decentlab/DL-ATM41/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/ThingParkEnterprise/uplink/converter.json index 0b74b3d7..2fc71390 100644 --- a/VENDORS/Decentlab/DL-ATM41/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/ThingsStackCommunity/uplink/converter.json index 9b462598..2760c22c 100644 --- a/VENDORS/Decentlab/DL-ATM41/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ATM41/ThingsStackIndustries/uplink/converter.json index 36a2dfa0..bd76ab33 100644 --- a/VENDORS/Decentlab/DL-ATM41/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ATM41/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ATM41',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/eleven-parameter-weather-station-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 17,\n values: [{name: 'solar_radiation',\n displayName: 'Solar radiation',\n convert: function (x) { return x[0] - 32768; },\n unit: 'W⋅m⁻²'},\n {name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return (x[1] - 32768) / 1000; },\n unit: 'mm'},\n {name: 'lightning_strike_count',\n displayName: 'Lightning strike count',\n convert: function (x) { return x[2] - 32768; }},\n {name: 'lightning_average_distance',\n displayName: 'Lightning average distance',\n convert: function (x) { return x[3] - 32768; },\n unit: 'km'},\n {name: 'wind_speed',\n displayName: 'Wind speed',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'wind_direction',\n displayName: 'Wind direction',\n convert: function (x) { return (x[5] - 32768) / 10; },\n unit: '°'},\n {name: 'maximum_wind_speed',\n displayName: 'Maximum wind speed',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[7] - 32768) / 10; },\n unit: '°C'},\n {name: 'vapor_pressure',\n displayName: 'Vapor pressure',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'relative_humidity',\n displayName: 'Relative humidity',\n convert: function (x) { return (x[10] - 32768) / 10; },\n unit: '%'},\n {name: 'sensor_temperature_internal',\n displayName: 'Sensor temperature (internal)',\n convert: function (x) { return (x[11] - 32768) / 10; },\n unit: '°C'},\n {name: 'x_orientation_angle',\n displayName: 'X orientation angle',\n convert: function (x) { return (x[12] - 32768) / 10; },\n unit: '°'},\n {name: 'y_orientation_angle',\n displayName: 'Y orientation angle',\n convert: function (x) { return (x[13] - 32768) / 10; },\n unit: '°'},\n {name: 'compass_heading',\n displayName: 'Compass heading',\n convert: function (x) { return x[14] - 32768; },\n unit: '°'},\n {name: 'north_wind_speed',\n displayName: 'North wind speed',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: 'm⋅s⁻¹'},\n {name: 'east_wind_speed',\n displayName: 'East wind speed',\n convert: function (x) { return (x[16] - 32768) / 100; },\n unit: 'm⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/ChirpStack/uplink/converter.json index 59c71b17..49f2b98d 100644 --- a/VENDORS/Decentlab/DL-BLG/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/converter.json index 52f54a5c..e2fc7b55 100644 --- a/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/ThingPark/uplink/converter.json index 660acb32..6b52310f 100644 --- a/VENDORS/Decentlab/DL-BLG/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/ThingParkEnterprise/uplink/converter.json index efa25e8b..685fef7e 100644 --- a/VENDORS/Decentlab/DL-BLG/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/ThingsStackCommunity/uplink/converter.json index 10032cee..0b2ebad0 100644 --- a/VENDORS/Decentlab/DL-BLG/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-BLG/ThingsStackIndustries/uplink/converter.json index 804101db..128cd58c 100644 --- a/VENDORS/Decentlab/DL-BLG/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-BLG/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-BLG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/black-globe-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage_ratio',\n displayName: 'Voltage ratio',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 2; }},\n {name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return 1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000; },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (1 / (0.0008271111 + 0.000208802 * Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000) + 0.000000080592 * Math.pow(Math.log(1000 / (((x[0] + x[1]*65536) / 8388608 - 1) / 2) - 41000), 3) )) - 273.15; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/ChirpStack/uplink/converter.json index ad3aca88..c81720bb 100644 --- a/VENDORS/Decentlab/DL-CTD10/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/converter.json index 6ee09816..0a8793de 100644 --- a/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/ThingPark/uplink/converter.json index e4daa3a1..e9f1f4c5 100644 --- a/VENDORS/Decentlab/DL-CTD10/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/ThingParkEnterprise/uplink/converter.json index 328989fb..ccff91fd 100644 --- a/VENDORS/Decentlab/DL-CTD10/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/ThingsStackCommunity/uplink/converter.json index 345d2c44..f2d73f05 100644 --- a/VENDORS/Decentlab/DL-CTD10/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-CTD10/ThingsStackIndustries/uplink/converter.json index 63ac59ec..1ea4bf0f 100644 --- a/VENDORS/Decentlab/DL-CTD10/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CTD10/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CTD10',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'water_depth',\n displayName: 'Water depth',\n convert: function (x) { return x[0] - 32768; },\n unit: 'mm'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'},\n {name: 'freezing_flag',\n displayName: 'Freezing flag',\n convert: function (x) { return x[3]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/ChirpStack/uplink/converter.json index 9522f81d..8d204b2c 100644 --- a/VENDORS/Decentlab/DL-CWS/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/converter.json index 3b3b94f8..efe31c10 100644 --- a/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/ThingPark/uplink/converter.json index 60dc95ba..757a6ecb 100644 --- a/VENDORS/Decentlab/DL-CWS/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/ThingParkEnterprise/uplink/converter.json index 8aa91d69..05e74158 100644 --- a/VENDORS/Decentlab/DL-CWS/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/ThingsStackCommunity/uplink/converter.json index 6b989f14..2be3ca7d 100644 --- a/VENDORS/Decentlab/DL-CWS/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-CWS/ThingsStackIndustries/uplink/converter.json index 27f633c4..d3027771 100644 --- a/VENDORS/Decentlab/DL-CWS/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/ChirpStack/uplink/converter.json index de2bad52..956e0e69 100644 --- a/VENDORS/Decentlab/DL-CWS2/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/converter.json index 710bd209..7f146a99 100644 --- a/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/ThingPark/uplink/converter.json index ae7dff2e..411e679a 100644 --- a/VENDORS/Decentlab/DL-CWS2/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/ThingParkEnterprise/uplink/converter.json index fe537171..0c0e5444 100644 --- a/VENDORS/Decentlab/DL-CWS2/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/ThingsStackCommunity/uplink/converter.json index d5ca8914..525b7d4d 100644 --- a/VENDORS/Decentlab/DL-CWS2/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-CWS2/ThingsStackIndustries/uplink/converter.json index c6757543..1a443326 100644 --- a/VENDORS/Decentlab/DL-CWS2/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-CWS2/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-CWS2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-winter-road-maintenance-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature_radiation_shield',\n displayName: 'Air temperature (radiation shield)',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity_radiation_shield',\n displayName: 'Air humidity (radiation shield)',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 6,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'angle',\n displayName: 'Angle',\n convert: function (x) { return (x[4] - 32768); },\n unit: '°'},\n {name: 'sensor_temperature',\n displayName: 'Sensor temperature',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/ChirpStack/uplink/converter.json index 1585e176..c0cf0b53 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/converter.json index fc39808e..701f6b64 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/ThingPark/uplink/converter.json index 41d7b603..4b59b362 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/ThingParkEnterprise/uplink/converter.json index 1e5814db..2ffebfe9 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/ThingsStackCommunity/uplink/converter.json index 171c839b..0ab50b71 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-002/ThingsStackIndustries/uplink/converter.json index b096a0f7..793c12cc 100644 --- a/VENDORS/Decentlab/DL-DLR2-002/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-002/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'pulse_count',\n displayName: 'Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'pulse_interval',\n displayName: 'Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_pulse_count',\n displayName: 'Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/ChirpStack/uplink/converter.json index 2c73ba2c..1928393a 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/converter.json index bdc20b58..6e3ba804 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/ThingPark/uplink/converter.json index aa8bc1a5..4487d5fa 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/ThingParkEnterprise/uplink/converter.json index 690937b0..0983eceb 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/ThingsStackCommunity/uplink/converter.json index c2fd5f64..ad138d23 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-003/ThingsStackIndustries/uplink/converter.json index 616f07c5..4d8e5d65 100644 --- a/VENDORS/Decentlab/DL-DLR2-003/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-003/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-003',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'input',\n displayName: 'Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/ChirpStack/uplink/converter.json index b591f73b..4f8d60c8 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/converter.json index 37c1ae62..292b7f59 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/ThingPark/uplink/converter.json index 551051e1..a67af502 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/ThingParkEnterprise/uplink/converter.json index 0e2b257c..53413764 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/ThingsStackCommunity/uplink/converter.json index 973fe68a..05a80246 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-004/ThingsStackIndustries/uplink/converter.json index 398dd5c7..18379f2d 100644 --- a/VENDORS/Decentlab/DL-DLR2-004/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-004/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-004',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 10.0\n },\n SENSORS: [\n {length: 1,\n values: [{name: 'current',\n displayName: 'Current',\n convert: function (x) { return 3 * (x[0] - 32768) / 32768 / 2 / this.PARAMETERS.R * 1000; },\n unit: 'mA'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/ChirpStack/uplink/converter.json index 370da524..ace1222c 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/converter.json index 6adf78b2..57c4ff69 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/ThingPark/uplink/converter.json index cb46457c..503db9a3 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/ThingParkEnterprise/uplink/converter.json index b040debe..788a487b 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/ThingsStackCommunity/uplink/converter.json index 73be55d6..bd40dffc 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-005/ThingsStackIndustries/uplink/converter.json index 3e7b0ecd..ec99677f 100644 --- a/VENDORS/Decentlab/DL-DLR2-005/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-005/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-005',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'voltage',\n displayName: 'Voltage',\n convert: function (x) { return 3 * ((x[0] + x[1]*65536) / 8388608 - 1); },\n unit: 'V'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/ChirpStack/uplink/converter.json index d6d575b0..53e11e13 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/converter.json index e94caf18..dfc21924 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/ThingPark/uplink/converter.json index 099036fc..498fd0b0 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/ThingParkEnterprise/uplink/converter.json index 4d9bff12..8f1e877e 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/ThingsStackCommunity/uplink/converter.json index fe9748d1..19fa2a49 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-006/ThingsStackIndustries/uplink/converter.json index c755c440..1a2329a8 100644 --- a/VENDORS/Decentlab/DL-DLR2-006/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-006/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-006',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/ChirpStack/uplink/converter.json index 27cdbef6..f143fd99 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/converter.json index 97b47b5e..71b01675 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/ThingPark/uplink/converter.json index 9b605232..9fb0bd1c 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/ThingParkEnterprise/uplink/converter.json index a3dbafe3..c220cbc5 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/ThingsStackCommunity/uplink/converter.json index 094a8802..565c8c85 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-008/ThingsStackIndustries/uplink/converter.json index fd7c735d..d97657bc 100644 --- a/VENDORS/Decentlab/DL-DLR2-008/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-008/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-008',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -244.83 + 2.3419 * ((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2))) + 0.0010664 * Math.pow((((x[0] + x[1]*65536) / 8388608 - 1) / 2) * this.PARAMETERS.R / (1 - (((x[0] + x[1]*65536) / 8388608 - 1) / 2)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/ChirpStack/uplink/converter.json index 41937571..f1063881 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/converter.json index 5ccf7bd2..416f2d95 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/ThingPark/uplink/converter.json index ca3dc6dd..4e1ae62c 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/ThingParkEnterprise/uplink/converter.json index ef548913..85cc7595 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/ThingsStackCommunity/uplink/converter.json index c1c35425..3ea6e5ba 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-009/ThingsStackIndustries/uplink/converter.json index 3b689630..8935dee1 100644 --- a/VENDORS/Decentlab/DL-DLR2-009/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-009/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-009',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n R: 2000\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'thermistor_resistance',\n displayName: 'Thermistor resistance',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)); },\n unit: 'Ω'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return -245.18 + 0.23469 * (((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1))) + 0.0000104876 * Math.pow(((x[0] + x[1]*65536) / 8388608 - 1) * 2000 / (1 - ((x[0] + x[1]*65536) / 8388608 - 1)), 2); },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/ChirpStack/uplink/converter.json index 65c338eb..d3c94de7 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/converter.json index 750f12a9..e22eaf45 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/ThingPark/uplink/converter.json index b9ac1347..89e3fb73 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/ThingParkEnterprise/uplink/converter.json index 879863fb..161276e2 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/ThingsStackCommunity/uplink/converter.json index 9fb8948f..5c3b33b4 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-010/ThingsStackIndustries/uplink/converter.json index 78e288e8..22518c8b 100644 --- a/VENDORS/Decentlab/DL-DLR2-010/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-010/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-010',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'ch0_pulse_count',\n displayName: 'CH0: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch0_pulse_interval',\n displayName: 'CH0: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch0_cumulative_pulse_count',\n displayName: 'CH0: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 4,\n values: [{name: 'ch1_pulse_count',\n displayName: 'CH1: Pulse count',\n convert: function (x) { return x[0]; }},\n {name: 'ch1_pulse_interval',\n displayName: 'CH1: Pulse interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'ch1_cumulative_pulse_count',\n displayName: 'CH1: Cumulative pulse count',\n convert: function (x) { return (x[2] + x[3] * 65536); }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/ChirpStack/uplink/converter.json index c9f17317..0e9ee75a 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/converter.json index 654b8a11..aebbfb49 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/ThingPark/uplink/converter.json index 40b68a97..89be8f6a 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/ThingParkEnterprise/uplink/converter.json index 73e633e4..683dcc57 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/ThingsStackCommunity/uplink/converter.json index 453a2f34..bc4f67d4 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-011/ThingsStackIndustries/uplink/converter.json index 31ed18d7..f4b296ea 100644 --- a/VENDORS/Decentlab/DL-DLR2-011/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-011/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-011',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'ch0_input',\n displayName: 'CH0: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'ch1_input',\n displayName: 'CH1: Input',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/ChirpStack/uplink/converter.json index bc41b4a2..c93e31ae 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/converter.json index aa3d003b..a5980a60 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/ThingPark/uplink/converter.json index 39fb1806..78d9da4a 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/ThingParkEnterprise/uplink/converter.json index 72f3735b..73d950ca 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/ThingsStackCommunity/uplink/converter.json index 6b7457ea..87d5e2eb 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DLR2-012/ThingsStackIndustries/uplink/converter.json index 9d9778b5..43289dde 100644 --- a/VENDORS/Decentlab/DL-DLR2-012/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DLR2-012/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DLR2-012',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/analog-or-digital-sensor-device-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'strain_gauge',\n displayName: 'Strain gauge',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) / 64 * 4 / 2.02 * 1000000; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/ChirpStack/uplink/converter.json index 67ed0e96..9a32e207 100644 --- a/VENDORS/Decentlab/DL-DS18/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/converter.json index 5aac4827..20bbd0f2 100644 --- a/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/ThingPark/uplink/converter.json index ae95adc3..85a45576 100644 --- a/VENDORS/Decentlab/DL-DS18/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/ThingParkEnterprise/uplink/converter.json index b62f0087..74e01786 100644 --- a/VENDORS/Decentlab/DL-DS18/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/ThingsStackCommunity/uplink/converter.json index a8c79720..7e2faec8 100644 --- a/VENDORS/Decentlab/DL-DS18/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DS18/ThingsStackIndustries/uplink/converter.json index eaec391d..b80950e3 100644 --- a/VENDORS/Decentlab/DL-DS18/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DS18/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DS18',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[0] - 32768) / 16; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/ChirpStack/uplink/converter.json index c7afb3e1..0bddc875 100644 --- a/VENDORS/Decentlab/DL-DWS/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/converter.json index d25ab16d..0d6558b3 100644 --- a/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/ThingPark/uplink/converter.json index 656dbf64..fe8e2053 100644 --- a/VENDORS/Decentlab/DL-DWS/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/ThingParkEnterprise/uplink/converter.json index fbfe01dd..bc0318af 100644 --- a/VENDORS/Decentlab/DL-DWS/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/ThingsStackCommunity/uplink/converter.json index b2a08465..2535fbad 100644 --- a/VENDORS/Decentlab/DL-DWS/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-DWS/ThingsStackIndustries/uplink/converter.json index 16007a3b..984e9ff1 100644 --- a/VENDORS/Decentlab/DL-DWS/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-DWS/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-DWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/weighing-scale-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f02: 232263168,\n s: 0.000302459,\n m0: 1370\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - this.PARAMETERS.f02) * this.PARAMETERS.s + this.PARAMETERS.m0; },\n unit: 'g'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/ChirpStack/uplink/converter.json index c7f47375..c6db6835 100644 --- a/VENDORS/Decentlab/DL-GMM/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/converter.json index 54d56d69..0c53f191 100644 --- a/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/ThingPark/uplink/converter.json index 14717b69..098d27e6 100644 --- a/VENDORS/Decentlab/DL-GMM/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/ThingParkEnterprise/uplink/converter.json index 54654b35..ade7983f 100644 --- a/VENDORS/Decentlab/DL-GMM/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/ThingsStackCommunity/uplink/converter.json index e77133cc..777cc97f 100644 --- a/VENDORS/Decentlab/DL-GMM/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-GMM/ThingsStackIndustries/uplink/converter.json index c3534717..9ec214dd 100644 --- a/VENDORS/Decentlab/DL-GMM/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-GMM/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-GMM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/greenhouse-multi-monitor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 7,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return (x[0] - 32768) / 10; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return (x[2] - 32768) / 10; },\n unit: '%'},\n {name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return (x[3] - 32768) / 1; },\n unit: 'ppm'},\n {name: 'atmospheric_pressure',\n displayName: 'Atmospheric pressure',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'vapor_pressure_deficit',\n displayName: 'Vapor pressure deficit',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: 'kPa'},\n {name: 'dew_point',\n displayName: 'Dew point',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/ChirpStack/uplink/converter.json index f18d4538..b2c35b42 100644 --- a/VENDORS/Decentlab/DL-IAM/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/converter.json index f3c39955..7a9ec392 100644 --- a/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/ThingPark/uplink/converter.json index d31b53a3..f0f8d4e3 100644 --- a/VENDORS/Decentlab/DL-IAM/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/ThingParkEnterprise/uplink/converter.json index 238a4594..6218b2f6 100644 --- a/VENDORS/Decentlab/DL-IAM/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/ThingsStackCommunity/uplink/converter.json index 6e5b53f4..b8a8d4e6 100644 --- a/VENDORS/Decentlab/DL-IAM/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-IAM/ThingsStackIndustries/uplink/converter.json index a70c9d82..34551a0c 100644 --- a/VENDORS/Decentlab/DL-IAM/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IAM/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IAM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/indoor-ambiance-monitor-including-co2-tvoc-and-motion-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]},\n {length: 2,\n values: [{name: 'ambient_light_visible_infrared',\n displayName: 'Ambient light (visible + infrared)',\n convert: function (x) { return x[0]; }},\n {name: 'ambient_light_infrared',\n displayName: 'Ambient light (infrared)',\n convert: function (x) { return x[1]; }},\n {name: 'illuminance',\n displayName: 'Illuminance',\n convert: function (x) { return Math.max(Math.max(1.0 * x[0] - 1.64 * x[1], 0.59 * x[0] - 0.86 * x[1]), 0) * 1.5504; },\n unit: 'lx'}]},\n {length: 3,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[1]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[2]; }}]},\n {length: 1,\n values: [{name: 'activity_counter',\n displayName: 'Activity counter',\n convert: function (x) { return x[0]; }}]},\n {length: 1,\n values: [{name: 'total_voc',\n displayName: 'Total VOC',\n convert: function (x) { return x[0]; },\n unit: 'ppb'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/ChirpStack/uplink/converter.json index 24073947..96996aa7 100644 --- a/VENDORS/Decentlab/DL-IFD/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/converter.json index dc7bb282..00d18bb8 100644 --- a/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/ThingPark/uplink/converter.json index 0addf933..49196ad8 100644 --- a/VENDORS/Decentlab/DL-IFD/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/ThingParkEnterprise/uplink/converter.json index e9206a03..d2d8bc4c 100644 --- a/VENDORS/Decentlab/DL-IFD/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/ThingsStackCommunity/uplink/converter.json index 48e3e913..9b2566fe 100644 --- a/VENDORS/Decentlab/DL-IFD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-IFD/ThingsStackIndustries/uplink/converter.json index 7356ee11..62a5a082 100644 --- a/VENDORS/Decentlab/DL-IFD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-IFD/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-IFD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/fruit-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'fruit_size',\n displayName: 'Fruit size',\n convert: function (x) { return x[0] / 100; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/ChirpStack/uplink/converter.json index ec875053..09c74d00 100644 --- a/VENDORS/Decentlab/DL-ILT/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/converter.json index a87081c0..e61df23d 100644 --- a/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/ThingPark/uplink/converter.json index 6c04a6c6..3185de09 100644 --- a/VENDORS/Decentlab/DL-ILT/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/ThingParkEnterprise/uplink/converter.json index cbcca1f7..21bdbb0e 100644 --- a/VENDORS/Decentlab/DL-ILT/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/ThingsStackCommunity/uplink/converter.json index 56c1dc0d..fc1ca0e4 100644 --- a/VENDORS/Decentlab/DL-ILT/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ILT/ThingsStackIndustries/uplink/converter.json index b44203da..8a98c5b6 100644 --- a/VENDORS/Decentlab/DL-ILT/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ILT/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ILT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/leaf-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'leaf_temperature',\n displayName: 'Leaf temperature',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/ChirpStack/uplink/converter.json index 80903911..77089d7f 100644 --- a/VENDORS/Decentlab/DL-ISD/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/converter.json index 1ae7ec75..c44b454f 100644 --- a/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/ThingPark/uplink/converter.json index d363ce70..5bbaffbd 100644 --- a/VENDORS/Decentlab/DL-ISD/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/ThingParkEnterprise/uplink/converter.json index 52e33e8d..486fb09b 100644 --- a/VENDORS/Decentlab/DL-ISD/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/ThingsStackCommunity/uplink/converter.json index 2575511e..53394622 100644 --- a/VENDORS/Decentlab/DL-ISD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ISD/ThingsStackIndustries/uplink/converter.json index 8cecac2b..40ebca78 100644 --- a/VENDORS/Decentlab/DL-ISD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISD/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/stem-dendrometer-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'stem_size',\n displayName: 'Stem size',\n convert: function (x) { return x[0] / 1000; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/ChirpStack/uplink/converter.json index 1c1070c7..12b881e9 100644 --- a/VENDORS/Decentlab/DL-ISF/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/converter.json index 44509ae9..cda20721 100644 --- a/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/ThingPark/uplink/converter.json index c0e129a6..b7b63cd3 100644 --- a/VENDORS/Decentlab/DL-ISF/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/ThingParkEnterprise/uplink/converter.json index 9c73cda3..541658fe 100644 --- a/VENDORS/Decentlab/DL-ISF/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/ThingsStackCommunity/uplink/converter.json index 1c3d3f9b..e7164080 100644 --- a/VENDORS/Decentlab/DL-ISF/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ISF/ThingsStackIndustries/uplink/converter.json index d0efbd23..540b9345 100644 --- a/VENDORS/Decentlab/DL-ISF/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ISF/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ISF',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/sapflow-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'sap_flow',\n displayName: 'Sap flow',\n convert: function (x) { return (x[0] * 16 - 50000) / 1000; },\n unit: 'l⋅h⁻¹'},\n {name: 'heat_velocity_outer',\n displayName: 'Heat velocity (outer)',\n convert: function (x) { return (x[1] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'heat_velocity_inner',\n displayName: 'Heat velocity (inner)',\n convert: function (x) { return (x[2] * 16 - 50000) / 1000; },\n unit: 'cm⋅h⁻¹'},\n {name: 'alpha_outer',\n displayName: 'Alpha (outer)',\n convert: function (x) { return (x[3] * 32 - 1000000) / 100000; }},\n {name: 'alpha_inner',\n displayName: 'Alpha (inner)',\n convert: function (x) { return (x[4] * 32 - 1000000) / 100000; }},\n {name: 'beta_outer',\n displayName: 'Beta (outer)',\n convert: function (x) { return (x[5] * 32 - 1000000) / 100000; }},\n {name: 'beta_inner',\n displayName: 'Beta (inner)',\n convert: function (x) { return (x[6] * 32 - 1000000) / 100000; }},\n {name: 'tmax_outer',\n displayName: 'Tmax (outer)',\n convert: function (x) { return (x[7] * 2) / 1000; },\n unit: 's'},\n {name: 'tmax_inner',\n displayName: 'Tmax (inner)',\n convert: function (x) { return (x[8] * 2) / 1000; },\n unit: 's'},\n {name: 'temperature_outer',\n displayName: 'Temperature (outer)',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'max_voltage',\n displayName: 'Max voltage',\n convert: function (x) { return (x[10] - 32768) / 1000; },\n unit: 'V'},\n {name: 'min_voltage',\n displayName: 'Min voltage',\n convert: function (x) { return (x[11] - 32768) / 1000; },\n unit: 'V'},\n {name: 'diagnostic',\n displayName: 'Diagnostic',\n convert: function (x) { return x[12] + x[13] * 65536; }},\n {name: 'upstream_tmax_outer',\n displayName: 'Upstream Tmax (outer)',\n convert: function (x) { return (x[14] * 2) / 1000; },\n unit: 's'},\n {name: 'upstream_tmax_inner',\n displayName: 'Upstream Tmax (inner)',\n convert: function (x) { return (x[15] * 2) / 1000; },\n unit: 's'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/ChirpStack/uplink/converter.json index d09595a0..a5867935 100644 --- a/VENDORS/Decentlab/DL-ITST/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/converter.json index ebad0bf4..8de5bd4d 100644 --- a/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/ThingPark/uplink/converter.json index 3f459ebf..ae6a9bff 100644 --- a/VENDORS/Decentlab/DL-ITST/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/ThingParkEnterprise/uplink/converter.json index 6f123719..179ab24c 100644 --- a/VENDORS/Decentlab/DL-ITST/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/ThingsStackCommunity/uplink/converter.json index cd325402..0a1635a7 100644 --- a/VENDORS/Decentlab/DL-ITST/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ITST/ThingsStackIndustries/uplink/converter.json index 8beb4feb..53415971 100644 --- a/VENDORS/Decentlab/DL-ITST/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ITST/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ITST',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/infrared-thermometer-/-surface-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'temperature_target',\n displayName: 'Temperature target',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'temperature_head',\n displayName: 'Temperature head',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/ChirpStack/uplink/converter.json index 6aa3c8a5..9e801469 100644 --- a/VENDORS/Decentlab/DL-KL66/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/converter.json index b9d73e1c..9a1da011 100644 --- a/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/ThingPark/uplink/converter.json index 9d8b7642..d58592aa 100644 --- a/VENDORS/Decentlab/DL-KL66/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/ThingParkEnterprise/uplink/converter.json index ce2b6280..1f9ce293 100644 --- a/VENDORS/Decentlab/DL-KL66/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/ThingsStackCommunity/uplink/converter.json index 1d12fbeb..a3db4d46 100644 --- a/VENDORS/Decentlab/DL-KL66/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-KL66/ThingsStackIndustries/uplink/converter.json index 6078f59a..23fd9048 100644 --- a/VENDORS/Decentlab/DL-KL66/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-KL66/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-KL66',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/strain-/-weight-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n f0: 15383.72,\n k: 46.4859\n },\n SENSORS: [\n {length: 3,\n values: [{name: 'counter_reading',\n displayName: 'Counter reading',\n convert: function (x) { return x[0]; }},\n {name: 'measurement_interval',\n displayName: 'Measurement interval',\n convert: function (x) { return x[1] / 32768; }},\n {name: 'frequency',\n displayName: 'Frequency',\n convert: function (x) { return x[0] / x[1] * 32768; },\n unit: 'Hz'},\n {name: 'weight',\n displayName: 'Weight',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000; },\n unit: 'g'},\n {name: 'elongation',\n displayName: 'Elongation',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067; },\n unit: 'µm'},\n {name: 'strain',\n displayName: 'Strain',\n convert: function (x) { return (Math.pow(x[0] / x[1] * 32768, 2) - Math.pow(this.PARAMETERS.f0, 2)) * this.PARAMETERS.k / 1000000 * (-1.5) / 1000 * 9.8067 / 0.066; },\n unit: 'µm⋅m⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-LID/ChirpStack/uplink/converter.json index 4a94bacb..a299d576 100644 --- a/VENDORS/Decentlab/DL-LID/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/converter.json index 9d255c54..f07766f0 100644 --- a/VENDORS/Decentlab/DL-LID/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-LID/ThingPark/uplink/converter.json index bc2b6adc..4abfdf69 100644 --- a/VENDORS/Decentlab/DL-LID/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-LID/ThingParkEnterprise/uplink/converter.json index 881094b1..55268c2c 100644 --- a/VENDORS/Decentlab/DL-LID/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-LID/ThingsStackCommunity/uplink/converter.json index 63626037..8a295860 100644 --- a/VENDORS/Decentlab/DL-LID/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-LID/ThingsStackIndustries/uplink/converter.json index 2a38e60e..65f916c4 100644 --- a/VENDORS/Decentlab/DL-LID/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LID/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LID',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/laser-distance-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 11,\n values: [{name: 'distance_average',\n displayName: 'Distance: average',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'distance_minimum',\n displayName: 'Distance: minimum',\n convert: function (x) { return x[1]; },\n unit: 'mm'},\n {name: 'distance_maximum',\n displayName: 'Distance: maximum',\n convert: function (x) { return x[2]; },\n unit: 'mm'},\n {name: 'distance_median',\n displayName: 'Distance: median',\n convert: function (x) { return x[3]; },\n unit: 'mm'},\n {name: 'distance_10th_percentile',\n displayName: 'Distance: 10th percentile',\n convert: function (x) { return x[4]; },\n unit: 'mm'},\n {name: 'distance_25th_percentile',\n displayName: 'Distance: 25th percentile',\n convert: function (x) { return x[5]; },\n unit: 'mm'},\n {name: 'distance_75th_percentile',\n displayName: 'Distance: 75th percentile',\n convert: function (x) { return x[6]; },\n unit: 'mm'},\n {name: 'distance_90th_percentile',\n displayName: 'Distance: 90th percentile',\n convert: function (x) { return x[7]; },\n unit: 'mm'},\n {name: 'distance_most_frequent_value',\n displayName: 'Distance: most frequent value',\n convert: function (x) { return x[8]; },\n unit: 'mm'},\n {name: 'number_of_samples',\n displayName: 'Number of samples',\n convert: function (x) { return x[9]; }},\n {name: 'total_acquisition_time',\n displayName: 'Total acquisition time',\n convert: function (x) { return x[10] / 1.024; },\n unit: 'ms'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/ChirpStack/uplink/converter.json index 4c796701..216a126d 100644 --- a/VENDORS/Decentlab/DL-LP8P/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/converter.json index 1551a094..ea851cba 100644 --- a/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/ThingPark/uplink/converter.json index 778e6750..bbc9e067 100644 --- a/VENDORS/Decentlab/DL-LP8P/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/ThingParkEnterprise/uplink/converter.json index cd3381e7..d17f96d4 100644 --- a/VENDORS/Decentlab/DL-LP8P/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/ThingsStackCommunity/uplink/converter.json index 11130d2f..e2a51f50 100644 --- a/VENDORS/Decentlab/DL-LP8P/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-LP8P/ThingsStackIndustries/uplink/converter.json index ca5259dc..f38116a6 100644 --- a/VENDORS/Decentlab/DL-LP8P/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LP8P/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LP8P',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/co2-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'barometer_temperature',\n displayName: 'Barometer temperature',\n convert: function (x) { return (x[0] - 5000) / 100; },\n unit: '°C'},\n {name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[1] * 2; },\n unit: 'Pa'}]},\n {length: 8,\n values: [{name: 'co2_concentration',\n displayName: 'CO2 concentration',\n convert: function (x) { return x[0] - 32768; },\n unit: 'ppm'},\n {name: 'co2_concentration_lpf',\n displayName: 'CO2 concentration LPF',\n convert: function (x) { return x[1] - 32768; },\n unit: 'ppm'},\n {name: 'co2_sensor_temperature',\n displayName: 'CO2 sensor temperature',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'capacitor_voltage_1',\n displayName: 'Capacitor voltage 1',\n convert: function (x) { return x[3] / 1000; },\n unit: 'V'},\n {name: 'capacitor_voltage_2',\n displayName: 'Capacitor voltage 2',\n convert: function (x) { return x[4] / 1000; },\n unit: 'V'},\n {name: 'co2_sensor_status',\n displayName: 'CO2 sensor status',\n convert: function (x) { return x[5]; }},\n {name: 'raw_ir_reading',\n displayName: 'Raw IR reading',\n convert: function (x) { return x[6]; }},\n {name: 'raw_ir_reading_lpf',\n displayName: 'Raw IR reading LPF',\n convert: function (x) { return x[7]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/ChirpStack/uplink/converter.json index 7ebbdd24..187bf3ab 100644 --- a/VENDORS/Decentlab/DL-LPW/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/converter.json index 7c2263aa..2f81297a 100644 --- a/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/ThingPark/uplink/converter.json index 8dbfdaf7..12849c9b 100644 --- a/VENDORS/Decentlab/DL-LPW/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/ThingParkEnterprise/uplink/converter.json index 0e242acd..14d130d4 100644 --- a/VENDORS/Decentlab/DL-LPW/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/ThingsStackCommunity/uplink/converter.json index 63cf0a22..4e385eff 100644 --- a/VENDORS/Decentlab/DL-LPW/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-LPW/ThingsStackIndustries/uplink/converter.json index 7bb6153b..0ada6c9a 100644 --- a/VENDORS/Decentlab/DL-LPW/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LPW/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LPW',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/linear-position-/-way-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'potentiometer_position',\n displayName: 'Potentiometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1 * 100; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/ChirpStack/uplink/converter.json index 9d88567b..1fa84f8c 100644 --- a/VENDORS/Decentlab/DL-LWS/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/converter.json index 2fab74b3..78926194 100644 --- a/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/ThingPark/uplink/converter.json index a2436028..8abb6cf8 100644 --- a/VENDORS/Decentlab/DL-LWS/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/ThingParkEnterprise/uplink/converter.json index 05cc5c95..06d60291 100644 --- a/VENDORS/Decentlab/DL-LWS/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/ThingsStackCommunity/uplink/converter.json index 9f2a4848..40758f79 100644 --- a/VENDORS/Decentlab/DL-LWS/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-LWS/ThingsStackIndustries/uplink/converter.json index b8af5c86..c1a9b1c6 100644 --- a/VENDORS/Decentlab/DL-LWS/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-LWS/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-LWS',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/leaf-wetness-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'leaf_wetness_index',\n displayName: 'Leaf wetness index',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 1; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/ChirpStack/uplink/converter.json index 1d84df7c..af7b4d90 100644 --- a/VENDORS/Decentlab/DL-MBX/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/converter.json index 1421129c..63638653 100644 --- a/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/ThingPark/uplink/converter.json index b316e413..ac9851e2 100644 --- a/VENDORS/Decentlab/DL-MBX/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/ThingParkEnterprise/uplink/converter.json index 311473b0..e35e9e4e 100644 --- a/VENDORS/Decentlab/DL-MBX/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/ThingsStackCommunity/uplink/converter.json index f200d409..d0ab6d87 100644 --- a/VENDORS/Decentlab/DL-MBX/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-MBX/ThingsStackIndustries/uplink/converter.json index dcb31c3a..52b2a062 100644 --- a/VENDORS/Decentlab/DL-MBX/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MBX/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MBX',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ultrasonic-distance-/-level-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'distance',\n displayName: 'Distance',\n convert: function (x) { return x[0]; },\n unit: 'mm'},\n {name: 'number_of_valid_samples',\n displayName: 'Number of valid samples',\n convert: function (x) { return x[1]; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/ChirpStack/uplink/converter.json index 288b7586..dcf9035d 100644 --- a/VENDORS/Decentlab/DL-MES5/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/converter.json index 371ed5a5..9c40518c 100644 --- a/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/ThingPark/uplink/converter.json index c24aad24..e928d90d 100644 --- a/VENDORS/Decentlab/DL-MES5/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/ThingParkEnterprise/uplink/converter.json index d0a9553a..1bd280c3 100644 --- a/VENDORS/Decentlab/DL-MES5/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/ThingsStackCommunity/uplink/converter.json index 9d6877ea..10ee2916 100644 --- a/VENDORS/Decentlab/DL-MES5/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-MES5/ThingsStackIndustries/uplink/converter.json index 6df5b212..d6132466 100644 --- a/VENDORS/Decentlab/DL-MES5/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-MES5/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-MES5',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/large-range-optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'sludge_blanket',\n displayName: 'Sludge blanket',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'suspended_solid',\n displayName: 'Suspended solid',\n convert: function (x) { return x[3] / 100; },\n unit: 'g⋅L⁻¹'},\n {name: 'turbidity',\n displayName: 'Turbidity',\n convert: function (x) { return x[4] / 10; },\n unit: 'FAU'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/ChirpStack/uplink/converter.json index 14674a76..92b9c4af 100644 --- a/VENDORS/Decentlab/DL-NTU/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/converter.json index 46b8b140..377f2821 100644 --- a/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/ThingPark/uplink/converter.json index 049533fa..40da9949 100644 --- a/VENDORS/Decentlab/DL-NTU/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/ThingParkEnterprise/uplink/converter.json index 4b3d46c6..24f1b4c4 100644 --- a/VENDORS/Decentlab/DL-NTU/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/ThingsStackCommunity/uplink/converter.json index d41f6bab..904b44e9 100644 --- a/VENDORS/Decentlab/DL-NTU/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-NTU/ThingsStackIndustries/uplink/converter.json index 2ccc116f..57f95e19 100644 --- a/VENDORS/Decentlab/DL-NTU/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-NTU/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-NTU',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-turbidity-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'turbidity_in_ntu',\n displayName: 'Turbidity in NTU',\n convert: function (x) { return x[2] / 10; },\n unit: 'NTU'},\n {name: 'turbidity_in_fnu',\n displayName: 'Turbidity in FNU',\n convert: function (x) { return x[3] / 10; },\n unit: 'FNU'},\n {name: 'turbidity_in_mg_l',\n displayName: 'Turbidity in mg/L',\n convert: function (x) { return x[4] / 10; },\n unit: 'mg⋅L⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/ChirpStack/uplink/converter.json index 80b7f035..710a8151 100644 --- a/VENDORS/Decentlab/DL-OPTOD/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/converter.json index 83c31196..f0485b81 100644 --- a/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/ThingPark/uplink/converter.json index 60fd68af..da908db4 100644 --- a/VENDORS/Decentlab/DL-OPTOD/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/ThingParkEnterprise/uplink/converter.json index ed7d96dd..8e726d13 100644 --- a/VENDORS/Decentlab/DL-OPTOD/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/ThingsStackCommunity/uplink/converter.json index ba8a0396..bc7ffaf1 100644 --- a/VENDORS/Decentlab/DL-OPTOD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-OPTOD/ThingsStackIndustries/uplink/converter.json index bdd99164..135dd455 100644 --- a/VENDORS/Decentlab/DL-OPTOD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-OPTOD/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-OPTOD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/optical-dissolved-oxygen-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'oxygen_saturation',\n displayName: 'Oxygen saturation',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'oxygen_concentration',\n displayName: 'Oxygen concentration',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: 'mg⋅L⁻¹'},\n {name: 'oxygen_concentration_alt',\n displayName: 'Oxygen concentration (alt)',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: 'ppm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/ChirpStack/uplink/converter.json index c853015f..53e066e3 100644 --- a/VENDORS/Decentlab/DL-PAR/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/converter.json index 453ae721..867a9c97 100644 --- a/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/ThingPark/uplink/converter.json index fe10aafc..3a2d2402 100644 --- a/VENDORS/Decentlab/DL-PAR/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/ThingParkEnterprise/uplink/converter.json index 546889c9..8c94e6df 100644 --- a/VENDORS/Decentlab/DL-PAR/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/ThingsStackCommunity/uplink/converter.json index d8102e05..7111a976 100644 --- a/VENDORS/Decentlab/DL-PAR/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PAR/ThingsStackIndustries/uplink/converter.json index b1d569b1..05585091 100644 --- a/VENDORS/Decentlab/DL-PAR/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PAR/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PAR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/photosynthetically-active-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'photosynthetically_active_radiation',\n displayName: 'Photosynthetically active radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'µmol⋅m⁻²⋅s⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/ChirpStack/uplink/converter.json index 682e0db2..ced9faa1 100644 --- a/VENDORS/Decentlab/DL-PHEHT/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/converter.json index 9eeda5de..ad31f394 100644 --- a/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/ThingPark/uplink/converter.json index 1124b74b..a954e859 100644 --- a/VENDORS/Decentlab/DL-PHEHT/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/ThingParkEnterprise/uplink/converter.json index 167a39f0..49ec2569 100644 --- a/VENDORS/Decentlab/DL-PHEHT/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/ThingsStackCommunity/uplink/converter.json index f78f8976..e0c24eb8 100644 --- a/VENDORS/Decentlab/DL-PHEHT/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PHEHT/ThingsStackIndustries/uplink/converter.json index 2c08fb67..5b5f0c5f 100644 --- a/VENDORS/Decentlab/DL-PHEHT/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PHEHT/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PHEHT',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/ph-orp-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 5,\n values: [{name: 'status',\n displayName: 'Status',\n convert: function (x) { return x[0]; }},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'ph',\n displayName: 'pH',\n convert: function (x) { return (x[2] - 32768) / 100; }},\n {name: 'redox',\n displayName: 'Redox',\n convert: function (x) { return (x[3] - 32768) / 10; },\n unit: 'mV'},\n {name: 'ph_mv',\n displayName: 'pH-mV',\n convert: function (x) { return (x[4] - 32768) / 10; },\n unit: 'mV'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PM/ChirpStack/uplink/converter.json index 6b0d7649..fa5f6b1e 100644 --- a/VENDORS/Decentlab/DL-PM/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/converter.json index 753fff22..c2985aa2 100644 --- a/VENDORS/Decentlab/DL-PM/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PM/ThingPark/uplink/converter.json index 9062eb66..da27e42c 100644 --- a/VENDORS/Decentlab/DL-PM/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PM/ThingParkEnterprise/uplink/converter.json index 24fe0fc5..4454e57e 100644 --- a/VENDORS/Decentlab/DL-PM/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PM/ThingsStackCommunity/uplink/converter.json index 3b0fcd1b..fc424876 100644 --- a/VENDORS/Decentlab/DL-PM/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PM/ThingsStackIndustries/uplink/converter.json index 796f5e4c..a68a52cb 100644 --- a/VENDORS/Decentlab/DL-PM/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PM/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/particulate-matter-temperature-humidity-and-barometric-pressure-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]},\n {length: 10,\n values: [{name: 'pm1_0_mass_concentration',\n displayName: 'PM1.0 mass concentration',\n convert: function (x) { return x[0] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm2_5_mass_concentration',\n displayName: 'PM2.5 mass concentration',\n convert: function (x) { return x[1] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm4_mass_concentration',\n displayName: 'PM4 mass concentration',\n convert: function (x) { return x[2] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'pm10_mass_concentration',\n displayName: 'PM10 mass concentration',\n convert: function (x) { return x[3] / 10; },\n unit: 'µg⋅m⁻³'},\n {name: 'typical_particle_size',\n displayName: 'Typical particle size',\n convert: function (x) { return x[4]; },\n unit: 'nm'},\n {name: 'pm0_5_number_concentration',\n displayName: 'PM0.5 number concentration',\n convert: function (x) { return x[5] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm1_0_number_concentration',\n displayName: 'PM1.0 number concentration',\n convert: function (x) { return x[6] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm2_5_number_concentration',\n displayName: 'PM2.5 number concentration',\n convert: function (x) { return x[7] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm4_number_concentration',\n displayName: 'PM4 number concentration',\n convert: function (x) { return x[8] / 10; },\n unit: '1⋅cm⁻³'},\n {name: 'pm10_number_concentration',\n displayName: 'PM10 number concentration',\n convert: function (x) { return x[9] / 10; },\n unit: '1⋅cm⁻³'}]},\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175.72 * x[0] / 65536 - 46.85; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 125 * x[1] / 65536 - 6; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'barometric_pressure',\n displayName: 'Barometric pressure',\n convert: function (x) { return x[0] * 2; },\n unit: 'Pa'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/ChirpStack/uplink/converter.json index 63327d21..e5273fbc 100644 --- a/VENDORS/Decentlab/DL-PR21/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/converter.json index c84ba6f5..ab3ccbdc 100644 --- a/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/ThingPark/uplink/converter.json index 5bb9a6c2..52e5471b 100644 --- a/VENDORS/Decentlab/DL-PR21/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/ThingParkEnterprise/uplink/converter.json index 9bc39f4d..575d16f3 100644 --- a/VENDORS/Decentlab/DL-PR21/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/ThingsStackCommunity/uplink/converter.json index 19b427a4..00984815 100644 --- a/VENDORS/Decentlab/DL-PR21/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PR21/ThingsStackIndustries/uplink/converter.json index 03b72fbd..40f459ef 100644 --- a/VENDORS/Decentlab/DL-PR21/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR21/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-with-g1/4-pipe-thread-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: -1.0,\n Pmax: 10.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/ChirpStack/uplink/converter.json index 2eb953f6..ffc8a600 100644 --- a/VENDORS/Decentlab/DL-PR26/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/converter.json index 512a8002..3e934415 100644 --- a/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/ThingPark/uplink/converter.json index 8be57d40..714de051 100644 --- a/VENDORS/Decentlab/DL-PR26/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/ThingParkEnterprise/uplink/converter.json index 1c32110c..cf349be8 100644 --- a/VENDORS/Decentlab/DL-PR26/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/ThingsStackCommunity/uplink/converter.json index 7a1b5c9e..f52a7487 100644 --- a/VENDORS/Decentlab/DL-PR26/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PR26/ThingsStackIndustries/uplink/converter.json index ad96f976..196bb6ac 100644 --- a/VENDORS/Decentlab/DL-PR26/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR26/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR26',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n Pmin: 0.0,\n Pmax: 1.0\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 16384) / 32768 * (this.PARAMETERS.Pmax - this.PARAMETERS.Pmin) + this.PARAMETERS.Pmin; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 384) * 0.003125 - 50; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/ChirpStack/uplink/converter.json index 16ed9adf..f684c1a8 100644 --- a/VENDORS/Decentlab/DL-PR36/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/converter.json index 181d2bdf..8931a894 100644 --- a/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/ThingPark/uplink/converter.json index 93c63e6f..c5623f4e 100644 --- a/VENDORS/Decentlab/DL-PR36/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/ThingParkEnterprise/uplink/converter.json index 764642ef..9c4b935a 100644 --- a/VENDORS/Decentlab/DL-PR36/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/ThingsStackCommunity/uplink/converter.json index 821d8936..afe92002 100644 --- a/VENDORS/Decentlab/DL-PR36/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PR36/ThingsStackIndustries/uplink/converter.json index 7407ebec..152b7388 100644 --- a/VENDORS/Decentlab/DL-PR36/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192\n },\n SENSORS: [\n {length: 2,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature',\n displayName: 'Temperature',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/ChirpStack/uplink/converter.json index f44d72d5..382ee187 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/converter.json index f07c3804..d70264d7 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/ThingPark/uplink/converter.json index 5ad16381..67104cac 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/ThingParkEnterprise/uplink/converter.json index fd0f9d18..aef7de0b 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/ThingsStackCommunity/uplink/converter.json index 85d8b542..3af328f2 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PR36CTD/ThingsStackIndustries/uplink/converter.json index e1bd1ea7..0917f26b 100644 --- a/VENDORS/Decentlab/DL-PR36CTD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PR36CTD/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PR36CTD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-pressure-/-liquid-level-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n kp: 8192,\n kec: 1024\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'pressure',\n displayName: 'Pressure',\n convert: function (x) { return (x[0] - 32768) / this.PARAMETERS.kp; },\n unit: 'bar'},\n {name: 'temperature_electronics',\n displayName: 'Temperature (electronics)',\n convert: function (x) { return (x[1] - 32768) / 256; },\n unit: '°C'},\n {name: 'temperature_pt1000',\n displayName: 'Temperature (PT1000)',\n convert: function (x) { return (x[2] - 32768) / 256; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return (x[3] - 32768) / this.PARAMETERS.kec; },\n unit: 'mS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/ChirpStack/uplink/converter.json index 759ed2b6..4b2fa51e 100644 --- a/VENDORS/Decentlab/DL-PYR/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/converter.json index 4312fb94..bc5974f6 100644 --- a/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/ThingPark/uplink/converter.json index d41b40fb..8d2a4564 100644 --- a/VENDORS/Decentlab/DL-PYR/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/ThingParkEnterprise/uplink/converter.json index e95fb5d1..26b6fce0 100644 --- a/VENDORS/Decentlab/DL-PYR/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/ThingsStackCommunity/uplink/converter.json index 6d406686..8e0fb586 100644 --- a/VENDORS/Decentlab/DL-PYR/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-PYR/ThingsStackIndustries/uplink/converter.json index c74fcbe4..53efa7ba 100644 --- a/VENDORS/Decentlab/DL-PYR/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-PYR/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-PYR',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/total-solar-radiation-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 1,\n values: [{name: 'total_solar_radiation',\n displayName: 'Total solar radiation',\n convert: function (x) { return 3 * (x[0] / 32768 - 1) * 1000 * 5; },\n unit: 'W⋅m⁻²'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/ChirpStack/uplink/converter.json index 71c3016d..63ce4b95 100644 --- a/VENDORS/Decentlab/DL-RHC/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/converter.json index ca4eb4e0..fda20300 100644 --- a/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/ThingPark/uplink/converter.json index 9e91355b..e39cd6dd 100644 --- a/VENDORS/Decentlab/DL-RHC/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/ThingParkEnterprise/uplink/converter.json index a2805915..c99a16d4 100644 --- a/VENDORS/Decentlab/DL-RHC/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/ThingsStackCommunity/uplink/converter.json index 8b1ee146..d600c518 100644 --- a/VENDORS/Decentlab/DL-RHC/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-RHC/ThingsStackIndustries/uplink/converter.json index 5021f6e0..9ebe724e 100644 --- a/VENDORS/Decentlab/DL-RHC/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-RHC/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-RHC',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/high-precision-air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'sensor_id',\n displayName: 'Sensor ID',\n convert: function (x) { return x[0] + x[1] * 65536; }},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return x[2] / 100; },\n unit: '%'},\n {name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/ChirpStack/uplink/converter.json index 675a56e1..6051d58c 100644 --- a/VENDORS/Decentlab/DL-SDD/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/converter.json index e83192b6..11124a32 100644 --- a/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/ThingPark/uplink/converter.json index 59928fc0..f7ebac2d 100644 --- a/VENDORS/Decentlab/DL-SDD/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/ThingParkEnterprise/uplink/converter.json index 94a41bc6..eb8860ba 100644 --- a/VENDORS/Decentlab/DL-SDD/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/ThingsStackCommunity/uplink/converter.json index 166ee14c..1d2af02a 100644 --- a/VENDORS/Decentlab/DL-SDD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-SDD/ThingsStackIndustries/uplink/converter.json index 2d8d8d12..68b28973 100644 --- a/VENDORS/Decentlab/DL-SDD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SDD/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SDD',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-salinity-profile */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 18,\n values: [{name: 'moisture_at_level_0',\n displayName: 'Moisture at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_1',\n displayName: 'Moisture at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_2',\n displayName: 'Moisture at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_3',\n displayName: 'Moisture at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_4',\n displayName: 'Moisture at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_5',\n displayName: 'Moisture at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_0',\n displayName: 'Salinity at level 0',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_1',\n displayName: 'Salinity at level 1',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_2',\n displayName: 'Salinity at level 2',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_3',\n displayName: 'Salinity at level 3',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_4',\n displayName: 'Salinity at level 4',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_5',\n displayName: 'Salinity at level 5',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 18,\n values: [{name: 'moisture_at_level_6',\n displayName: 'Moisture at level 6',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_7',\n displayName: 'Moisture at level 7',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_8',\n displayName: 'Moisture at level 8',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_9',\n displayName: 'Moisture at level 9',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_10',\n displayName: 'Moisture at level 10',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '%'},\n {name: 'moisture_at_level_11',\n displayName: 'Moisture at level 11',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '%'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'salinity_at_level_6',\n displayName: 'Salinity at level 6',\n convert: function (x) { return x[12] - 100; }},\n {name: 'salinity_at_level_7',\n displayName: 'Salinity at level 7',\n convert: function (x) { return x[13] - 100; }},\n {name: 'salinity_at_level_8',\n displayName: 'Salinity at level 8',\n convert: function (x) { return x[14] - 100; }},\n {name: 'salinity_at_level_9',\n displayName: 'Salinity at level 9',\n convert: function (x) { return x[15] - 100; }},\n {name: 'salinity_at_level_10',\n displayName: 'Salinity at level 10',\n convert: function (x) { return x[16] - 100; }},\n {name: 'salinity_at_level_11',\n displayName: 'Salinity at level 11',\n convert: function (x) { return x[17] - 100; }}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json index dd22a333..f4d2c4ed 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json index 401d473e..be781ef1 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json index 5cf20443..2feb45d4 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json index fcf8c440..533f0957 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json index f697c13f..5a1a4894 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json index 2bd6a97b..0f49c3d4 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/ChirpStack/uplink/converter.json index b6346f77..8250ffe0 100644 --- a/VENDORS/Decentlab/DL-SMTP/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/converter.json index dde93915..8dfcb9ce 100644 --- a/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/ThingPark/uplink/converter.json index 405738e0..fde453c4 100644 --- a/VENDORS/Decentlab/DL-SMTP/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/ThingParkEnterprise/uplink/converter.json index b6d40a24..67463008 100644 --- a/VENDORS/Decentlab/DL-SMTP/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/ThingsStackCommunity/uplink/converter.json index a3e874bd..b90b8238 100644 --- a/VENDORS/Decentlab/DL-SMTP/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SMTP/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-SMTP/ThingsStackIndustries/uplink/converter.json index b2e4ea39..792a529f 100644 --- a/VENDORS/Decentlab/DL-SMTP/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SMTP/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SMTP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-and-temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'soil_moisture_at_depth_0',\n displayName: 'Soil moisture at depth 0',\n convert: function (x) { return (x[0] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_0',\n displayName: 'Soil temperature at depth 0',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_1',\n displayName: 'Soil moisture at depth 1',\n convert: function (x) { return (x[2] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_1',\n displayName: 'Soil temperature at depth 1',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_2',\n displayName: 'Soil moisture at depth 2',\n convert: function (x) { return (x[4] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_2',\n displayName: 'Soil temperature at depth 2',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_3',\n displayName: 'Soil moisture at depth 3',\n convert: function (x) { return (x[6] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_3',\n displayName: 'Soil temperature at depth 3',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_4',\n displayName: 'Soil moisture at depth 4',\n convert: function (x) { return (x[8] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_4',\n displayName: 'Soil temperature at depth 4',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_5',\n displayName: 'Soil moisture at depth 5',\n convert: function (x) { return (x[10] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_5',\n displayName: 'Soil temperature at depth 5',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_6',\n displayName: 'Soil moisture at depth 6',\n convert: function (x) { return (x[12] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_6',\n displayName: 'Soil temperature at depth 6',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'soil_moisture_at_depth_7',\n displayName: 'Soil moisture at depth 7',\n convert: function (x) { return (x[14] - 2500) / 500; }},\n {name: 'soil_temperature_at_depth_7',\n displayName: 'Soil temperature at depth 7',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/ChirpStack/uplink/converter.json index 01dac6e4..bc92fdf3 100644 --- a/VENDORS/Decentlab/DL-TBRG/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/converter.json index 301f89a2..42b6568d 100644 --- a/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/ThingPark/uplink/converter.json index bd76247f..d73f28ff 100644 --- a/VENDORS/Decentlab/DL-TBRG/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/ThingParkEnterprise/uplink/converter.json index 632d286a..bde116c4 100644 --- a/VENDORS/Decentlab/DL-TBRG/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/ThingsStackCommunity/uplink/converter.json index b5685fce..f2b5db85 100644 --- a/VENDORS/Decentlab/DL-TBRG/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-TBRG/ThingsStackIndustries/uplink/converter.json index bde352c3..a4ac779a 100644 --- a/VENDORS/Decentlab/DL-TBRG/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TBRG/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TBRG',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/tipping-bucket-rain-gauge-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n /* device-specific parameters */\n PARAMETERS: {\n resolution: 0.1\n },\n SENSORS: [\n {length: 4,\n values: [{name: 'precipitation',\n displayName: 'Precipitation',\n convert: function (x) { return x[0] * this.PARAMETERS.resolution; },\n unit: 'mm'},\n {name: 'precipitation_interval',\n displayName: 'Precipitation interval',\n convert: function (x) { return x[1]; },\n unit: 's'},\n {name: 'cumulative_precipitation',\n displayName: 'Cumulative precipitation',\n convert: function (x) { return (x[2] + x[3] * 65536) * this.PARAMETERS.resolution; },\n unit: 'mm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-TP/ChirpStack/uplink/converter.json index 04e865c7..92e7ce57 100644 --- a/VENDORS/Decentlab/DL-TP/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/converter.json index f6f51bbc..c97fca3c 100644 --- a/VENDORS/Decentlab/DL-TP/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-TP/ThingPark/uplink/converter.json index 36d1fa5e..7b334bdf 100644 --- a/VENDORS/Decentlab/DL-TP/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-TP/ThingParkEnterprise/uplink/converter.json index 5cffd522..30427ba2 100644 --- a/VENDORS/Decentlab/DL-TP/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-TP/ThingsStackCommunity/uplink/converter.json index 07b5d8ed..7911998e 100644 --- a/VENDORS/Decentlab/DL-TP/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-TP/ThingsStackIndustries/uplink/converter.json index cfb8edb8..89674441 100644 --- a/VENDORS/Decentlab/DL-TP/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TP/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TP',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/temperature-profile-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 16,\n values: [{name: 'temperature_at_level_0',\n displayName: 'Temperature at level 0',\n convert: function (x) { return (x[0] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_1',\n displayName: 'Temperature at level 1',\n convert: function (x) { return (x[1] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_2',\n displayName: 'Temperature at level 2',\n convert: function (x) { return (x[2] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_3',\n displayName: 'Temperature at level 3',\n convert: function (x) { return (x[3] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_4',\n displayName: 'Temperature at level 4',\n convert: function (x) { return (x[4] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_5',\n displayName: 'Temperature at level 5',\n convert: function (x) { return (x[5] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_6',\n displayName: 'Temperature at level 6',\n convert: function (x) { return (x[6] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_7',\n displayName: 'Temperature at level 7',\n convert: function (x) { return (x[7] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_8',\n displayName: 'Temperature at level 8',\n convert: function (x) { return (x[8] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_9',\n displayName: 'Temperature at level 9',\n convert: function (x) { return (x[9] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_10',\n displayName: 'Temperature at level 10',\n convert: function (x) { return (x[10] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_11',\n displayName: 'Temperature at level 11',\n convert: function (x) { return (x[11] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_12',\n displayName: 'Temperature at level 12',\n convert: function (x) { return (x[12] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_13',\n displayName: 'Temperature at level 13',\n convert: function (x) { return (x[13] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_14',\n displayName: 'Temperature at level 14',\n convert: function (x) { return (x[14] - 32768) / 100; },\n unit: '°C'},\n {name: 'temperature_at_level_15',\n displayName: 'Temperature at level 15',\n convert: function (x) { return (x[15] - 32768) / 100; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/ChirpStack/uplink/converter.json index 97b7ddf0..cf9c9f12 100644 --- a/VENDORS/Decentlab/DL-TRS11/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/converter.json index c99bd9b4..d00db0ba 100644 --- a/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/ThingPark/uplink/converter.json index d6181926..4bf4d2a5 100644 --- a/VENDORS/Decentlab/DL-TRS11/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/ThingParkEnterprise/uplink/converter.json index c7712837..43a9c4b9 100644 --- a/VENDORS/Decentlab/DL-TRS11/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/ThingsStackCommunity/uplink/converter.json index d701fee9..fbcfe66f 100644 --- a/VENDORS/Decentlab/DL-TRS11/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-TRS11/ThingsStackIndustries/uplink/converter.json index 5097bcc5..84f67b4b 100644 --- a/VENDORS/Decentlab/DL-TRS11/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS11/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS11',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/ChirpStack/uplink/converter.json index 75da0415..1f17f574 100644 --- a/VENDORS/Decentlab/DL-TRS12/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/converter.json index 7d7a5d12..84720450 100644 --- a/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/ThingPark/uplink/converter.json index 42688da0..499e99da 100644 --- a/VENDORS/Decentlab/DL-TRS12/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/ThingParkEnterprise/uplink/converter.json index 466b07f6..2feb7d05 100644 --- a/VENDORS/Decentlab/DL-TRS12/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/ThingsStackCommunity/uplink/converter.json index 43e24ee3..d73de558 100644 --- a/VENDORS/Decentlab/DL-TRS12/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-TRS12/ThingsStackIndustries/uplink/converter.json index 39262146..ae2b1b80 100644 --- a/VENDORS/Decentlab/DL-TRS12/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS12/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS12',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-moisture-temperature-and-electrical-conductivity-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 3,\n values: [{name: 'dielectric_permittivity',\n displayName: 'Dielectric permittivity',\n convert: function (x) { return Math.pow(0.000000002887 * Math.pow(x[0]/10, 3) - 0.0000208 * Math.pow(x[0]/10, 2) + 0.05276 * (x[0]/10) - 43.39, 2); }},\n {name: 'volumetric_water_content',\n displayName: 'Volumetric water content',\n convert: function (x) { return x[0]/10 * 0.0003879 - 0.6956; },\n unit: 'm³⋅m⁻³'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'},\n {name: 'electrical_conductivity',\n displayName: 'Electrical conductivity',\n convert: function (x) { return x[2]; },\n unit: 'µS⋅cm⁻¹'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/ChirpStack/uplink/converter.json index b19412df..25c03688 100644 --- a/VENDORS/Decentlab/DL-TRS21/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/converter.json index 4f58087c..dbb57300 100644 --- a/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/ThingPark/uplink/converter.json index 04064359..b21f2912 100644 --- a/VENDORS/Decentlab/DL-TRS21/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/ThingParkEnterprise/uplink/converter.json index cd0cf4b6..6184d063 100644 --- a/VENDORS/Decentlab/DL-TRS21/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/ThingsStackCommunity/uplink/converter.json index 733a6340..f67d5142 100644 --- a/VENDORS/Decentlab/DL-TRS21/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-TRS21/ThingsStackIndustries/uplink/converter.json index 3d0dee2f..1177e1b4 100644 --- a/VENDORS/Decentlab/DL-TRS21/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-TRS21/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-TRS21',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'water_potential',\n displayName: 'Water potential',\n convert: function (x) { return -(x[0] / 10); },\n unit: 'kPa'},\n {name: 'soil_temperature',\n displayName: 'Soil temperature',\n convert: function (x) { return (x[1] - 32768) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/ChirpStack/uplink/converter.json index caa0bead..4f22d8f9 100644 --- a/VENDORS/Decentlab/DL-WRM/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/converter.json index a14a960b..91d69901 100644 --- a/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/ThingPark/uplink/converter.json index 01e8eaab..85b6b934 100644 --- a/VENDORS/Decentlab/DL-WRM/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/ThingParkEnterprise/uplink/converter.json index 680bfaef..c41c5821 100644 --- a/VENDORS/Decentlab/DL-WRM/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/ThingsStackCommunity/uplink/converter.json index a3f69549..267fc2f8 100644 --- a/VENDORS/Decentlab/DL-WRM/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-WRM/ThingsStackIndustries/uplink/converter.json index 35f4e5cb..e46d46d5 100644 --- a/VENDORS/Decentlab/DL-WRM/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-WRM/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-WRM',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/winter-road-maintenance-sensor-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 2,\n values: [{name: 'surface_temperature',\n displayName: 'Surface temperature',\n convert: function (x) { return (x[0] - 1000) / 10; },\n unit: '°C'},\n {name: 'head_temperature',\n displayName: 'Head temperature',\n convert: function (x) { return (x[1] - 1000) / 10; },\n unit: '°C'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/ChirpStack/uplink/converter.json index 4282ac6f..643bfa85 100644 --- a/VENDORS/Decentlab/DL-ZN1/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/converter.json index 49e0a408..6d027c43 100644 --- a/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/ThingPark/uplink/converter.json index 760a4d26..b1890ddf 100644 --- a/VENDORS/Decentlab/DL-ZN1/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/ThingParkEnterprise/uplink/converter.json index 17a2899f..ea03b6f1 100644 --- a/VENDORS/Decentlab/DL-ZN1/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/ThingsStackCommunity/uplink/converter.json index 6572bd4b..f1603546 100644 --- a/VENDORS/Decentlab/DL-ZN1/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ZN1/ThingsStackIndustries/uplink/converter.json index a72dab12..86683081 100644 --- a/VENDORS/Decentlab/DL-ZN1/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN1/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN1',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'dendrometer_position',\n displayName: 'Dendrometer position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/ChirpStack/uplink/converter.json index a5a7e0c1..62965e60 100644 --- a/VENDORS/Decentlab/DL-ZN2/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/converter.json index 4340e097..6d160fec 100644 --- a/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/ThingPark/uplink/converter.json index 75fb55c9..5dc4fccf 100644 --- a/VENDORS/Decentlab/DL-ZN2/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/ThingParkEnterprise/uplink/converter.json index 79eed79f..6dc8a976 100644 --- a/VENDORS/Decentlab/DL-ZN2/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/ThingsStackCommunity/uplink/converter.json index 395da264..56c424d4 100644 --- a/VENDORS/Decentlab/DL-ZN2/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-ZN2/ThingsStackIndustries/uplink/converter.json index 4184306b..7eac6593 100644 --- a/VENDORS/Decentlab/DL-ZN2/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-ZN2/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "const message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-ZN2',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/dendrometer-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 4,\n values: [{name: 'dendrometer_a_position',\n displayName: 'Dendrometer A Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - 1) * 20000; },\n unit: 'µm'},\n {name: 'dendrometer_b_position',\n displayName: 'Dendrometer B Position',\n convert: function (x) { return ((x[0] + x[1]*65536) / 8388608 - (x[2] + x[3]*65536) / 8388608) * 20000; },\n unit: 'µm'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file