Skip to content

Commit

Permalink
new decoder for cmi4160, fix for cmi4111, refactoring (#752)
Browse files Browse the repository at this point in the history
* Elvaco devices

* adding cmi4160 module

* add cmi4160 to index.yaml

* fix example result

* prettier formatting
  • Loading branch information
rgarmyn authored Mar 4, 2024
1 parent 2bd6bed commit 0d9430c
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 50 deletions.
7 changes: 4 additions & 3 deletions vendor/elvaco/cmi4110.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down Expand Up @@ -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));
19 changes: 19 additions & 0 deletions vendor/elvaco/cmi4111-codec.yaml
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions vendor/elvaco/cmi4111-profile.yaml
Original file line number Diff line number Diff line change
@@ -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
97 changes: 75 additions & 22 deletions vendor/elvaco/cmi4111.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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}`);
}

Expand All @@ -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)) {
Expand All @@ -112,7 +163,7 @@ function decodeCMI4111Standard(payloadArr) {
decodedDictionary[unitInfo.measure] = value;
}

return standardPayload(decodedDictionary);
return decodedDictionary;
}

function bytesToHexArray(bytes) {
Expand All @@ -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);
});
}
Expand All @@ -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'],
Expand All @@ -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));
35 changes: 35 additions & 0 deletions vendor/elvaco/cmi4111.yaml
Original file line number Diff line number Diff line change
@@ -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
6 changes: 2 additions & 4 deletions vendor/elvaco/cmi4130.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
44 changes: 29 additions & 15 deletions vendor/elvaco/cmi4140-codec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions vendor/elvaco/cmi4140.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down Expand Up @@ -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));
Loading

0 comments on commit 0d9430c

Please sign in to comment.