Skip to content

Commit 71a611f

Browse files
author
Luca Barbetti
authored
Merge pull request #77 from arduino/multi-value-properties
Multi value properties
2 parents 6ff416b + b79e782 commit 71a611f

File tree

2 files changed

+137
-7
lines changed

2 files changed

+137
-7
lines changed

src/index.js

+109-7
Original file line numberDiff line numberDiff line change
@@ -121,29 +121,51 @@ const connect = options => new Promise((resolve, reject) => {
121121
}
122122

123123
const propertyValue = CBOR.decode(buf);
124+
const propertyNameId = 0;
125+
const attributeNameId = 1;
126+
127+
let valueToSend = {};
128+
let propertyNameKeyPrevious = '';
124129
propertyValue.forEach((p) => {
125130
// Support cbor labels
126-
const propertyNameKey = p.n !== undefined ? p.n : p['0'];
131+
let propertyNameKey = p.n !== undefined ? p.n : p['0'];
132+
const propertyNameKeySplit = propertyNameKey.split(':');
133+
127134
const valueKey = p.v !== undefined ? 'v' : '2';
128135
const valueStringKey = p.vs !== undefined ? 'vs' : '3';
129136
const valueBooleanKey = p.vb !== undefined ? 'vb' : '4';
137+
let value;
138+
propertyNameKey = propertyNameKeySplit[propertyNameId];
130139
if (propertyCallback[msg.topic][propertyNameKey]) {
131-
let value = null;
132-
133140
if (!(p[valueKey] === undefined)) {
134141
value = p[valueKey];
135142
} else if (!(p[valueStringKey] === undefined)) {
136143
value = p[valueStringKey];
137144
} else if (!(p[valueBooleanKey] === undefined)) {
138145
value = p[valueBooleanKey];
139146
}
140-
141-
propertyCallback[msg.topic][propertyNameKey](value);
147+
}
148+
if (propertyNameKeyPrevious === '') {
149+
propertyNameKeyPrevious = propertyNameKeySplit[propertyNameId];
150+
}
151+
if (propertyNameKeyPrevious !== propertyNameKey) {
152+
propertyCallback[msg.topic][propertyNameKeyPrevious](valueToSend);
153+
propertyNameKeyPrevious = propertyNameKey;
154+
valueToSend = {};
155+
}
156+
if (propertyNameKeySplit.length === 1) {
157+
valueToSend = value;
158+
} else {
159+
const attributeName = propertyNameKeySplit[attributeNameId];
160+
valueToSend[attributeName] = value;
142161
}
143162
});
163+
if (valueToSend !== {}) {
164+
propertyCallback[msg.topic][propertyNameKeyPrevious](valueToSend);
165+
}
144166
}
145167
};
146-
168+
147169
client.onConnected = (reconnect) => {
148170
if (reconnect === true) {
149171
// This is a re-connection: re-subscribe to all topics subscribed before the
@@ -404,6 +426,44 @@ const sendProperty = (thingId, name, value, timestamp) => {
404426
throw new Error('Name must be a valid string');
405427
}
406428

429+
if (typeof value === 'object') {
430+
const objectKeys = Object.keys(value);
431+
const cborValues = objectKeys.map((key, i) => {
432+
const cborValue = {
433+
n: `${name}:${key}`,
434+
};
435+
436+
if (i === 0) {
437+
cborValue.bt = timestamp || new Date().getTime();
438+
}
439+
440+
switch (typeof value[key]) {
441+
case 'string':
442+
cborValue.vs = value[key];
443+
break;
444+
case 'number':
445+
cborValue.v = value[key];
446+
break;
447+
case 'boolean':
448+
cborValue.vb = value[key];
449+
break;
450+
default:
451+
break;
452+
}
453+
454+
return cborValue;
455+
})
456+
.map((cborValue) => {
457+
if (connectionOptions.useCloudProtocolV2) {
458+
return toCloudProtocolV2(cborValue);
459+
}
460+
461+
return cborValue;
462+
});
463+
464+
return sendMessage(propertyInputTopic, CBOR.encode(cborValues, true));
465+
}
466+
407467
let cborValue = {
408468
bt: timestamp || new Date().getTime(),
409469
n: name,
@@ -439,6 +499,49 @@ const getSenml = (deviceId, name, value, timestamp) => {
439499
throw new Error('Name must be a valid string');
440500
}
441501

502+
503+
if (typeof value === 'object') {
504+
const objectKeys = Object.keys(value);
505+
const senMls = objectKeys.map((key, i) => {
506+
const senMl = {
507+
n: `${name}:${key}`,
508+
};
509+
510+
if (i === 0) {
511+
senMl.bt = timestamp || new Date().getTime();
512+
513+
if (deviceId) {
514+
senMl.bn = `urn:uuid:${deviceId}`;
515+
}
516+
}
517+
518+
switch (typeof value[key]) {
519+
case 'string':
520+
senMl.vs = value[key];
521+
break;
522+
case 'number':
523+
senMl.v = value[key];
524+
break;
525+
case 'boolean':
526+
senMl.vb = value[key];
527+
break;
528+
default:
529+
break;
530+
}
531+
532+
return senMl;
533+
})
534+
.map((senMl) => {
535+
if (connectionOptions.useCloudProtocolV2) {
536+
return toCloudProtocolV2(senMl);
537+
}
538+
539+
return senMl;
540+
});
541+
542+
return senMls;
543+
}
544+
442545
const senMl = {
443546
bt: timestamp || new Date().getTime(),
444547
n: name,
@@ -462,7 +565,6 @@ const getSenml = (deviceId, name, value, timestamp) => {
462565
break;
463566
}
464567

465-
466568
if (connectionOptions.useCloudProtocolV2) {
467569
return toCloudProtocolV2(senMl);
468570
}

test/cbor.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,34 @@ describe('Test CBOR encoding using CloudProtocol v1', () => {
187187
expect(cborHex).toStrictEqual(output);
188188
});
189189

190+
it('Generate a valid cbor for a senml complex property with basename', () => {
191+
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,';
192+
const senMl = ArduinoCloud.getSenml(deviceId, 'test_bool', {
193+
lat: 60.07967,
194+
lon: 24.30628,
195+
}, timestamp, false);
196+
console.log([senMl]);
197+
const cborbase64 = ArduinoCloud.getCborValue([senMl], false);
198+
console.log(cborbase64);
199+
const cborHex = base64toHEX(cborbase64);
200+
console.log(cborHex);
201+
expect(cborHex).toStrictEqual(output);
202+
});
203+
204+
it('Generate a valid cbor for a senml complex property without basename', () => {
205+
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,';
206+
const senMl = ArduinoCloud.getSenml(null, 'test_bool', {
207+
lat: 60.07967,
208+
lon: 24.30628,
209+
}, timestamp, false);
210+
console.log([senMl]);
211+
const cborbase64 = ArduinoCloud.getCborValue([senMl], false);
212+
console.log(cborbase64);
213+
const cborHex = base64toHEX(cborbase64);
214+
console.log(cborHex);
215+
expect(cborHex).toStrictEqual(output);
216+
});
217+
190218

191219
it('Generate a valid cbor for multiple properties with basename', () => {
192220
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,';

0 commit comments

Comments
 (0)