From b5e70edf1afacc3bc43ad9aaa5e1c62e285ddbe3 Mon Sep 17 00:00:00 2001 From: Luca Barbetti Date: Tue, 23 Apr 2019 17:17:22 +0200 Subject: [PATCH 1/2] Add support for multi value properties --- src/index.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++- test/cbor.test.js | 28 ++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index a836f3e..041b8cd 100644 --- a/src/index.js +++ b/src/index.js @@ -404,6 +404,44 @@ const sendProperty = (thingId, name, value, timestamp) => { throw new Error('Name must be a valid string'); } + if (typeof value === 'object') { + const objectKeys = Object.keys(value); + const cborValues = objectKeys.map((key, i) => { + const cborValue = { + n: `${name}:${key}`, + }; + + if (i === 0) { + cborValue.bt = timestamp || new Date().getTime(); + } + + switch (typeof value[key]) { + case 'string': + cborValue.vs = value[key]; + break; + case 'number': + cborValue.v = value[key]; + break; + case 'boolean': + cborValue.vb = value[key]; + break; + default: + break; + } + + return cborValue; + }) + .map((cborValue) => { + if (connectionOptions.useCloudProtocolV2) { + return toCloudProtocolV2(cborValue); + } + + return cborValue; + }); + + return sendMessage(propertyInputTopic, CBOR.encode(cborValues, true)); + } + let cborValue = { bt: timestamp || new Date().getTime(), n: name, @@ -439,6 +477,49 @@ const getSenml = (deviceId, name, value, timestamp) => { throw new Error('Name must be a valid string'); } + + if (typeof value === 'object') { + const objectKeys = Object.keys(value); + const senMls = objectKeys.map((key, i) => { + const senMl = { + n: `${name}:${key}`, + }; + + if (i === 0) { + senMl.bt = timestamp || new Date().getTime(); + + if (deviceId) { + senMl.bn = `urn:uuid:${deviceId}`; + } + } + + switch (typeof value[key]) { + case 'string': + senMl.vs = value[key]; + break; + case 'number': + senMl.v = value[key]; + break; + case 'boolean': + senMl.vb = value[key]; + break; + default: + break; + } + + return senMl; + }) + .map((senMl) => { + if (connectionOptions.useCloudProtocolV2) { + return toCloudProtocolV2(senMl); + } + + return senMl; + }); + + return senMls; + } + const senMl = { bt: timestamp || new Date().getTime(), n: name, @@ -462,7 +543,6 @@ const getSenml = (deviceId, name, value, timestamp) => { break; } - if (connectionOptions.useCloudProtocolV2) { return toCloudProtocolV2(senMl); } diff --git a/test/cbor.test.js b/test/cbor.test.js index 3508717..d67cbd0 100644 --- a/test/cbor.test.js +++ b/test/cbor.test.js @@ -187,6 +187,34 @@ describe('Test CBOR encoding using CloudProtocol v1', () => { expect(cborHex).toStrictEqual(output); }); + it('Generate a valid cbor for a senml complex property with basename', () => { + const output = '0x81,0x82,0xA4,0x61,0x6E,0x6D,0x74,0x65,0x73,0x74,0x5F,0x62,0x6F,0x6F,0x6C,0x3A,0x6C,0x61,0x74,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x61,0x76,0xFB,0x40,0x4E,0x0A,0x32,0xA0,0x66,0x3C,0x75,0xA2,0x61,0x6E,0x6D,0x74,0x65,0x73,0x74,0x5F,0x62,0x6F,0x6F,0x6C,0x3A,0x6C,0x6F,0x6E,0x61,0x76,0xFB,0x40,0x38,0x4E,0x68,0x5D,0xB7,0x6B,0x3C,'; + const senMl = ArduinoCloud.getSenml(deviceId, 'test_bool', { + lat: 60.07967, + lon: 24.30628, + }, timestamp, false); + console.log([senMl]); + const cborbase64 = ArduinoCloud.getCborValue([senMl], false); + console.log(cborbase64); + const cborHex = base64toHEX(cborbase64); + console.log(cborHex); + expect(cborHex).toStrictEqual(output); + }); + + it('Generate a valid cbor for a senml complex property without basename', () => { + const output = '0x81,0x82,0xA3,0x61,0x6E,0x6D,0x74,0x65,0x73,0x74,0x5F,0x62,0x6F,0x6F,0x6C,0x3A,0x6C,0x61,0x74,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x76,0xFB,0x40,0x4E,0x0A,0x32,0xA0,0x66,0x3C,0x75,0xA2,0x61,0x6E,0x6D,0x74,0x65,0x73,0x74,0x5F,0x62,0x6F,0x6F,0x6C,0x3A,0x6C,0x6F,0x6E,0x61,0x76,0xFB,0x40,0x38,0x4E,0x68,0x5D,0xB7,0x6B,0x3C,'; + const senMl = ArduinoCloud.getSenml(null, 'test_bool', { + lat: 60.07967, + lon: 24.30628, + }, timestamp, false); + console.log([senMl]); + const cborbase64 = ArduinoCloud.getCborValue([senMl], false); + console.log(cborbase64); + const cborHex = base64toHEX(cborbase64); + console.log(cborHex); + expect(cborHex).toStrictEqual(output); + }); + it('Generate a valid cbor for multiple properties with basename', () => { const output = '0x86,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x69,0x74,0x65,0x73,0x74,0x5F,0x75,0x69,0x6E,0x74,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x61,0x76,0x04,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x69,0x74,0x65,0x73,0x74,0x5F,0x73,0x69,0x6E,0x74,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x61,0x76,0x23,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x6A,0x74,0x65,0x73,0x74,0x5F,0x66,0x6C,0x6F,0x61,0x74,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x61,0x76,0xFB,0x40,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x6B,0x74,0x65,0x73,0x74,0x5F,0x73,0x74,0x72,0x69,0x6E,0x67,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x62,0x76,0x73,0x6A,0x74,0x65,0x73,0x74,0x20,0x76,0x61,0x6C,0x75,0x65,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x69,0x74,0x65,0x73,0x74,0x5F,0x62,0x6F,0x6F,0x6C,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x62,0x76,0x62,0xF5,0xA4,0x62,0x62,0x74,0x1A,0x5B,0x98,0xD7,0x80,0x61,0x6E,0x6B,0x74,0x65,0x73,0x74,0x5F,0x64,0x6F,0x75,0x62,0x6C,0x65,0x62,0x62,0x6E,0x78,0x2D,0x75,0x72,0x6E,0x3A,0x75,0x75,0x69,0x64,0x3A,0x31,0x66,0x34,0x63,0x65,0x64,0x37,0x30,0x2D,0x35,0x33,0x61,0x64,0x2D,0x34,0x62,0x32,0x39,0x2D,0x62,0x32,0x32,0x31,0x2D,0x31,0x62,0x30,0x61,0x62,0x62,0x64,0x66,0x63,0x37,0x35,0x37,0x61,0x76,0xFB,0x7F,0xEF,0xFF,0xFC,0x57,0xCA,0x82,0xAE,'; From 6613ce6569e6f2fc86cc643c7b30e9a76fcce37f Mon Sep 17 00:00:00 2001 From: ilcato Date: Sat, 4 May 2019 08:25:35 +0200 Subject: [PATCH 2/2] Multi value properties decode management --- src/index.js | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 041b8cd..eed888f 100644 --- a/src/index.js +++ b/src/index.js @@ -121,15 +121,22 @@ const connect = options => new Promise((resolve, reject) => { } const propertyValue = CBOR.decode(buf); + const propertyNameId = 0; + const attributeNameId = 1; + + let valueToSend = {}; + let propertyNameKeyPrevious = ''; propertyValue.forEach((p) => { // Support cbor labels - const propertyNameKey = p.n !== undefined ? p.n : p['0']; + let propertyNameKey = p.n !== undefined ? p.n : p['0']; + const propertyNameKeySplit = propertyNameKey.split(':'); + const valueKey = p.v !== undefined ? 'v' : '2'; const valueStringKey = p.vs !== undefined ? 'vs' : '3'; const valueBooleanKey = p.vb !== undefined ? 'vb' : '4'; + let value; + propertyNameKey = propertyNameKeySplit[propertyNameId]; if (propertyCallback[msg.topic][propertyNameKey]) { - let value = null; - if (!(p[valueKey] === undefined)) { value = p[valueKey]; } else if (!(p[valueStringKey] === undefined)) { @@ -137,13 +144,28 @@ const connect = options => new Promise((resolve, reject) => { } else if (!(p[valueBooleanKey] === undefined)) { value = p[valueBooleanKey]; } - - propertyCallback[msg.topic][propertyNameKey](value); + } + if (propertyNameKeyPrevious === '') { + propertyNameKeyPrevious = propertyNameKeySplit[propertyNameId]; + } + if (propertyNameKeyPrevious !== propertyNameKey) { + propertyCallback[msg.topic][propertyNameKeyPrevious](valueToSend); + propertyNameKeyPrevious = propertyNameKey; + valueToSend = {}; + } + if (propertyNameKeySplit.length === 1) { + valueToSend = value; + } else { + const attributeName = propertyNameKeySplit[attributeNameId]; + valueToSend[attributeName] = value; } }); + if (valueToSend !== {}) { + propertyCallback[msg.topic][propertyNameKeyPrevious](valueToSend); + } } }; - + client.onConnected = (reconnect) => { if (reconnect === true) { // This is a re-connection: re-subscribe to all topics subscribed before the