diff --git a/server/services/google-actions/lib/smarthome/googleActions.onExecute.js b/server/services/google-actions/lib/smarthome/googleActions.onExecute.js index 0f65048ec1..56d802a65d 100644 --- a/server/services/google-actions/lib/smarthome/googleActions.onExecute.js +++ b/server/services/google-actions/lib/smarthome/googleActions.onExecute.js @@ -1,3 +1,5 @@ +const Promise = require('bluebird'); + const logger = require('../../../../utils/logger'); const { ACTIONS, ACTIONS_STATUS, EVENTS } = require('../../../../utils/constants'); @@ -7,77 +9,65 @@ const { TRAIT_BY_COMMAND } = require('../traits'); * @description The function that will run for an EXECUTE request. * It should return a valid response or a Promise that resolves to valid response. * @param {object} body - Request body. - * @returns {object} A valid response. + * @returns {Promise} A valid response. * @example - * googleActions.onExecute({}); + * await googleActions.onExecute({}); * @see https://actions-on-google.github.io/actions-on-google-nodejs/interfaces/smarthome.smarthomeapp.html#onexecute */ -function onExecute(body) { +async function onExecute(body) { const commands = []; - body.inputs - .filter((input) => input.payload && input.payload.commands) - .forEach((input) => { - input.payload.commands - .filter((command) => { - return command.devices && command.devices.length > 0; - }) - .forEach((command) => { - const { devices, execution } = command; - const requestedDevices = devices - .map((device) => { - // Load related device - const gladysDevice = this.gladys.stateManager.get('device', device.id); - - if (!gladysDevice) { - commands.push({ ids: [device.id], status: 'ERROR' }); - } - - return gladysDevice; - }) - .filter(Boolean); + const inputCommands = body.inputs + .filter((input) => input.payload) + .map((input) => input.payload) + .map((payload) => payload.commands) + .filter(Boolean) + .flatMap((command) => command); - requestedDevices.forEach((device) => { - // Each execution triggered - execution.forEach((exec) => { - const trait = TRAIT_BY_COMMAND[exec.command]; - const deviceStatus = { ids: [device.selector], status: 'ERROR' }; + await Promise.each(inputCommands, async ({ devices = [], execution }) => { + await Promise.each(devices, async (device) => { + const { id: selector } = device; + // Load related device + const gladysDevice = this.gladys.stateManager.get('device', selector); - if (!trait || !trait.commands[exec.command]) { - // Command key not found - logger.error(`GoogleActions "${exec.command}" command is not managed.`); - // All devices are failure - deviceStatus.status = 'ERROR'; - } else { - const commandExecutor = trait.commands[exec.command]; + if (!gladysDevice) { + commands.push({ ids: [selector], status: 'ERROR' }); + } else { + // Each execution triggered + await Promise.each(execution, async (exec) => { + const trait = TRAIT_BY_COMMAND[exec.command]; - // Build related feature events according incomping attributes - const { events = [], states } = commandExecutor(device, exec.params); + if (!trait || !trait.commands[exec.command]) { + // Command key not found + logger.error(`GoogleActions "${exec.command}" command is not managed.`); + // All devices are failure + commands.push({ ids: [selector], status: 'ERROR' }); + } else { + const commandExecutor = trait.commands[exec.command]; - if (events.length > 0) { - events.forEach((eventMessage) => { - const action = { - type: ACTIONS.DEVICE.SET_VALUE, - status: ACTIONS_STATUS.PENDING, - device: device.selector, - ...eventMessage, - }; - this.gladys.event.emit(EVENTS.ACTION.TRIGGERED, action); - deviceStatus.status = 'PENDING'; - }); - } + const deviceStatus = { ids: [selector], status: 'ERROR' }; + // Build related feature events according incomping attributes + const { events = [] } = await commandExecutor(gladysDevice, exec.params, this.gladys); - if (states) { - deviceStatus.status = 'SUCCESS'; - deviceStatus.states = states; - } - } + if (events.length > 0) { + events.forEach((eventMessage) => { + const action = { + type: ACTIONS.DEVICE.SET_VALUE, + status: ACTIONS_STATUS.PENDING, + device: selector, + ...eventMessage, + }; + this.gladys.event.emit(EVENTS.ACTION.TRIGGERED, action); + }); + deviceStatus.status = 'PENDING'; + } - commands.push(deviceStatus); - }); - }); + commands.push(deviceStatus); + } }); + } }); + }); return { requestId: body.requestId, diff --git a/server/services/google-actions/package-lock.json b/server/services/google-actions/package-lock.json index 46d22b72c1..20c690e009 100644 --- a/server/services/google-actions/package-lock.json +++ b/server/services/google-actions/package-lock.json @@ -18,10 +18,15 @@ "win32" ], "dependencies": { - "set-value": "^4.0.1", - "uuid": "^9.0.0" + "bluebird": "^3.7.2", + "set-value": "^4.0.1" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -65,17 +70,14 @@ "engines": { "node": ">=11.0" } - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } } }, "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -102,11 +104,6 @@ "is-plain-object": "^2.0.4", "is-primitive": "^3.0.1" } - }, - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" } } } diff --git a/server/services/google-actions/package.json b/server/services/google-actions/package.json index e928440251..e7820361cb 100644 --- a/server/services/google-actions/package.json +++ b/server/services/google-actions/package.json @@ -12,7 +12,7 @@ "arm64" ], "dependencies": { - "set-value": "^4.0.1", - "uuid": "^9.0.0" + "bluebird": "^3.7.2", + "set-value": "^4.0.1" } } diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.light.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.light.test.js index cfa4976963..454870b671 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.light.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.light.test.js @@ -144,8 +144,8 @@ describe('GoogleActions Handler - onSync - brightness (light)', () => { assert.notCalled(gladys.event.emit); }); - it('should emit Gladys event with new value - onExecute', () => { - const result = googleActionsHandler.onExecute(body); + it('should emit Gladys event with new value - onExecute', async () => { + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.switch.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.switch.test.js index 6c1ed657e6..de797e20ee 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.switch.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.brightness.trait.switch.test.js @@ -144,8 +144,8 @@ describe('GoogleActions Handler - onSync - brightness (switch)', () => { assert.notCalled(gladys.event.emit); }); - it('should emit Gladys event with new value - onExecute', () => { - const result = googleActionsHandler.onExecute(body); + it('should emit Gladys event with new value - onExecute', async () => { + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.color.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.color.test.js index 32055cfa1e..9e7b2e6a5f 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.color.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.color.test.js @@ -146,7 +146,7 @@ describe('GoogleActions Handler - onSync - color', () => { }); it('should emit Gladys event with new value - onExecute', async () => { - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.colorTemp.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.colorTemp.test.js index b74d7564e3..a27419a447 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.colorTemp.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.colorSetting.trait.colorTemp.test.js @@ -153,8 +153,8 @@ describe('GoogleActions Handler - onSync - color', () => { assert.notCalled(gladys.event.emit); }); - it('should emit Gladys event with new value - onExecute', () => { - const result = googleActionsHandler.onExecute(body); + it('should emit Gladys event with new value - onExecute', async () => { + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.curtain.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.curtain.test.js index 5217073170..9a790906b1 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.curtain.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.curtain.test.js @@ -183,8 +183,8 @@ describe('GoogleActions Handler - onSync - openClose - curtain', () => { assert.notCalled(gladys.event.emit); }); - it('should emit Gladys event with new value - onExecute', () => { - const result = googleActionsHandler.onExecute(body); + it('should emit Gladys event with new value - onExecute', async () => { + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.shutter.test.js b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.shutter.test.js index 6522294d67..777fa02574 100644 --- a/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.shutter.test.js +++ b/server/test/services/google-actions/lib/smarthome/devices_and_traits/googleActions.openCloseTrait.trait.shutter.test.js @@ -183,8 +183,8 @@ describe('GoogleActions Handler - onSync - openClose - shutter', () => { assert.notCalled(gladys.event.emit); }); - it('should emit Gladys event with new value - onExecute', () => { - const result = googleActionsHandler.onExecute(body); + it('should emit Gladys event with new value - onExecute', async () => { + const result = await googleActionsHandler.onExecute(body); const expectedResult = { requestId: 'request-id', diff --git a/server/test/services/google-actions/lib/smarthome/googleActions.onExecute.test.js b/server/test/services/google-actions/lib/smarthome/googleActions.onExecute.test.js index ebdf4d3940..d56b6b209c 100644 --- a/server/test/services/google-actions/lib/smarthome/googleActions.onExecute.test.js +++ b/server/test/services/google-actions/lib/smarthome/googleActions.onExecute.test.js @@ -37,7 +37,7 @@ describe('GoogleActions Handler - onExecute', () => { sinon.reset(); }); - it('should do nothing - empty payload', () => { + it('should do nothing - empty payload', async () => { const body = { requestId: 'request-id', user: { @@ -47,7 +47,7 @@ describe('GoogleActions Handler - onExecute', () => { inputs: [], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -62,7 +62,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('should do nothing - empty commands', () => { + it('should do nothing - empty commands', async () => { const body = { requestId: 'request-id', user: { @@ -79,7 +79,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -93,7 +93,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('should do nothing - empty devices', () => { + it('should do nothing - empty devices', async () => { const body = { requestId: 'request-id', user: { @@ -112,7 +112,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -126,7 +126,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('should send errorneous device - unkonwn device', () => { + it('should send errorneous device - unkonwn device', async () => { gladys.stateManager.get = fake.returns(null); const body = { @@ -148,7 +148,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -167,7 +167,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('should send errorneous device - command not managed', () => { + it('should send errorneous device - command not managed', async () => { const body = { requestId: 'request-id', user: { @@ -192,7 +192,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -211,7 +211,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('should send errorneous device - no events generated', () => { + it('should send errorneous device - no events generated', async () => { gladys.stateManager.get = fake.returns({ selector: 'device-1', features: [], @@ -244,7 +244,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id', @@ -263,7 +263,7 @@ describe('GoogleActions Handler - onExecute', () => { assert.notCalled(gladys.event.emit); }); - it('onExecute - success', () => { + it('should emit event - onExecute', async () => { const body = { requestId: 'request-id', user: { @@ -291,7 +291,7 @@ describe('GoogleActions Handler - onExecute', () => { ], }; - const result = googleActionsHandler.onExecute(body); + const result = await googleActionsHandler.onExecute(body); const exptectedResult = { requestId: 'request-id',