diff --git a/decoders/connector/ellenex/dus2-l/assets/logo.png b/decoders/connector/ellenex/dus2-l/assets/logo.png new file mode 100644 index 00000000..0e0e1eb5 Binary files /dev/null and b/decoders/connector/ellenex/dus2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/dus2-l/connector.jsonc b/decoders/connector/ellenex/dus2-l/connector.jsonc new file mode 100644 index 00000000..00b171ac --- /dev/null +++ b/decoders/connector/ellenex/dus2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex DUS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/dus2-l/description.md b/decoders/connector/ellenex/dus2-l/description.md new file mode 100644 index 00000000..9a8b84f9 --- /dev/null +++ b/decoders/connector/ellenex/dus2-l/description.md @@ -0,0 +1 @@ +LoRaWAN low power ultrasonic level sensor for liquid or solid media \ No newline at end of file diff --git a/decoders/connector/ellenex/dus2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/dus2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..08b1e93e --- /dev/null +++ b/decoders/connector/ellenex/dus2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/ellenex/dus2-l/v1.0.0/payload.js b/decoders/connector/ellenex/dus2-l/v1.0.0/payload.js new file mode 100644 index 00000000..82a831d4 --- /dev/null +++ b/decoders/connector/ellenex/dus2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "01880009CD078F22" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '01880009CD078F22' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const tank_height = Number(device.params.find(param => param.key === 'tank_height').value); + if (!tank_height && typeof tank_height !== 'number') throw 'Missing "tank_height" key in the configuration parameters'; + + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const distance = bytes.readUIntBE(3, 2); + const level = tank_height - bytes.readUIntBE(3, 2); + + data.push( + { variable: 'distance', value: distance, unit: 'mm' }, + { variable: 'level', value: level, unit: 'mm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. +// Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/fms2-l/assets/logo.png b/decoders/connector/ellenex/fms2-l/assets/logo.png new file mode 100644 index 00000000..83283492 Binary files /dev/null and b/decoders/connector/ellenex/fms2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/fms2-l/connector.jsonc b/decoders/connector/ellenex/fms2-l/connector.jsonc new file mode 100644 index 00000000..8af2d484 --- /dev/null +++ b/decoders/connector/ellenex/fms2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex FMS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/fms2-l/description.md b/decoders/connector/ellenex/fms2-l/description.md new file mode 100644 index 00000000..b7b5e352 --- /dev/null +++ b/decoders/connector/ellenex/fms2-l/description.md @@ -0,0 +1 @@ +LoRaWAN low power pulse counter and analogue interface for the water meter and flowmeter totalising for usage and leakage monitoring \ No newline at end of file diff --git a/decoders/connector/ellenex/fms2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/fms2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..84e3df21 --- /dev/null +++ b/decoders/connector/ellenex/fms2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/ellenex/fms2-l/v1.0.0/payload.js b/decoders/connector/ellenex/fms2-l/v1.0.0/payload.js new file mode 100644 index 00000000..4aa00d14 --- /dev/null +++ b/decoders/connector/ellenex/fms2-l/v1.0.0/payload.js @@ -0,0 +1,94 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "80162600005b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '80162600005b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + // Payload = 80 16 26 00 00 5b 22 24 + const pressure = bytes.readUIntBE(1, 2); + const pulse_counter = bytes.readUIntBE(3, 4); + const battery = bytes.readUIntBE(7, 1) * 0.1; + + + data.push( + { variable: 'pressure', value: pressure }, + { variable: 'pulse_counter', value: pulse_counter }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pds2-l/assets/logo.png b/decoders/connector/ellenex/pds2-l/assets/logo.png new file mode 100644 index 00000000..715fa1b3 Binary files /dev/null and b/decoders/connector/ellenex/pds2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pds2-l/connector.jsonc b/decoders/connector/ellenex/pds2-l/connector.jsonc new file mode 100644 index 00000000..3611f310 --- /dev/null +++ b/decoders/connector/ellenex/pds2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PDS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pds2-l/description.md b/decoders/connector/ellenex/pds2-l/description.md new file mode 100644 index 00000000..2fa821c1 --- /dev/null +++ b/decoders/connector/ellenex/pds2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Differential Pressure Transmitter for Liquid and Gas Media \ No newline at end of file diff --git a/decoders/connector/ellenex/pds2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pds2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..241472e6 --- /dev/null +++ b/decoders/connector/ellenex/pds2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids and gases compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pds2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pds2-l/v1.0.0/payload.js new file mode 100644 index 00000000..bb5af47e --- /dev/null +++ b/decoders/connector/ellenex/pds2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (K * bytes.readUIntBE(3, 2) * m) + b; + // ((K*Pressure sensor output (DEC)*m)+b) + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'pressure', value: pressure, unit: 'bar' }, + { variable: 'battery', value: battery, unit: 'V' }); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pdt2-l/assets/logo.png b/decoders/connector/ellenex/pdt2-l/assets/logo.png new file mode 100644 index 00000000..e6963790 Binary files /dev/null and b/decoders/connector/ellenex/pdt2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pdt2-l/connector.jsonc b/decoders/connector/ellenex/pdt2-l/connector.jsonc new file mode 100644 index 00000000..577e2e2c --- /dev/null +++ b/decoders/connector/ellenex/pdt2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PDT2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pdt2-l/description.md b/decoders/connector/ellenex/pdt2-l/description.md new file mode 100644 index 00000000..070e0de4 --- /dev/null +++ b/decoders/connector/ellenex/pdt2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Differential Pressure Transmitter With Built-in Temperature Sensor for Pressure and Flow Measurement of Gas Media \ No newline at end of file diff --git a/decoders/connector/ellenex/pdt2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pdt2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..bdab68ce --- /dev/null +++ b/decoders/connector/ellenex/pdt2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Pressure range of 500Pa (or others)\n* Wide range of industrial applications\n* High accuracy and Ultra-low power\n* Designed to meet outdoor applications\n* Compatible with air and\n* other non-conductive and non-corrosive Media", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/ellenex/pdt2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pdt2-l/v1.0.0/payload.js new file mode 100644 index 00000000..51ae40e8 --- /dev/null +++ b/decoders/connector/ellenex/pdt2-l/v1.0.0/payload.js @@ -0,0 +1,98 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const differential_pressure = bytes.readIntBE(3, 2); + // differential_pressure = sensor output + const temperature = bytes.readUIntBE(5, 2) / 100; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'differential_pressure', value: differential_pressure, unit: 'Pascal' }, + { variable: 'temperature', value: temperature, unit: 'C' }, + { variable: 'battery', value: battery, unit: 'V' }); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/plc2-l/assets/logo.png b/decoders/connector/ellenex/plc2-l/assets/logo.png new file mode 100644 index 00000000..56aaba75 Binary files /dev/null and b/decoders/connector/ellenex/plc2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/plc2-l/connector.jsonc b/decoders/connector/ellenex/plc2-l/connector.jsonc new file mode 100644 index 00000000..5709a521 --- /dev/null +++ b/decoders/connector/ellenex/plc2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLC2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/plc2-l/description.md b/decoders/connector/ellenex/plc2-l/description.md new file mode 100644 index 00000000..f0222950 --- /dev/null +++ b/decoders/connector/ellenex/plc2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Level Transmitter for Corrosive Liquid Media \ No newline at end of file diff --git a/decoders/connector/ellenex/plc2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/plc2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..1e56b2e4 --- /dev/null +++ b/decoders/connector/ellenex/plc2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,36 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for corrosive liquids", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "liquid_density", + "label": "Liquid density", + "type": "number" + }, + { + "name": "sensor_range", + "label": "Sensor range", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/plc2-l/v1.0.0/payload.js b/decoders/connector/ellenex/plc2-l/v1.0.0/payload.js new file mode 100644 index 00000000..4449b905 --- /dev/null +++ b/decoders/connector/ellenex/plc2-l/v1.0.0/payload.js @@ -0,0 +1,102 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + const sensor_range = Number(device.params.find(param => param.key === 'sensor_range').value); + if (!sensor_range && typeof sensor_range !== 'number') throw 'Missing "sensor_range" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const level = (sensor_range * ((bytes.readUIntBE(3, 2) - 4000) / 16000)) / liquid_density; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pld2-l/assets/logo.png b/decoders/connector/ellenex/pld2-l/assets/logo.png new file mode 100644 index 00000000..0e102551 Binary files /dev/null and b/decoders/connector/ellenex/pld2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pld2-l/connector.jsonc b/decoders/connector/ellenex/pld2-l/connector.jsonc new file mode 100644 index 00000000..1f183c81 --- /dev/null +++ b/decoders/connector/ellenex/pld2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLD2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pld2-l/description.md b/decoders/connector/ellenex/pld2-l/description.md new file mode 100644 index 00000000..127a8bd6 --- /dev/null +++ b/decoders/connector/ellenex/pld2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Level Transmitter for Liquid with Built-in Temperature Sensor \ No newline at end of file diff --git a/decoders/connector/ellenex/pld2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pld2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..4f713d28 --- /dev/null +++ b/decoders/connector/ellenex/pld2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,43 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + }, + { + "name": "liquid_density", + "type": "number", + "label": "Liquid density" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pld2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pld2-l/v1.0.0/payload.js new file mode 100644 index 00000000..0d06158e --- /dev/null +++ b/decoders/connector/ellenex/pld2-l/v1.0.0/payload.js @@ -0,0 +1,110 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const level = ((K * bytes.readUIntBE(3, 2) * m) + b) / liquid_density; + // ((K*pressure sensor output (DEC)*m)+b) / liquid density + const temperature = bytes.readUIntBE(5, 2) / 100; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'temperature', value: temperature, unit: 'C' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/plm2-l/assets/logo.png b/decoders/connector/ellenex/plm2-l/assets/logo.png new file mode 100644 index 00000000..e28384cb Binary files /dev/null and b/decoders/connector/ellenex/plm2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/plm2-l/connector.jsonc b/decoders/connector/ellenex/plm2-l/connector.jsonc new file mode 100644 index 00000000..44ce38a2 --- /dev/null +++ b/decoders/connector/ellenex/plm2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLM2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/plm2-l/description.md b/decoders/connector/ellenex/plm2-l/description.md new file mode 100644 index 00000000..1269d31a --- /dev/null +++ b/decoders/connector/ellenex/plm2-l/description.md @@ -0,0 +1 @@ +Low power level transmitter with a narrow (15.8mm) sensor head diameter for liquid media compatible with stainless steel (groundwater, etc.) \ No newline at end of file diff --git a/decoders/connector/ellenex/plm2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/plm2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..87375158 --- /dev/null +++ b/decoders/connector/ellenex/plm2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,36 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "sensor_range", + "label": "Sensor range", + "type": "number" + }, + { + "name": "liquid_density", + "label": "Liquid density", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/plm2-l/v1.0.0/payload.js b/decoders/connector/ellenex/plm2-l/v1.0.0/payload.js new file mode 100644 index 00000000..cf7c35d8 --- /dev/null +++ b/decoders/connector/ellenex/plm2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + const sensor_range = Number(device.params.find(param => param.key === 'sensor_range').value); + if (!sensor_range && typeof sensor_range !== 'number') throw 'Missing "sensor_range" key in the configuration parameters'; + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const level = (sensor_range * ((bytes.readUIntBE(3, 2) - 4000) / 16000)) / liquid_density; + // (Sensor range * ((Pressure sensor output(DEC)- 4000) / 16000))/ Liquid density + const battery = bytes.readUIntBE(7, 1) * 0.1; + + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pls2-l/assets/logo.png b/decoders/connector/ellenex/pls2-l/assets/logo.png new file mode 100644 index 00000000..25185249 Binary files /dev/null and b/decoders/connector/ellenex/pls2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pls2-l/connector.jsonc b/decoders/connector/ellenex/pls2-l/connector.jsonc new file mode 100644 index 00000000..279a6135 --- /dev/null +++ b/decoders/connector/ellenex/pls2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pls2-l/description.md b/decoders/connector/ellenex/pls2-l/description.md new file mode 100644 index 00000000..b911f83c --- /dev/null +++ b/decoders/connector/ellenex/pls2-l/description.md @@ -0,0 +1 @@ +Low Power Submersible Level Sensor for Tank and Storage Level Monitoring over LoRaWAN or Satellite \ No newline at end of file diff --git a/decoders/connector/ellenex/pls2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pls2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..1bc656c8 --- /dev/null +++ b/decoders/connector/ellenex/pls2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,44 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies and Satellite\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + }, + { + "name": "liquid_density", + "type": "number", + "label": "Liquid density" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/myriota/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pls2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pls2-l/v1.0.0/payload.js new file mode 100644 index 00000000..c32ce8ee --- /dev/null +++ b/decoders/connector/ellenex/pls2-l/v1.0.0/payload.js @@ -0,0 +1,116 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "018500020C079022" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '018500020C079022' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + // const device = { + // params: [ + // { key: 'K', value: 0.019 }, + // { key: 'm', value: 0.1 }, + // { key: 'b', value: 0 }, + // { key: 'liquid_density', value: 1 }, + // ], + // }; + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + + // Payload = 018500020C079022 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const level = ((K * bytes.readUIntBE(3, 2) * m) + b) / liquid_density; + // ((K*Pressure sensor output (DEC)*m)+b)/liquid density + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pls3-l/assets/logo.png b/decoders/connector/ellenex/pls3-l/assets/logo.png new file mode 100644 index 00000000..f2d63a87 Binary files /dev/null and b/decoders/connector/ellenex/pls3-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pls3-l/connector.jsonc b/decoders/connector/ellenex/pls3-l/connector.jsonc new file mode 100644 index 00000000..0d6d3717 --- /dev/null +++ b/decoders/connector/ellenex/pls3-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLS3-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pls3-l/description.md b/decoders/connector/ellenex/pls3-l/description.md new file mode 100644 index 00000000..740ec6bc --- /dev/null +++ b/decoders/connector/ellenex/pls3-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated IP68 Low Power Level Transmitter for Liquid Media \ No newline at end of file diff --git a/decoders/connector/ellenex/pls3-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pls3-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..ea9721be --- /dev/null +++ b/decoders/connector/ellenex/pls3-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,48 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids compatible with SS and Hastelloy", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + }, + { + "name": "liquid_density", + "type": "number", + "default": "Liquid density" + }, + { + "name": "sensor_range", + "type": "number", + "default": "Sensor range" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pls3-l/v1.0.0/payload.js b/decoders/connector/ellenex/pls3-l/v1.0.0/payload.js new file mode 100644 index 00000000..e8dd2f2d --- /dev/null +++ b/decoders/connector/ellenex/pls3-l/v1.0.0/payload.js @@ -0,0 +1,111 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "00 f9 00 09 d3 1f 94 22" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '00 f9 00 09 d3 1f 94 22' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + const sensor_range = Number(device.params.find(param => param.key === 'sensor_range').value); + if (!sensor_range && typeof sensor_range !== 'number') throw 'Missing "sensor_range" key in the configuration parameters'; + + // Payload = 00 f9 00 09 d3 1f 94 22 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const l1 = (bytes.readUIntBE(5, 2) - 1638.3) * sensor_range / 13106.4; + const l2 = (K * bytes.readUIntBE(3, 2) * m) + b; + console.log(l1, l2); + const level = ((l1 - (l2 * 10)) / liquid_density); + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/plv2-l/assets/logo.png b/decoders/connector/ellenex/plv2-l/assets/logo.png new file mode 100644 index 00000000..afbf9eaa Binary files /dev/null and b/decoders/connector/ellenex/plv2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/plv2-l/connector.jsonc b/decoders/connector/ellenex/plv2-l/connector.jsonc new file mode 100644 index 00000000..1dc28fcc --- /dev/null +++ b/decoders/connector/ellenex/plv2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PLV2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/plv2-l/description.md b/decoders/connector/ellenex/plv2-l/description.md new file mode 100644 index 00000000..936000c3 --- /dev/null +++ b/decoders/connector/ellenex/plv2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated IP68 Low Power Floating Level Transmitter for Water Resource Management \ No newline at end of file diff --git a/decoders/connector/ellenex/plv2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/plv2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..156bd841 --- /dev/null +++ b/decoders/connector/ellenex/plv2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,53 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy and Ultra-low power\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Designed for Water Resource Level and Volume\n* Measurement", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + }, + { + "name": "liquid_density", + "label": "Liquid density", + "type": "number" + }, + { + "name": "sensor_range", + "label": "Sensor range", + "type": "number" + }, + { + "name": "buoy_depth", + "label": "Buoy depth", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/plv2-l/v1.0.0/payload.js b/decoders/connector/ellenex/plv2-l/v1.0.0/payload.js new file mode 100644 index 00000000..3fcd77c1 --- /dev/null +++ b/decoders/connector/ellenex/plv2-l/v1.0.0/payload.js @@ -0,0 +1,113 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "00 f9 00 09 d3 1f 94 22" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '00 f9 00 09 d3 1f 94 22' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + const liquid_density = Number(device.params.find(param => param.key === 'liquid_density').value); + if (!liquid_density && typeof liquid_density !== 'number') throw 'Missing "liquid_density" key in the configuration parameters'; + const sensor_range = Number(device.params.find(param => param.key === 'sensor_range').value); + if (!sensor_range && typeof sensor_range !== 'number') throw 'Missing "sensor_range" key in the configuration parameters'; + const buoy_depth = Number(device.params.find(param => param.key === 'buoy_depth').value); + if (!buoy_depth && typeof buoy_depth !== 'number') throw 'Missing "buoy_depth" key in the configuration parameters'; + + + // Payload = 00 f9 00 09 d3 1f 94 22 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const l1 = (bytes.readUIntBE(5, 2) - 1638.3) * sensor_range / 13106.4; + const l2 = (K * bytes.readUIntBE(3, 2) * m) + b; + const level = ((l1 - (l2 * 10)) / liquid_density) + buoy_depth; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push( + { variable: 'level', value: level, unit: 'm' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/ptc2-l/assets/logo.png b/decoders/connector/ellenex/ptc2-l/assets/logo.png new file mode 100644 index 00000000..a807740d Binary files /dev/null and b/decoders/connector/ellenex/ptc2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/ptc2-l/connector.jsonc b/decoders/connector/ellenex/ptc2-l/connector.jsonc new file mode 100644 index 00000000..a6a07da0 --- /dev/null +++ b/decoders/connector/ellenex/ptc2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PTC2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/ptc2-l/description.md b/decoders/connector/ellenex/ptc2-l/description.md new file mode 100644 index 00000000..00113213 --- /dev/null +++ b/decoders/connector/ellenex/ptc2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Pressure Transmitter for Corrosive Liquid and Gas Media \ No newline at end of file diff --git a/decoders/connector/ellenex/ptc2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/ptc2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..d77d32f6 --- /dev/null +++ b/decoders/connector/ellenex/ptc2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for corrosive liquids and gases", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/ptc2-l/v1.0.0/payload.js b/decoders/connector/ellenex/ptc2-l/v1.0.0/payload.js new file mode 100644 index 00000000..bb5af47e --- /dev/null +++ b/decoders/connector/ellenex/ptc2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (K * bytes.readUIntBE(3, 2) * m) + b; + // ((K*Pressure sensor output (DEC)*m)+b) + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'pressure', value: pressure, unit: 'bar' }, + { variable: 'battery', value: battery, unit: 'V' }); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/ptd2-l/assets/logo.png b/decoders/connector/ellenex/ptd2-l/assets/logo.png new file mode 100644 index 00000000..9e99bec2 Binary files /dev/null and b/decoders/connector/ellenex/ptd2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/ptd2-l/connector.jsonc b/decoders/connector/ellenex/ptd2-l/connector.jsonc new file mode 100644 index 00000000..dea40b3d --- /dev/null +++ b/decoders/connector/ellenex/ptd2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PTD2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/ptd2-l/description.md b/decoders/connector/ellenex/ptd2-l/description.md new file mode 100644 index 00000000..aae98165 --- /dev/null +++ b/decoders/connector/ellenex/ptd2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Pressure Transmitter for Liquid and Gas Media with Built-in Temperature Sensor \ No newline at end of file diff --git a/decoders/connector/ellenex/ptd2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/ptd2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..241472e6 --- /dev/null +++ b/decoders/connector/ellenex/ptd2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids and gases compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/ptd2-l/v1.0.0/payload.js b/decoders/connector/ellenex/ptd2-l/v1.0.0/payload.js new file mode 100644 index 00000000..ab28cf44 --- /dev/null +++ b/decoders/connector/ellenex/ptd2-l/v1.0.0/payload.js @@ -0,0 +1,107 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (K * bytes.readUIntBE(3, 2) * m) + b; + // ((K*Pressure sensor output (DEC)*m)+b) + const temperature = bytes.readUIntBE(5, 2) / 100; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push( + { variable: 'pressure', value: pressure, unit: 'bar' }, + { variable: 'temperature', value: temperature, unit: 'C' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { + // Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pte2-l/assets/logo.png b/decoders/connector/ellenex/pte2-l/assets/logo.png new file mode 100644 index 00000000..8a7784ef Binary files /dev/null and b/decoders/connector/ellenex/pte2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pte2-l/connector.jsonc b/decoders/connector/ellenex/pte2-l/connector.jsonc new file mode 100644 index 00000000..c2897942 --- /dev/null +++ b/decoders/connector/ellenex/pte2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PTE2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pte2-l/description.md b/decoders/connector/ellenex/pte2-l/description.md new file mode 100644 index 00000000..4955afdf --- /dev/null +++ b/decoders/connector/ellenex/pte2-l/description.md @@ -0,0 +1 @@ +LoRaWAN low power soil/earth pressure transmitter for land movement, pavement, and instruction overload liquid, sludge level monitoring \ No newline at end of file diff --git a/decoders/connector/ellenex/pte2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pte2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..e6c90204 --- /dev/null +++ b/decoders/connector/ellenex/pte2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,41 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "sensor_range", + "label": "Sensor range", + "type": "number" + }, + { + "name": "span", + "label": "Span", + "type": "number" + }, + { + "name": "zero", + "label": "Zero", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pte2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pte2-l/v1.0.0/payload.js new file mode 100644 index 00000000..476ba023 --- /dev/null +++ b/decoders/connector/ellenex/pte2-l/v1.0.0/payload.js @@ -0,0 +1,104 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "0145001631800021" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '0145001631800021' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const sensor_range = Number(device.params.find(param => param.key === 'sensor_range').value); + if (!sensor_range) throw 'Missing "sensor_range" key in the configuration parameters'; + const zero = Number(device.params.find(param => param.key === 'zero').value); + if (!zero) throw 'Missing "zero" key in the configuration parameters'; + const span = Number(device.params.find(param => param.key === 'span').value); + if (!span) throw 'Missing "span" key in the configuration parameters'; + + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (sensor_range * (bytes.readUIntBE(3, 2) - zero) / (span - zero)); + const battery = bytes.readUIntBE(7, 1) * 0.1; + + + data.push( + { variable: 'pressure', value: pressure, unit: 'Kg/cm²' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { +// Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/ptf2-l/assets/logo.png b/decoders/connector/ellenex/ptf2-l/assets/logo.png new file mode 100644 index 00000000..0802fb97 Binary files /dev/null and b/decoders/connector/ellenex/ptf2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/ptf2-l/connector.jsonc b/decoders/connector/ellenex/ptf2-l/connector.jsonc new file mode 100644 index 00000000..08cabed5 --- /dev/null +++ b/decoders/connector/ellenex/ptf2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PTF2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/ptf2-l/description.md b/decoders/connector/ellenex/ptf2-l/description.md new file mode 100644 index 00000000..36fd81d9 --- /dev/null +++ b/decoders/connector/ellenex/ptf2-l/description.md @@ -0,0 +1 @@ +Low Power Flush Type Pressure Transmitter for Liquid and Gas Media over LoRaWAN or Satellite \ No newline at end of file diff --git a/decoders/connector/ellenex/ptf2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/ptf2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..96b0400e --- /dev/null +++ b/decoders/connector/ellenex/ptf2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,39 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies and Satellite\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids and gases compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/myriota/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/ptf2-l/v1.0.0/payload.js b/decoders/connector/ellenex/ptf2-l/v1.0.0/payload.js new file mode 100644 index 00000000..b814b0c5 --- /dev/null +++ b/decoders/connector/ellenex/ptf2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (K * bytes.readUIntBE(3, 2) * m) + b; + // ((K*Pressure sensor output (DEC)*m)+b) + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'pressure', value: pressure, unit: 'bar' }, + { variable: 'battery', value: battery, unit: 'V' }); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { +// Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/pts2-l/assets/logo.png b/decoders/connector/ellenex/pts2-l/assets/logo.png new file mode 100644 index 00000000..4e52c697 Binary files /dev/null and b/decoders/connector/ellenex/pts2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/pts2-l/connector.jsonc b/decoders/connector/ellenex/pts2-l/connector.jsonc new file mode 100644 index 00000000..e0cabed7 --- /dev/null +++ b/decoders/connector/ellenex/pts2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex PTS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/pts2-l/description.md b/decoders/connector/ellenex/pts2-l/description.md new file mode 100644 index 00000000..92d9c036 --- /dev/null +++ b/decoders/connector/ellenex/pts2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Pressure Transmitter for Liquid and Gas Media \ No newline at end of file diff --git a/decoders/connector/ellenex/pts2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/pts2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..f23b71fa --- /dev/null +++ b/decoders/connector/ellenex/pts2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids and gases compatible with SS316", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [ + { + "name": "K", + "type": "number" + }, + { + "name": "m", + "type": "number" + }, + { + "name": "b", + "type": "number" + } + ], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} diff --git a/decoders/connector/ellenex/pts2-l/v1.0.0/payload.js b/decoders/connector/ellenex/pts2-l/v1.0.0/payload.js new file mode 100644 index 00000000..a686f250 --- /dev/null +++ b/decoders/connector/ellenex/pts2-l/v1.0.0/payload.js @@ -0,0 +1,103 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + const K = Number(device.params.find(param => param.key === 'K').value); + if (!K && typeof K !== 'number') throw 'Missing "K" key in the configuration parameters'; + const m = Number(device.params.find(param => param.key === 'm').value); + if (!m && typeof m !== 'number') throw 'Missing "m" key in the configuration parameters'; + const b = Number(device.params.find(param => param.key === 'b').value); + if (!b && typeof b !== 'number') throw 'Missing "b" key in the configuration parameters'; + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const pressure = (K * bytes.readUIntBE(3, 2) * m) + b; + // ((K*Pressure sensor output (DEC)*m)+b) + const battery = bytes.readUIntBE(7, 1) * 0.1; + + data.push({ variable: 'pressure', value: pressure, unit: 'bar' }, + { variable: 'battery', value: battery, unit: 'V' }); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { +// Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/decoders/connector/ellenex/tts2-l/assets/logo.png b/decoders/connector/ellenex/tts2-l/assets/logo.png new file mode 100644 index 00000000..ecd48014 Binary files /dev/null and b/decoders/connector/ellenex/tts2-l/assets/logo.png differ diff --git a/decoders/connector/ellenex/tts2-l/connector.jsonc b/decoders/connector/ellenex/tts2-l/connector.jsonc new file mode 100644 index 00000000..3e79fd83 --- /dev/null +++ b/decoders/connector/ellenex/tts2-l/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Ellenex TTS2-L", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/ellenex/tts2-l/description.md b/decoders/connector/ellenex/tts2-l/description.md new file mode 100644 index 00000000..b5fb0b1c --- /dev/null +++ b/decoders/connector/ellenex/tts2-l/description.md @@ -0,0 +1 @@ +LoRaWAN Operated Low Power Temperature Transmitter for Liquid and Gas Media \ No newline at end of file diff --git a/decoders/connector/ellenex/tts2-l/v1.0.0/payload-config.jsonc b/decoders/connector/ellenex/tts2-l/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..a4d15cb1 --- /dev/null +++ b/decoders/connector/ellenex/tts2-l/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "* Compatible with all LoRaWAN ® frequencies\n* Wide range of industrial applications\n* High accuracy\n* Designed to meet outdoor applications\n* Long-term durable performance in harsh environment\n* Ultra-low power\n* Suitable for liquids and gases compatible with SS", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/ellenex/tts2-l/v1.0.0/payload.js b/decoders/connector/ellenex/tts2-l/v1.0.0/payload.js new file mode 100644 index 00000000..65f98b71 --- /dev/null +++ b/decoders/connector/ellenex/tts2-l/v1.0.0/payload.js @@ -0,0 +1,98 @@ +/* This is an generic payload parser example. +** The code find the payload variable and parse it if exists. +** +** IMPORTANT: In most case, you will only need to edit the parsePayload function. +** +** Testing: +** You can do manual tests to this parse by using the Device Emulator. Copy and Paste the following code: +** [{ "variable": "payload", "value": "003f0016265b2224" }] +** +** The ignore_vars variable in this code should be used to ignore variables +** from the device that you don't want. +*/ + +// let payload = [{ variable: 'payload', value: '003f0016265b2224' }]; + +// Add ignorable variables in this array. +const ignore_vars = []; + +function toTagoFormat(object_item, serie, prefix = '') { + const result = []; + for (const key in object_item) { + if (ignore_vars.includes(key)) continue; + + if (typeof object_item[key] === 'object') { + result.push({ + variable: object_item[key].variable || `${prefix}${key}`.toLowerCase(), + value: object_item[key].value, + serie: object_item[key].serie || serie, + metadata: object_item[key].metadata, + location: object_item[key].location, + unit: object_item[key].unit, + }); + } else { + result.push({ + variable: `${prefix}${key}`.toLowerCase(), + value: object_item[key], + serie, + }); + } + } + + return result; +} + + +/** + * This is the main function to parse the payload. Everything else doesn't require your attention. + * @param {String} payload_raw + * @returns {Object} containing key and value to TagoIO + */ +function parsePayload(payload_raw) { + try { + const bytes = Buffer.from(payload_raw, 'hex'); + const data = []; + // Decode an uplink message from a buffer + // (array) of bytes to an object of fields. + + // Payload = 00 3f 00 16 26 5b 22 24 + const board_serial = bytes.readUIntBE(0, 2); + const sensor_reading_type = bytes.readUIntBE(2, 1); + data.push({ variable: 'board_serial', value: board_serial }, + { variable: 'sensor_reading_type', value: sensor_reading_type }); + if (sensor_reading_type === 0) { + const temperature = bytes.readUIntBE(5, 2) / 100; + const battery = bytes.readUIntBE(7, 1) * 0.1; + + + data.push( + { variable: 'temperature', value: temperature, unit: 'C' }, + { variable: 'battery', value: battery, unit: 'V' }, + ); + } + + + return data; + } catch (e) { + console.log(e); + // Return the variable parse_error for debugging. + return { parse_error: e.message }; + } +} +// Remove unwanted variables. +payload = payload.filter(x => !ignore_vars.includes(x.variable)); + +// Payload is an environment variable. Is where what is being inserted to your device comes in. +// Payload always is an array of objects. [ { variable, value...}, {variable, value...} ...] +const payload_raw = payload.find(x => x.variable === 'payload_raw' || x.variable === 'payload' || x.variable === 'data'); +if (payload_raw) { +// Get a unique serie for the incoming data. + const { value, time } = payload_raw; + let { serie } = payload_raw; + serie = new Date().getTime(); + + // Parse the payload_raw to JSON format (it comes in a String format) + if (value) { + payload = payload.concat(toTagoFormat(parsePayload(value.replace(/ /g, '')), serie)); + } +} diff --git a/schema/connector_details.json b/schema/connector_details.json index d204009b..a1217854 100644 --- a/schema/connector_details.json +++ b/schema/connector_details.json @@ -24,7 +24,7 @@ "type": "array", "description": "List of device parameters.", "items": { - "type": "string" + "type": "object" } }, "networks": { @@ -52,4 +52,4 @@ }, "required": ["description", "networks"], "additionalProperties": false -} \ No newline at end of file +}