Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion admin/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,7 @@
"chars": "Zeichen",
"connected": "verbunden",
"ms": "ms",
"rejectUnauthorized": "Nicht autorisierte Server ablehnen"
"rejectUnauthorized": "Nicht autorisierte Server ablehnen",
"Parse comma-separated numbers as character codes": "Komma-getrennte Zahlen als Zeichencodes interpretieren",
"Convert comma-separated numbers like '72,101,108,108,111' to text strings like 'Hello'. Only values 0-255 are supported.": "Konvertiere komma-getrennte Zahlen wie '72,101,108,108,111' in Textstrings wie 'Hello'. Nur Werte 0-255 werden unterstützt."
}
4 changes: 3 additions & 1 deletion admin/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,7 @@
"chars": "chars",
"connected": "connected",
"ms": "ms",
"rejectUnauthorized": "Reject unauthorized servers"
"rejectUnauthorized": "Reject unauthorized servers",
"Parse comma-separated numbers as character codes": "Parse comma-separated numbers as character codes",
"Convert comma-separated numbers like '72,101,108,108,111' to text strings like 'Hello'. Only values 0-255 are supported.": "Convert comma-separated numbers like '72,101,108,108,111' to text strings like 'Hello'. Only values 0-255 are supported."
}
10 changes: 10 additions & 0 deletions admin/jsonConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,16 @@
"md": 6,
"lg": 4,
"xl": 4
},
"parseCharCodes": {
"type": "checkbox",
"label": "Parse comma-separated numbers as character codes",
"tooltip": "Convert comma-separated numbers like '72,101,108,108,111' to text strings like 'Hello'. Only values 0-255 are supported.",
"xs": 12,
"sm": 12,
"md": 6,
"lg": 4,
"xl": 4
}
}
}
Expand Down
21 changes: 18 additions & 3 deletions lib/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,29 @@ function convertMessage(topic, message, adapter, clientID) {
}

// try to convert 101,124,444,... To utf8 string
if (type === 'string' && message.match(/^(\d)+,\s?(\d)+,\s?(\d)+/)) {
if (type === 'string' && adapter.config.parseCharCodes && message.match(/^(\d)+,\s?(\d)+,\s?(\d)+/)) {
const parts = message.split(',');
try {
let str = '';
let allValidCharCodes = true;

// Build string and validate character codes in one pass
for (let p = 0; p < parts.length; p++) {
str += String.fromCharCode(parseInt(parts[p].trim(), 10));
const charCode = parseInt(parts[p].trim(), 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot To prevent double "parseInt" there should be no issue to start to build the strig as long as characters as value but just stop that at the first invalid char. Only when all chars were valid you use the build string. So you prevent duplicate logic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimized the implementation to build the string and validate character codes in a single pass. Now only one parseInt call per part is made, and the loop stops immediately at the first invalid character code. The converted string is only used if all parts were valid. (commit 75aeaaa)

// Only allow values that work reliably with String.fromCharCode (0-255 for extended ASCII)
if (charCode < 0 || charCode > 255) {
allValidCharCodes = false;
break;
}
str += String.fromCharCode(charCode);
}

// Only use the converted string if all parts were valid character codes
if (allValidCharCodes) {
message = str;
// Character code conversion complete - don't try to parse as number later
return message;
}
message = str;
} catch {
// cannot convert and ignore it
}
Expand Down
51 changes: 50 additions & 1 deletion test/testConvert.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,57 @@
const expect = require('chai').expect;
const convertMessage = require('../lib/common').convertMessage;

const adapterMockWithCharCodes = { config: { parseCharCodes: true } };
const adapterMockWithoutCharCodes = { config: { parseCharCodes: false } };

describe('Test convert version', function () {
it('Number', async () => {
expect(convertMessage('data', '233.57')).to.be.equal(233.57);
expect(convertMessage('data', '233.57', adapterMockWithoutCharCodes)).to.be.equal(233.57);
});

it('Character code conversion - enabled - valid ASCII codes', async () => {
expect(convertMessage('data', '65,66,67', adapterMockWithCharCodes)).to.be.equal('ABC');
expect(convertMessage('data', '72,101,108,108,111', adapterMockWithCharCodes)).to.be.equal('Hello');
expect(convertMessage('data', '32,87,111,114,108,100', adapterMockWithCharCodes)).to.be.equal(' World');
});

it('Character code conversion - disabled - no conversion should happen', async () => {
expect(convertMessage('data', '65,66,67', adapterMockWithoutCharCodes)).to.be.equal('65,66,67');
expect(convertMessage('data', '72,101,108,108,111', adapterMockWithoutCharCodes)).to.be.equal('72,101,108,108,111');
expect(convertMessage('data', '32,87,111,114,108,100', adapterMockWithoutCharCodes)).to.be.equal('32,87,111,114,108,100');
});

it('Character code conversion - invalid codes should remain unchanged', async () => {
// NUKI 3.0 Pro lock data - should NOT be converted (even when enabled)
expect(convertMessage('data', '3,0,442236930,1,2', adapterMockWithCharCodes)).to.be.equal('3,0,442236930,1,2');

// Values above 255 should NOT be converted (even when enabled)
expect(convertMessage('data', '256,512,1024', adapterMockWithCharCodes)).to.be.equal('256,512,1024');
expect(convertMessage('data', '70000,80000,90000', adapterMockWithCharCodes)).to.be.equal('70000,80000,90000');

// Negative numbers should NOT be converted (even when enabled)
expect(convertMessage('data', '-1,2,3', adapterMockWithCharCodes)).to.be.equal('-1,2,3');

// Mix of valid and invalid should NOT be converted (even when enabled)
expect(convertMessage('data', '0,65535,1114112', adapterMockWithCharCodes)).to.be.equal('0,65535,1114112');
});

it('Character code conversion - edge cases', async () => {
// Control characters (0-31) are valid when enabled
expect(convertMessage('data', '9,10,13', adapterMockWithCharCodes)).to.be.equal('\t\n\r');

// Extended ASCII (128-255) is valid when enabled
expect(convertMessage('data', '128,129,130', adapterMockWithCharCodes)).to.be.equal('\x80\x81\x82');

// Single number doesn't match pattern (requires at least 3 numbers) but may be converted to number
expect(convertMessage('data', '65', adapterMockWithCharCodes)).to.be.equal(65); // String '65' becomes number 65
expect(convertMessage('data', '65,66', adapterMockWithCharCodes)).to.be.equal(65.66); // Two numbers: '65,66' -> '65.66' -> 65.66
});

it('Character code conversion - non-numeric data unchanged', async () => {
expect(convertMessage('data', 'hello,world', adapterMockWithCharCodes)).to.be.equal('hello,world');
expect(convertMessage('data', 'a,b,c', adapterMockWithCharCodes)).to.be.equal('a,b,c');
expect(convertMessage('data', '1,2,abc', adapterMockWithCharCodes)).to.be.equal('1,2,abc');
expect(convertMessage('data', '3.14,2.71,1.41', adapterMockWithCharCodes)).to.be.equal('3.14,2.71,1.41');
});
});