From 0d9430c96edc10d713fce36761e4a85a81ba8eb1 Mon Sep 17 00:00:00 2001 From: Ruben Garmyn <79699739+rgarmyn@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:18:55 +0100 Subject: [PATCH] new decoder for cmi4160, fix for cmi4111, refactoring (#752) * Elvaco devices * adding cmi4160 module * add cmi4160 to index.yaml * fix example result * prettier formatting --- vendor/elvaco/cmi4110.js | 7 +- vendor/elvaco/cmi4111-codec.yaml | 19 ++++ vendor/elvaco/cmi4111-profile.yaml | 49 ++++++++++ vendor/elvaco/cmi4111.js | 97 ++++++++++++++----- vendor/elvaco/cmi4111.yaml | 35 +++++++ vendor/elvaco/cmi4130.js | 6 +- vendor/elvaco/cmi4140-codec.yaml | 44 ++++++--- vendor/elvaco/cmi4140.js | 8 +- vendor/elvaco/cmi4160-codec.yaml | 25 +++++ vendor/elvaco/cmi4160-profile.yaml | 49 ++++++++++ vendor/elvaco/cmi4160.jpeg | Bin 0 -> 2839 bytes vendor/elvaco/cmi4160.js | 145 +++++++++++++++++++++++++++++ vendor/elvaco/cmi4160.yaml | 35 +++++++ vendor/elvaco/index.yaml | 4 +- 14 files changed, 473 insertions(+), 50 deletions(-) create mode 100644 vendor/elvaco/cmi4111-codec.yaml create mode 100644 vendor/elvaco/cmi4111-profile.yaml create mode 100644 vendor/elvaco/cmi4111.yaml create mode 100644 vendor/elvaco/cmi4160-codec.yaml create mode 100644 vendor/elvaco/cmi4160-profile.yaml create mode 100644 vendor/elvaco/cmi4160.jpeg create mode 100644 vendor/elvaco/cmi4160.js create mode 100644 vendor/elvaco/cmi4160.yaml diff --git a/vendor/elvaco/cmi4110.js b/vendor/elvaco/cmi4110.js index 54ba1a2407..6b768610a1 100644 --- a/vendor/elvaco/cmi4110.js +++ b/vendor/elvaco/cmi4110.js @@ -119,7 +119,8 @@ function bytesToHexArray(bytes) { } function hexToBytes(hex) { - return hex.map(function (byte) { + const hexArray = hex.match(/.{1,2}/g); + return hexArray.map(function (byte) { return parseInt(byte, 16); }); } @@ -153,6 +154,6 @@ function decodeUplink(input) { // array= 000c06526761020c14978999000b2d0000000b3b0000000a5a33060a5e41050c782911036602fd170000 // hex = ["00","0c","06","52","67","61","02","0c","14","97","89","99","00","0b","2d","00","00","00","0b","3b","00","00","00","0a","5a","33","06","0a","5e","41","05","0c","78","29","11","03","66","02","fd","17","00","00"] -bytes = [0, 12, 6, 82, 103, 97, 2, 12, 20, 151, 137, 153, 0, 11, 45, 0, 0, 0, 11, 59, 0, 0, 0, 10, 90, 51, 6, 10, 94, 65, 5, 12, 120, 41, 17, 3, 102, 2, 253, 23, 0, 0]; -input = { fPort: 2, bytes: bytes }; +// bytes = [0, 12, 6, 82, 103, 97, 2, 12, 20, 151, 137, 153, 0, 11, 45, 0, 0, 0, 11, 59, 0, 0, 0, 10, 90, 51, 6, 10, 94, 65, 5, 12, 120, 41, 17, 3, 102, 2, 253, 23, 0, 0]; +// input = { fPort: 2, bytes: bytes }; // console.log(decodeUplink(input)); diff --git a/vendor/elvaco/cmi4111-codec.yaml b/vendor/elvaco/cmi4111-codec.yaml new file mode 100644 index 0000000000..864be478e7 --- /dev/null +++ b/vendor/elvaco/cmi4111-codec.yaml @@ -0,0 +1,19 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: cmi4111.js + examples: + - description: Landis UH temperature, energy, volume, power, flow, flow_temperature, return_temperature, serial, and error flag. + input: + fPort: 2 + bytes: [5, 4, 6, 90, 38, 0, 0, 4, 20, 240, 20, 10, 0, 2, 45, 11, 0, 2, 59, 38, 0, 2, 90, 123, 2, 2, 94, 124, 1, 12, 120, 113, 53, 73, 105, 4, 253, 23, 0, 0, 8, 0] + output: + data: + energy: 9818, + volume: 6607.2, + power: 1.1, + flow: 0.038, + flow_temperature: 63.5, + return_temperature: 38, + serial_from_message: 69493571, + error_flag: 524288 diff --git a/vendor/elvaco/cmi4111-profile.yaml b/vendor/elvaco/cmi4111-profile.yaml new file mode 100644 index 0000000000..2b4a7e2241 --- /dev/null +++ b/vendor/elvaco/cmi4111-profile.yaml @@ -0,0 +1,49 @@ +vendorProfileID: 574 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: '1.0.2' +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: 'RP001-1.0.2' + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 16 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/elvaco/cmi4111.js b/vendor/elvaco/cmi4111.js index c17a6031c7..bcf07f1de3 100644 --- a/vendor/elvaco/cmi4111.js +++ b/vendor/elvaco/cmi4111.js @@ -1,35 +1,86 @@ const difVifMapping = { - '0c': { + 34: { + // value during error state + '03': { measure: '', unit: '', decimal: 0 }, + 13: { measure: '', unit: '', decimal: 0 }, + }, + '04': { + '00': { measure: 'energy', unit: 'kWh', decimal: 6 }, + '01': { measure: 'energy', unit: 'kWh', decimal: 5 }, + '02': { measure: 'energy', unit: 'kWh', decimal: 4 }, + '03': { measure: 'energy', unit: 'kWh', decimal: 3 }, + '04': { measure: 'energy', unit: 'kWh', decimal: 2 }, + '05': { measure: 'energy', unit: 'kWh', decimal: 1 }, '06': { measure: 'energy', unit: 'kWh', decimal: 0 }, '07': { measure: 'energy', unit: 'kWh', decimal: -1 }, - // "FB00": {"measure": "energy", "unit": "kWh", "decimal": -3}, - // "FB01": {"measure": "energy", "unit": "kWh", "decimal": -3}, + 10: { measure: 'volume', unit: 'm3', decimal: 6 }, + 11: { measure: 'volume', unit: 'm3', decimal: 5 }, + 12: { measure: 'volume', unit: 'm3', decimal: 4 }, + 13: { measure: 'volume', unit: 'm3', decimal: 3 }, 14: { measure: 'volume', unit: 'm3', decimal: 2 }, 15: { measure: 'volume', unit: 'm3', decimal: 1 }, 16: { measure: 'volume', unit: 'm3', decimal: 0 }, - 78: { measure: 'serial', unit: '', decimal: 0 }, + 17: { measure: 'volume', unit: 'm3', decimal: -1 }, + fd17: { measure: 'error_flag', unit: '', decimal: 0 }, + '6d': { measure: 'datetime_heat_meter', unit: '', decimal: 0 }, }, - '0b': { + '02': { + 29: { measure: 'power', unit: 'kW', decimal: 5 }, '2a': { measure: 'power', unit: 'kW', decimal: 4 }, '2b': { measure: 'power', unit: 'kW', decimal: 3 }, '2c': { measure: 'power', unit: 'kW', decimal: 2 }, '2d': { measure: 'power', unit: 'kW', decimal: 1 }, '2e': { measure: 'power', unit: 'kW', decimal: 0 }, '2f': { measure: 'power', unit: 'kW', decimal: -1 }, + 39: { measure: 'flow', unit: 'm3/h', decimal: 5 }, + '3a': { measure: 'flow', unit: 'm3/h', decimal: 4 }, '3b': { measure: 'flow', unit: 'm3/h', decimal: 3 }, '3c': { measure: 'flow', unit: 'm3/h', decimal: 2 }, '3d': { measure: 'flow', unit: 'm3/h', decimal: 1 }, '3e': { measure: 'flow', unit: 'm3/h', decimal: 0 }, '3f': { measure: 'flow', unit: 'm3/h', decimal: -1 }, - }, - '0a': { + 58: { measure: 'flow_temperature', unit: '°C', decimal: 3 }, + 59: { measure: 'flow_temperature', unit: '°C', decimal: 2 }, '5a': { measure: 'flow_temperature', unit: '°C', decimal: 1 }, '5b': { measure: 'flow_temperature', unit: '°C', decimal: 0 }, + '5c': { measure: 'return_temperature', unit: '°C', decimal: 3 }, + '5d': { measure: 'return_temperature', unit: '°C', decimal: 2 }, '5e': { measure: 'return_temperature', unit: '°C', decimal: 1 }, '5f': { measure: 'return_temperature', unit: '°C', decimal: 0 }, + fd17: { measure: 'error_flag', unit: '', decimal: 0 }, + }, + '0c': { 78: { measure: 'serial_from_message', unit: '', decimal: 0 } }, + 84: { + '0201': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 5 }, + '0202': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 4 }, + '0203': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 3 }, + '0204': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 2 }, + '0205': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 1 }, + '0206': { measure: 'energy_tariff_2', unit: 'kWh', decimal: 0 }, + '0207': { measure: 'energy_tariff_2', unit: 'kWh', decimal: -1 }, + 2001: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 5 }, + 2002: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 4 }, + 2003: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 3 }, + 2004: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 2 }, + 2005: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 1 }, + 2006: { measure: 'energy_tariff_2', unit: 'kWh', decimal: 0 }, + 2007: { measure: 'energy_tariff_2', unit: 'kWh', decimal: -1 }, + '0301': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 5 }, + '0302': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 4 }, + '0303': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 3 }, + '0304': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 2 }, + '0305': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 1 }, + '0306': { measure: 'energy_tariff_3', unit: 'kWh', decimal: 0 }, + '0307': { measure: 'energy_tariff_3', unit: 'kWh', decimal: -1 }, + 3001: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 5 }, + 3002: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 4 }, + 3003: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 3 }, + 3004: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 2 }, + 3005: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 1 }, + 3006: { measure: 'energy_tariff_3', unit: 'kWh', decimal: 0 }, + 3007: { measure: 'energy_tariff_3', unit: 'kWh', decimal: -1 }, + fd17: { measure: 'error_flag', unit: '', decimal: 0 }, }, - '02': { fd17: { measure: 'error_flag', unit: '', decimal: 0 } }, - '04': { fd17: { measure: 'error_flag', unit: '', decimal: 0 } }, }; function getVif(payloadArr, index) { @@ -61,17 +112,17 @@ function decodeCMI4111Standard(payloadArr) { while (i < payloadArr.length) { const dif = payloadArr[i]; - const vif = payloadArr[i + 1]; + let vif = payloadArr[i + 1]; const difInt = parseInt(dif, 16); i += 2; - const bcdLen = difInt >= 2 && difInt <= 4 ? difInt : 4; - if (payloadArr.slice(i).length <= 5 && vif === 'fd') { + //end of payload: error flag vif += payloadArr[i]; i += 1; } + const bcdLen = difInt >= 2 && difInt <= 4 ? difInt : 4; - if (!(dif in difVifMapping_CMI4111) || !(vif in difVifMapping_CMI4111[dif])) { + if (!(dif in difVifMapping) || !(vif in difVifMapping[dif])) { throw new Error(`Unknown dif ${dif} and vif ${vif}`); } @@ -87,7 +138,7 @@ function decodeCMI4111Standard(payloadArr) { .slice(i, i + bcdLen) .reverse() .join(''); // Little-endian (LSB) - const unitInfo = difVifMapping_CMI4111[dif][vif]; + const unitInfo = difVifMapping[dif][vif]; let valueInt; if (reversedValues.startsWith('fff') && ['power', 'flow'].includes(unitInfo.measure)) { @@ -112,7 +163,7 @@ function decodeCMI4111Standard(payloadArr) { decodedDictionary[unitInfo.measure] = value; } - return standardPayload(decodedDictionary); + return decodedDictionary; } function bytesToHexArray(bytes) { @@ -122,7 +173,8 @@ function bytesToHexArray(bytes) { } function hexToBytes(hex) { - return hex.map(function (byte) { + const hexArray = hex.match(/.{1,2}/g); + return hexArray.map(function (byte) { return parseInt(byte, 16); }); } @@ -131,13 +183,15 @@ function decodeUplink(input) { switch (input.fPort) { case 2: const hex_array = bytesToHexArray(input.bytes); + //print the hex array + console.log('hex_array', hex_array); if (hex_array.length < 40) { return { data: {}, errors: ['payload length < 40 '], }; } - if (hex_array[0] != '00') { + if (hex_array[0] != '05') { return { data: {}, errors: ['Payload type unknown, currently standard format supported'], @@ -154,8 +208,7 @@ function decodeUplink(input) { } } -// array= 000c06526761020c14978999000b2d0000000b3b0000000a5a33060a5e41050c782911036602fd170000 -// hex = ["00","0c","06","52","67","61","02","0c","14","97","89","99","00","0b","2d","00","00","00","0b","3b","00","00","00","0a","5a","33","06","0a","5e","41","05","0c","78","29","11","03","66","02","fd","17","00","00"] -bytes = [0, 12, 6, 82, 103, 97, 2, 12, 20, 151, 137, 153, 0, 11, 45, 0, 0, 0, 11, 59, 0, 0, 0, 10, 90, 51, 6, 10, 94, 65, 5, 12, 120, 41, 17, 3, 102, 2, 253, 23, 0, 0]; -input = { fPort: 2, bytes: bytes }; -console.log(decodeUplink(input)); +// array = '0504065a2600000414f0140a00022d0b00023b2600025a7b02025e7c010c787135496904fd1700000800'; +// bytes = [5, 4, 6, 90, 38, 0, 0, 4, 20, 240, 20, 10, 0, 2, 45, 11, 0, 2, 59, 38, 0, 2, 90, 123, 2, 2, 94, 124, 1, 12, 120, 113, 53, 73, 105, 4, 253, 23, 0, 0, 8, 0]; +// input = { fPort: 2, bytes: bytes }; +// console.log(decodeUplink(input)); diff --git a/vendor/elvaco/cmi4111.yaml b/vendor/elvaco/cmi4111.yaml new file mode 100644 index 0000000000..6e94459a88 --- /dev/null +++ b/vendor/elvaco/cmi4111.yaml @@ -0,0 +1,35 @@ +name: CMi4111 +description: Heat Meter Connectivity Module (MCM) for Landis+Gyr T230 +# Hardware versions (optional) +hardwareVersions: + - version: '1.0' + numeric: 1 +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Supported hardware versions (optional) + hardwareVersions: + - '1.0' # Must refer to hardwareVersions declared above + # LoRaWAN Device Profiles per region + # Supported regions: EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # When vendorID is empty, the profile is loaded from the current directory. + vendorID: elvaco + # Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters). + # This is the file name of the profile and must have the .yaml extension. + id: cmi4110-profile + # Specify whether the device is LoRa Alliance certified. + lorawanCertified: true + codec: cmi4110-codec + # US902-928: + # # This is the file name of the profile and must have the .yaml extension. + # id: custom-profile-us915 + # # Specify whether the device is LoRa Alliance certified. + # lorawanCertified: true + # # This is the file name of the codec defintion and must have the .yaml extension. + # codec: device-a-codec diff --git a/vendor/elvaco/cmi4130.js b/vendor/elvaco/cmi4130.js index 9205b3897e..8ed3bcd20f 100644 --- a/vendor/elvaco/cmi4130.js +++ b/vendor/elvaco/cmi4130.js @@ -140,9 +140,7 @@ function decodeUplink(input) { }; } } -// int(reversed_values, 16) // array= 150405b827bd3a04142cddb10d02290000023a000002598f26025d61160c784405817904fd1700000000 -// hex = ['0f', '04', '07', 'e1', '04', '02', '00', '04', '15', '11', 'a2', '4c', '00', '02', '2d', '3e', '00', '02', '3b', '0c', '03', '02', '5a', 'a6', '02', '02', '5e', '60', '02', '0c', '78', '19', '67', '90', '10', '02', 'fd', '17', '00', '00'] -bytes = [15, 4, 7, 225, 4, 2, 0, 4, 21, 17, 162, 76, 0, 2, 45, 62, 0, 2, 59, 12, 3, 2, 90, 166, 2, 2, 94, 96, 2, 12, 120, 25, 103, 144, 16, 2, 253, 23, 0, 0]; -input = { fPort: 2, bytes: bytes }; +// bytes = [15, 4, 7, 225, 4, 2, 0, 4, 21, 17, 162, 76, 0, 2, 45, 62, 0, 2, 59, 12, 3, 2, 90, 166, 2, 2, 94, 96, 2, 12, 120, 25, 103, 144, 16, 2, 253, 23, 0, 0]; +// input = { fPort: 2, bytes: bytes }; // console.log(decodeUplink(input)); diff --git a/vendor/elvaco/cmi4140-codec.yaml b/vendor/elvaco/cmi4140-codec.yaml index cb7e4ac67b..bc38072b37 100644 --- a/vendor/elvaco/cmi4140-codec.yaml +++ b/vendor/elvaco/cmi4140-codec.yaml @@ -2,18 +2,32 @@ # For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ uplinkDecoder: fileName: cmi4140.js -# examples: -# - description: Multical temperature, energy, volume, power, flow , flow_temperature, return_temperature, serial and error flag. -# input: -# fPort: 2 -# bytes: [0, 12, 6, 82, 103, 97, 2, 12, 20, 151,137, 153, 0, 11, 45, 0, 0, 0, 11, 59,0, 0, 0, 10, 90, 51, 6, 10, 94, 65, 5, 12, 120, 41, 17, 3, 102, 2, 253, 23,0, 0] -# output: -# data: -# energy: 98547500, -# volume: 2297603, -# power: 0, -# flow: 0, -# flow_temperature: 98.71, -# return_temperature: 57.29, -# serial: 79810544, -# error_flag: 0 + examples: + - description: Multical temperature, energy, volume, power, flow , flow_temperature, return_temperature, serial and error flag. + input: + fPort: 2 + bytes: [21, 4, 5, 252, 67, 127, 14, 4, 19, 64, 145, 152, 34, 2, 46, 144, 21, 2, 60, 72, 43, 2, 89, 216, 37, 2, 93, 232, 20, 12, 120, 39, 148, 129, 121, 4, 253, 23, 0, 0, 1, 0] + output: + data: + energy: 24322150 + volume: 580424 + power: 5520 + flow: 110.8 + flow_temperature: 96.88 + return_temperature: 53.52 + serial: 79819427 + error_flag: 10000 + - description: Second example + input: + fPort: 2 + bytes: [21, 4, 5, 184, 39, 189, 58, 4, 20, 44, 221, 177, 13, 2, 41, 0, 0, 2, 58, 0, 0, 2, 89, 143, 38, 2, 93, 97, 22, 12, 120, 68, 5, 129, 121, 4, 253, 23, 0, 0, 0, 0] + output: + data: + energy: 98547500 + volume: 2297603 + power: 0 + flow: 0 + flow_temperature: 98.71 + return_temperature: 57.29 + serial: 79810544 + error_flag: 0 diff --git a/vendor/elvaco/cmi4140.js b/vendor/elvaco/cmi4140.js index 4e1b7301bd..4599567862 100644 --- a/vendor/elvaco/cmi4140.js +++ b/vendor/elvaco/cmi4140.js @@ -104,7 +104,8 @@ function bytesToHexArray(bytes) { } function hexToBytes(hex) { - return hex.map(function (byte) { + const hexArray = hex.match(/.{1,2}/g); + return hexArray.map(function (byte) { return parseInt(byte, 16); }); } @@ -139,7 +140,6 @@ function decodeUplink(input) { } } -// payload = "150405b827bd3a04142cddb10d02290000023a000002598f26025d61160c784405817904fd1700000000" -bytes = [21, 4, 5, 184, 39, 189, 58, 4, 20, 44, 221, 177, 13, 2, 41, 0, 0, 2, 58, 0, 0, 2, 89, 143, 38, 2, 93, 97, 22, 12, 120, 68, 5, 129, 121, 4, 253, 23, 0, 0, 0, 0]; -input = { fPort: 2, bytes: bytes }; +// bytes = [21, 4, 5, 252, 67, 127, 14, 4, 19, 64, 145, 152, 34, 2, 46, 144, 21, 2, 60, 72, 43, 2, 89, 216, 37, 2, 93, 232, 20, 12, 120, 39, 148, 129, 121, 4, 253, 23, 0, 0, 1, 0]; +// input = { fPort: 2, bytes: bytes }; // console.log(decodeUplink(input)); diff --git a/vendor/elvaco/cmi4160-codec.yaml b/vendor/elvaco/cmi4160-codec.yaml new file mode 100644 index 0000000000..479e76f14e --- /dev/null +++ b/vendor/elvaco/cmi4160-codec.yaml @@ -0,0 +1,25 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: cmi4160.js + examples: + - description: Second example + input: + fPort: 2 + bytes: [30, 4, 6, 143, 161, 1, 0, 4, 19, 132, 183, 30, 0, 2, 43, 207, 15, 2, 59, 93, 0, 2, 90, 16, 3, 2, 94, 152, 1, 7, 121, 130, 37, 50, 105, 165, 17, 64, 4, 1, 253, 23, 0] + output: + data: + energy: 106895 + volume: 2013.06 + power: 4.047 + flow: 0.093 + flow_temperature: 78.4 + return_temperature: 40.8 + error_flag: 0 + serial: 69322582 + # - description: Value during error state + # input: + # fPort: 2 + # bytes : [30, 4, 7, 233, 28, 5, 0, 4, 21, 141, 103, 15, 0, 50, 47, 75, 51, 50, 61, 55, 51, 50, 90, 89, 4, 50, 94, 89, 4, 7, 121, 34, 152, 132, 97, 165, 17, 64, 4, 1, 253, 23, 4] + # output: + # data: diff --git a/vendor/elvaco/cmi4160-profile.yaml b/vendor/elvaco/cmi4160-profile.yaml new file mode 100644 index 0000000000..2b4a7e2241 --- /dev/null +++ b/vendor/elvaco/cmi4160-profile.yaml @@ -0,0 +1,49 @@ +vendorProfileID: 574 + +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: '1.0.2' +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0 or RP002-1.0.1 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: 'RP001-1.0.2' + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 16 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/elvaco/cmi4160.jpeg b/vendor/elvaco/cmi4160.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..020857d9862d36f9c5635bb5bcd05764a660acb5 GIT binary patch literal 2839 zcmcIj`#;lr82>VqqGMx7M`|d{Cbx!YmFSog850vaoRRBtnQKvUOX6&pC>)oWVdR!O z>9`yr*SX9sPI8HuNJ2a7{80S^=lQ%|pU?Yw-p}j#JkRs_Jls+4Yd{QRY+?-X@c{rI zZvotKAQBK3+9w8qh>1bOA!58kOq8cd|HK1OC|FWb5+V+jmQ^|?Eu(lu8hTtErl<@@ zB9T&ZnmU@QC?y0E&UfIzfkUE)q<#{EB4nY5;RrYi0Y@MZa2>ch9HGv`a5w_RGw{qP z1PcD2#@pd=)PEG5`y2r82WEg75FZTS2lIizeB4&x$k!AFc}ad30X}|^pwK=Zd0q_Q z=i?Uu2?z=87Zm)@gU5jdAjj1O&s)HL^C1g~YuwE)lgB#wmiOd1wZ6Cmko%3coY}}2GGEk*bg562jrh< zvn^!ATV&{kye-HZ@r`R7J)*k4&@lZ5XR`5$Y~Z|fb@-{`Tlk~9`tA=ZIBy-wJnC;X zeYx{%w9z?xxLubW8`Gg3YR%fqj#4)RJC1WIE)|kCo+p3=`?IZmEu?*?LKvLEbNZa9M@o zE{H#7t0!j}Ky<4Xk#d=YX)jin#OI@8|o_ zqNd3ixc9*P(9;kjgVcy^sr?A42$l2M?VR*Kn0gp>cU^qrYO&>#<58XM!S(P_mJg9b z(wF~qMc9LM6m`glvhH$&6QcGcH7>kw{`qD9zFDoT&*ZJiTit!FZ7WL6LTR0&g|YS| zzlhtnU$vZf`23_=ro%eQRoct~BR05OR9*`nk!|hR`=zU0eK(?x3s`??XdZSd*_)B$$#o9!8>r;Xo?zrEpEHG!Vc-7I=mPb z83tRy6tRciasiI3uxaeDomKkQQv?1w_6l;+z^?pbf0Ygr8q2^9`Xyxz7{ME*+?%DZxVt)|qyyou*@^X)-pyUYJVGnyhTPYhCfjEHLxi~UL{{&{-SP4oLpkcjlb5!&v>a0_GrBMWM;jg>3pO}zL7QXHsy(rq4bx#i*d5`NMe-r_&29~ZgH(e`ZJK>bp572G4tj`c9I46kAtzH37}Jgd zJ*LKCQY)9*9Zv`>v1F)=2y`umL~{)#5@xcgdAWf>Q!lraa)sSom;u#xckjs8+&atX+(z2`eHG+o}%fvSn&IK+i zwZ(3v9u&rCFfqCWiZ?bAiu!#8-njhUL;YxS|5dAOJFu%~-#xl{$1twzuv*<4!`49z z)2lS?Y@&_P`bB)hbjJtJjtZNsyX|I$Bt^l?wVgWc4rR!U;kme%Qdk*-TYMy?&a>}`p}QjGCaa7qf`Xj@+K2`OIsk66U#mLHN&~fb=y%gJP*;tFZs&$v;EjQLydgj4|)MDEH z8bXGV!b3~=Z(iytiHXR;NdGm%0}gJ5gbk6*lhY|#=`JK z=0d-dvzLVbXQr{mVvi&J>?Q1Myx(6&>!1XN+2aQfXwWpRNpuc%xarBVaqeF?+G#3f z8NcY6AqOxR>j006@-EV|I#eh;02kyb2a|OWspTsOihZ^@Sv}osXx<)#_?2*r-&Qdu zJC4M$q6ZY(snEwNuvPO-F_)2$kzn}2$gUjgwLZ~I$tXRaD|G3>2=*z9_^T0kdG)w> kRRRF;Qzcb^YNfK9{88t=Rq_q+Q= 2 && dif_int <= 4) || dif_int == 7) { + bcd_len = dif_int; + } + + if (!(dif in difVifMapping && vif in difVifMapping[dif])) { + throw 'Unknown dif ' + dif + ' and vif dif in difVifMapping' + vif; + } + const reversed_values = payloadArr + .slice(i, i + bcd_len) + .reverse() + .join(''); //Little-endian (LSB) + i += bcd_len; + unit_info = difVifMapping[dif][vif]; + if (unit_info['unit']) { + value = parseInt(reversed_values, 16) / Math.pow(10, unit_info['decimal']); + } else if (unit_info['measure'] == 'serial') { + value = parseInt(reversed_values.slice(-8)); // byte 2-5 is serial number + i += 1; + } else { + value = parseInt(reversed_values, 16); + } + if (dif == '32') { + decoded_dictionary[unit_info['measure']] = 0; + error_state = true; + } else { + decoded_dictionary[unit_info['measure']] = value; + } + } + if (error_state) { + decoded_dictionary['error_flag'] = 32; + } + return decoded_dictionary; +} + +function bytesToHexArray(bytes) { + return bytes.map(function (byte) { + return ('0' + (byte & 0xff).toString(16)).slice(-2); + }); +} + +function hexToBytes(hex) { + const hexArray = hex.match(/.{1,2}/g); + return hexArray.map(function (byte) { + return parseInt(byte, 16); + }); +} + +function decodeUplink(input) { + switch (input.fPort) { + case 2: + const hex_array = bytesToHexArray(input.bytes); + if (hex_array.length < 40) { + return { + data: {}, + errors: ['payload length < 40 '], + }; + } + if (hex_array[0] != '1e') { + return { + data: {}, + errors: ['Payload type unknown, currently standard format supported'], + }; + } + return { + data: decode_cmi4160_standard(hex_array), + }; + default: + return { + data: {}, + errors: ['unknown FPort'], + }; + } +} + +// bytes = [30, 4, 7, 233, 28, 5, 0, 4, 21, 141, 103, 15, 0, 50, 47, 75, 51, 50, 61, 55, 51, 50, 90, 89, 4, 50, 94, 89, 4, 7, 121, 34, 152, 132, 97, 165, 17, 64, 4, 1, 253, 23, 4]; +// bytes = [30, 4, 6, 143, 161, 1, 0, 4, 19, 132, 183, 30, 0, 2, 43, 207, 15, 2, 59, 93, 0, 2, 90, 16, 3, 2, 94, 152, 1, 7, 121, 130, 37, 50, 105, 165, 17, 64, 4, 1, 253, 23, 0]; +// input = { fPort: 2, bytes: bytes }; + +// console.log(decodeUplink(input)); diff --git a/vendor/elvaco/cmi4160.yaml b/vendor/elvaco/cmi4160.yaml new file mode 100644 index 0000000000..8d8d31d163 --- /dev/null +++ b/vendor/elvaco/cmi4160.yaml @@ -0,0 +1,35 @@ +name: CMi4130 +description: Heat Meter Connectivity Module (MCM) for Diehl Metering SHARKY 775 +# Hardware versions (optional) +hardwareVersions: + - version: '1.0' + numeric: 1 +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Supported hardware versions (optional) + hardwareVersions: + - '1.0' # Must refer to hardwareVersions declared above + # LoRaWAN Device Profiles per region + # Supported regions: EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + # Optional identifier of the vendor of the profile. When you specify the vendorID, the profile is loaded from + # the vendorID's folder. This allows you to reuse profiles from module or LoRaWAN end device stack vendors. + # When vendorID is empty, the profile is loaded from the current directory. + vendorID: elvaco + # Unique identifier of the profile (lowercase, alphanumeric with dashes, max 36 characters). + # This is the file name of the profile and must have the .yaml extension. + id: cmi4160-profile + # Specify whether the device is LoRa Alliance certified. + lorawanCertified: true + codec: cmi4160-codec + # US902-928: + # # This is the file name of the profile and must have the .yaml extension. + # id: custom-profile-us915 + # # Specify whether the device is LoRa Alliance certified. + # lorawanCertified: true + # # This is the file name of the codec defintion and must have the .yaml extension. + # codec: device-a-codec diff --git a/vendor/elvaco/index.yaml b/vendor/elvaco/index.yaml index 8f17944ed2..561c42fc9c 100644 --- a/vendor/elvaco/index.yaml +++ b/vendor/elvaco/index.yaml @@ -1,7 +1,7 @@ endDevices: # Unique identifier of the end device (lowercase, alphanumeric with dashes, max 36 characters) - cmi4110 - # - cmi4111 + - cmi4111 - cmi4130 - cmi4140 - # - cmi4160 + - cmi4160