diff --git a/nodes/device.html b/nodes/device.html index a4acfba..6e70247 100644 --- a/nodes/device.html +++ b/nodes/device.html @@ -72,7 +72,7 @@ function listHubitatDeviceAttributes(server, deviceId, attribute) { const selectMenu = $('#node-input-attribute'); selectMenu.find('option').remove().end(); - const option = $('
Send events allow to send or not event when it receive one from Hubitat.
currentValue
payload is deprecated and replaced by value
.
This node is not compatible with Node-RED 0.x
diff --git a/nodes/device.js b/nodes/device.js index 585c2e4..a0929d3 100644 --- a/nodes/device.js +++ b/nodes/device.js @@ -53,17 +53,20 @@ module.exports = function HubitatDeviceModule(RED) { return node.hubitat.getDevice(node.deviceId).then((device) => { if (!device.attributes) { throw new Error(JSON.stringify(device)); } - device.attributes.forEach((attribute) => { - attribute.value = attribute.currentValue; - // delete attribute.currentValue; // kept for compatibility - if (node.attribute === attribute.name) { - node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(attribute.value)}` }); - node.log(`Initialized. ${node.attribute}: ${attribute.value}`); - } - }); - node.currentAttributes = device.attributes; + // delete attribute.currentValue; // kept for compatibility + node.currentAttributes = device.attributes.reduce((obj, item) => { + obj[item.name] = { ...item, value: item.currentValue, deviceId: node.deviceId }; + return obj; + }, {}); - if (!node.attribute) { + if (node.attribute) { + const attribute = node.currentAttributes[node.attribute]; + if (!attribute) { + throw new Error(`Selected attribute (${node.attribute}) is not handled by device`); + } + node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(attribute.value)}` }); + node.log(`Initialized. ${node.attribute}: ${attribute.value}`); + } else { node.status({}); node.log('Initialized'); } @@ -83,32 +86,25 @@ module.exports = function HubitatDeviceModule(RED) { return; } } - - let found = false; - node.currentAttributes.forEach((attribute) => { - if (event.name === attribute.name) { - attribute.value = castHubitatValue(node, attribute.dataType, event.value); - attribute.deviceId = node.deviceId; - attribute.currentValue = attribute.value; // deprecated since 0.0.18 - if ((node.attribute === event.name) || (!node.attribute)) { - if (node.attribute) { - node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(attribute.value)}` }); - node.log(`${node.attribute}: ${attribute.value}`); - } else { - node.status({}); - node.log('Attributes refreshed'); - } - if (node.sendEvent) { - const msg = { ...attribute }; - node.send({ payload: msg, topic: node.name }); - } - } - found = true; - } - }); - if (!found) { + const attribute = node.currentAttributes[event.name]; + if (!attribute) { node.status({ fill: 'red', shape: node.shape, text: `Unknown event: ${event.name}` }); } + attribute.value = castHubitatValue(node, attribute.dataType, event.value); + attribute.currentValue = attribute.value; // deprecated since 0.0.18 + if ((node.attribute === event.name) || (!node.attribute)) { + if (node.attribute) { + node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(attribute.value)}` }); + node.log(`${node.attribute}: ${attribute.value}`); + } else { + node.status({}); + node.log('Attributes refreshed'); + } + if (node.sendEvent) { + const msg = { ...attribute }; + node.send({ payload: msg, topic: node.name }); + } + } }; this.hubitat.hubitatEvent.on(`device.${node.deviceId}`, callback); @@ -125,29 +121,33 @@ module.exports = function HubitatDeviceModule(RED) { } const attributeSearched = msg.attribute || node.attribute; - if (attributeSearched === undefined) { - node.status({ fill: 'red', shape: node.shape, text: 'Undefined attribute' }); + if (!attributeSearched) { + msg.payload = { ...node.currentAttributes }; + msg.topic = node.name; + send(msg); + node.status({}); + done(); return; } - let foundAttribute; - node.currentAttributes.forEach((attribute) => { - if (attributeSearched === attribute.name) { - msg.payload = { ...attribute }; - msg.payload.deviceId = node.deviceId; - msg.topic = node.name; - send(msg); - foundAttribute = attribute; - } - }); - if (foundAttribute === undefined) { + + const attribute = node.currentAttributes[attributeSearched]; + if (!attribute) { node.status({ fill: 'red', shape: node.shape, text: `Invalid attribute: ${attributeSearched}` }); - } else if (!node.attribute) { + done(); + return; + } + + msg.payload = { ...attribute }; + msg.topic = node.name; + send(msg); + if (!node.attribute) { node.status({}); - } else if (node.attribute === foundAttribute.name) { - node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(foundAttribute.value)}` }); + } else if (node.attribute === attribute.name) { + node.status({ fill: 'blue', shape: node.shape, text: `${node.attribute}: ${JSON.stringify(attribute.value)}` }); } done(); }); + node.on('close', () => { node.debug('Closed'); this.hubitat.hubitatEvent.removeListener(`device.${node.deviceId}`, callback); diff --git a/test/nodes/device_spec.js b/test/nodes/device_spec.js index ad745a8..4cefc23 100644 --- a/test/nodes/device_spec.js +++ b/test/nodes/device_spec.js @@ -60,7 +60,7 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 'old-value' }]; + n1.currentAttributes = { testAttribute: { name: 'testAttribute', value: 'old-value', deviceId: '42' } }; n2.on('input', (msg) => { try { msg.should.have.property('payload', { ...hubitatEvent, currentValue: hubitatEvent.value }); @@ -86,7 +86,7 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 'old-value' }]; + n1.currentAttributes = { testAttribute: { name: 'testAttribute', value: 'old-value' } }; let inError = false; n2.on('input', (msg) => { inError = true; @@ -97,7 +97,7 @@ describe('Hubitat Device Node', () => { done(new Error('device receive wrong event')); } else { try { - n1.should.have.property('currentAttributes', [{ name: 'testAttribute', value: 'old-value' }]); + n1.should.have.property('currentAttributes', { testAttribute: { name: 'testAttribute', value: 'old-value' } }); done(); } catch (err) { done(err); @@ -130,6 +130,27 @@ describe('Hubitat Device Node', () => { } }); }); + it('should send all atributes when not specified', (done) => { + const flow = [ + defaultConfigNode, + { ...defaultDeviceNode, attribute: '', wires: [['n2']] }, + { id: 'n2', type: 'helper' }, + ]; + helper.load([deviceNode, configNode], flow, () => { + const n1 = helper.getNode('n1'); + const n2 = helper.getNode('n2'); + n1.currentAttributes = { testAttribute: { name: 'testAttribute', value: 'old-value' } }; + n2.on('input', (msg) => { + try { + msg.should.have.property('payload', { ...n1.currentAttributes }); + done(); + } catch (err) { + done(err); + } + }); + n1.receive({}); + }); + }); it('should not link the internal properties to the output message', (done) => { const flow = [ defaultConfigNode, @@ -139,12 +160,12 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 'value' }]; + n1.currentAttributes = { testAttribute: { name: 'testAttribute', value: 'value' } }; n2.on('input', (msg) => { try { // eslint-disable-next-line no-param-reassign msg.payload.value = 'update value in another node'; - n1.currentAttributes.should.containEql({ name: 'testAttribute', value: 'value' }); + n1.currentAttributes.should.containEql({ testAttribute: { name: 'testAttribute', value: 'value' } }); done(); } catch (err) { done(err); @@ -167,12 +188,12 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 'old-value' }]; + n1.currentAttributes = { testAttribute: { value: 'old-value' } }; n2.on('input', (msg) => { try { - n1.currentAttributes[0].should.have.property('value', 'new-value'); + n1.currentAttributes.testAttribute.should.have.property('value', 'new-value'); msg.payload.value = 'overwrite-value'; - n1.currentAttributes[0].should.have.property('value', 'new-value'); + n1.currentAttributes.testAttribute.should.have.property('value', 'new-value'); done(); } catch (err) { done(err); @@ -191,7 +212,7 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 1, dataType: 'NUMBER' }]; + n1.currentAttributes = { testAttribute: { value: 1, dataType: 'NUMBER' } }; n2.on('input', (msg) => { try { msg.payload.should.have.property('value', -2.5); @@ -213,7 +234,7 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: false, dataType: 'BOOL' }]; + n1.currentAttributes = { testAttribute: { value: false, dataType: 'BOOL' } }; n2.on('input', (msg) => { try { msg.payload.should.have.property('value', true); @@ -235,7 +256,8 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: { x: -9, y: 1, z: -2 }, dataType: 'VECTOR3' }]; + n1.currentAttributes = { testAttribute: { value: { x: -9, y: 1, z: -2 }, dataType: 'VECTOR3' } }; + n2.on('input', (msg) => { try { msg.payload.should.have.property('value', { x: 2, y: -4, z: 1.5 }); @@ -257,7 +279,8 @@ describe('Hubitat Device Node', () => { helper.load([deviceNode, configNode], flow, () => { const n1 = helper.getNode('n1'); const n2 = helper.getNode('n2'); - n1.currentAttributes = [{ name: 'testAttribute', value: 'undefined', dataType: 'UNDEFINED' }]; + n1.currentAttributes = { testAttribute: { value: 'undefined', dataType: 'UNDEFINED' } }; + n2.on('input', (msg) => { try { msg.payload.should.have.property('value', 'string');