diff --git a/lib/controller.js b/lib/controller.js index 36984f3e9..8b5c6dc94 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -248,19 +248,36 @@ class Controller { if (settings.get().experimental.output === 'json') { this.mqtt.publish(entity.friendlyName, JSON.stringify(messagePayload), options); } else if (settings.get().experimental.output === 'attribute') { - Object.keys(messagePayload).forEach((key) => { - if (typeof messagePayload[key] == 'object') { - Object.keys(messagePayload[key]).forEach((subKey) => { - this.mqtt.publish(`${entity.friendlyName}/${key}-${subKey}`, - `${messagePayload[key][subKey]}`, options); - }); - } else { - this.mqtt.publish(`${entity.friendlyName}/${key}`, `${messagePayload[key]}`, options); - } - }); + this.iteratePayloadForAttrOutput(entity.friendlyName+'/', messagePayload, options); } } + iteratePayloadForAttrOutput(topicRoot, payload, options) { + Object.keys(payload).forEach((key) => { + let subPayload = payload[key]; + let message; + + // Special cases + if (key === 'color' && + subPayload.r !== undefined && + subPayload.g !== undefined && + subPayload.b !== undefined) { + subPayload = [subPayload.r, subPayload.g, subPayload.b]; + } + + // Check Array first, since it is also an Object + if (Array.isArray(subPayload)) { + message = subPayload.map((x) => `${x}`).join(','); + } else if (typeof subPayload === 'object') { + return this.iteratePayloadForAttrOutput(topicRoot+key+'-', subPayload, options); + } else { + message = typeof subPayload === 'string' ? subPayload : JSON.stringify(subPayload); + } + + this.mqtt.publish(`${topicRoot}${key}`, message, options); + }); + } + getDeviceInfoForMqtt(ieeeAddr) { const device = this.zigbee.getDevice(ieeeAddr); const { diff --git a/test/controller.test.js b/test/controller.test.js index 5e72ca3a8..f12951982 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -91,13 +91,27 @@ describe('Controller', () => { }, }); - const payload = {temperature: 1, humidity: 2}; + const payload = { + temperature: 1, + humidity: 2, + state: 'ON', + allowedStates: ['ON', 'OFF'], + color: {r: 100, g: 0, b: 102, a: 0}, + nested: { + state: 'OFF', + color: {r: 1, g: 0, b: 2}, + }, + }; controller.publishEntityState('0x12345678', payload); - expect(mqttPublish).toHaveBeenCalledTimes(2); - expect(mqttPublish.mock.calls[0][0]).toBe('test/temperature'); - expect(mqttPublish.mock.calls[0][1]).toBe('1'); - expect(mqttPublish.mock.calls[1][0]).toBe('test/humidity'); - expect(mqttPublish.mock.calls[1][1]).toBe('2'); + expect(mqttPublish.mock.calls.map((x) => [x[0], x[1]])).toEqual([ + ['test/temperature', '1'], + ['test/humidity', '2'], + ['test/state', 'ON'], + ['test/allowedStates', 'ON,OFF'], + ['test/color', '100,0,102'], + ['test/nested-state', 'OFF'], + ['test/nested-color', '1,0,2'], + ]); }); it('Should cache state', () => {