Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Decentlab DL-SHT35-001 type and Add DL-SHT35-002 #168

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/validate-data-converters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ on:
- main
pull_request_target:
paths:
- 'VENDORS/**/uplink/**'
- 'VENDORS/**/downlink/**'
- 'VENDORS/**'

jobs:
build:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
"lora_dev_eui",
"lora_snr"
],
"decoder": "// WARNING: This script is designed to work with a remote JS executor. 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}"
"decoder": "// WARNING: This script is designed to work with a remote JS executor. 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-001',\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}"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"deviceName": "782",
"deviceType": "DL-SHT35",
"deviceType": "DL-SHT35-001",
"attributes": {
"lora_dev_eui": "70B3D57BA0003BB8",
"protocol_version": 2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"deviceName": "782",
"deviceType": "DL-SHT35",
"deviceType": "DL-SHT35-001",
"attributes": {
"lora_dev_eui": "70B3D57BA0003BB8",
"protocol_version": 2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"deviceName": "782",
"deviceType": "DL-SHT35",
"deviceType": "DL-SHT35-001",
"attributes": {
"lora_dev_eui": "0202020202020202",
"protocol_version": 2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"deviceName": "782",
"deviceType": "DL-SHT35",
"deviceType": "DL-SHT35-001",
"attributes": {
"lora_dev_eui": "0202020202020202",
"protocol_version": 2
Expand Down
Loading