From ce9baab5dd815b68cd373f380323431a36ea8e37 Mon Sep 17 00:00:00 2001 From: Pierre-Gilles Leymarie Date: Mon, 2 Mar 2020 09:27:18 +0100 Subject: [PATCH] Fix #540: In MQTT service UI, display broker configuration errors (By Atrovato) (#667) * fix #540 : handle mqtt connect error * Improve MQTT service UX * Add tests to mqtt connection error/success Co-authored-by: Alexandre Trovato <1839717+atrovato@users.noreply.github.com> --- front/src/config/i18n/en.json | 3 +- .../integration/all/mqtt/device-page/index.js | 3 +- .../all/mqtt/device-page/setup/index.js | 3 +- .../all/mqtt/setup-page/SetupTab.jsx | 2 +- .../all/mqtt/setup-page/actions.js | 9 ++- .../integration/all/mqtt/setup-page/index.js | 3 +- server/services/mqtt/lib/connect.js | 8 +++ server/test/services/mqtt/lib/connect.test.js | 60 +++++++++++++++++++ 8 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 server/test/services/mqtt/lib/connect.test.js diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index cc47dde2aa..5d899f118d 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -390,7 +390,8 @@ "saveLabel": "Save configuration", "error": "An error occured while saving configuration.", "connecting": "Configuration saved. Now connecting to your MQTT broker...", - "connected": "Connected to the MQTT broker with success !" + "connected": "Connected to the MQTT broker with success !", + "connectionError": "Error while connecting, please check your configuration." } }, "xiaomi": { diff --git a/front/src/routes/integration/all/mqtt/device-page/index.js b/front/src/routes/integration/all/mqtt/device-page/index.js index 9bcb89a6aa..1742a09d4f 100644 --- a/front/src/routes/integration/all/mqtt/device-page/index.js +++ b/front/src/routes/integration/all/mqtt/device-page/index.js @@ -3,7 +3,6 @@ import { connect } from 'unistore/preact'; import actions from './actions'; import MqttPage from '../MqttPage'; import DeviceTab from './DeviceTab'; -import integrationConfig from '../../../../../config/integrations'; @connect('session,user,mqttDevices,houses,getMqttDevicesStatus', actions) class MqttDevicePage extends Component { @@ -15,7 +14,7 @@ class MqttDevicePage extends Component { render(props, {}) { return ( - + ); diff --git a/front/src/routes/integration/all/mqtt/device-page/setup/index.js b/front/src/routes/integration/all/mqtt/device-page/setup/index.js index 9eceea384f..62e4601c82 100644 --- a/front/src/routes/integration/all/mqtt/device-page/setup/index.js +++ b/front/src/routes/integration/all/mqtt/device-page/setup/index.js @@ -3,7 +3,6 @@ import { connect } from 'unistore/preact'; import actions from '../actions'; import FeatureTab from './FeatureTab'; import MqttPage from '../../MqttPage'; -import integrationConfig from '../../../../../../config/integrations'; import uuid from 'uuid'; import get from 'get-value'; import update from 'immutability-helper'; @@ -169,7 +168,7 @@ class MqttDeviceSetupPage extends Component { render(props, state) { return ( - + { )} {props.mqttConnectionError && (

- - {props.mqttConnectionError} +

)}
diff --git a/front/src/routes/integration/all/mqtt/setup-page/actions.js b/front/src/routes/integration/all/mqtt/setup-page/actions.js index d409b06b64..e6f77e0bad 100644 --- a/front/src/routes/integration/all/mqtt/setup-page/actions.js +++ b/front/src/routes/integration/all/mqtt/setup-page/actions.js @@ -36,7 +36,8 @@ const createActions = store => { event.preventDefault(); store.setState({ connectMqttStatus: RequestStatus.Getting, - mqttConnected: false + mqttConnected: false, + mqttConnectionError: undefined }); try { await state.httpClient.post('/api/v1/service/mqtt/variable/MQTT_URL', { @@ -55,6 +56,8 @@ const createActions = store => { store.setState({ connectMqttStatus: RequestStatus.Success }); + + setTimeout(() => store.setState({ connectMqttStatus: undefined }), 3000); } catch (e) { store.setState({ connectMqttStatus: RequestStatus.Error, @@ -65,7 +68,8 @@ const createActions = store => { displayConnectedMessage(state) { // display 3 seconds a message "MQTT connected" store.setState({ - mqttConnected: true + mqttConnected: true, + mqttConnectionError: undefined }); setTimeout( () => @@ -79,6 +83,7 @@ const createActions = store => { displayMqttError(state, error) { store.setState({ mqttConnected: false, + connectMqttStatus: undefined, mqttConnectionError: error }); } diff --git a/front/src/routes/integration/all/mqtt/setup-page/index.js b/front/src/routes/integration/all/mqtt/setup-page/index.js index 142249256a..ae4ae6cf42 100644 --- a/front/src/routes/integration/all/mqtt/setup-page/index.js +++ b/front/src/routes/integration/all/mqtt/setup-page/index.js @@ -3,7 +3,6 @@ import { connect } from 'unistore/preact'; import actions from './actions'; import MqttPage from '../MqttPage'; import SetupTab from './SetupTab'; -import integrationConfig from '../../../../../config/integrations'; import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../../../server/utils/constants'; @connect('user,session,mqttURL,mqttUsername,mqttPassword,connectMqttStatus,mqttConnected,mqttConnectionError', actions) @@ -21,7 +20,7 @@ class MqttNodePage extends Component { render(props, {}) { return ( - + ); diff --git a/server/services/mqtt/lib/connect.js b/server/services/mqtt/lib/connect.js index ca887f52ca..81d80bc227 100644 --- a/server/services/mqtt/lib/connect.js +++ b/server/services/mqtt/lib/connect.js @@ -46,6 +46,14 @@ async function connect() { payload: err, }); }); + this.mqttClient.on('offline', () => { + logger.warn(`Disconnected from MQTT server`); + this.gladys.event.emit(EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.MQTT.ERROR, + payload: 'DISCONNECTED', + }); + this.connected = false; + }); this.mqttClient.on('message', (topic, message) => { this.handleNewMessage(topic, message.toString()); }); diff --git a/server/test/services/mqtt/lib/connect.test.js b/server/test/services/mqtt/lib/connect.test.js new file mode 100644 index 0000000000..ff451a9c29 --- /dev/null +++ b/server/test/services/mqtt/lib/connect.test.js @@ -0,0 +1,60 @@ +const sinon = require('sinon'); + +const { assert, fake } = sinon; +const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../../utils/constants'); +const { MockedMqttClient } = require('../mocks.test'); + +const MqttHandler = require('../../../../services/mqtt/lib'); + +describe('mqttHandler.connect', () => { + it('should connect and receive success', async () => { + const gladys = { + variable: { + getValue: fake.resolves('result'), + }, + event: { + emit: fake.returns(null), + }, + }; + const mqttHandler = new MqttHandler(gladys, MockedMqttClient, 'faea9c35-759a-44d5-bcc9-2af1de37b8b4'); + await mqttHandler.connect(); + mqttHandler.mqttClient.emit('connect'); + assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.MQTT.CONNECTED, + }); + }); + it('should connect and receive error', async () => { + const gladys = { + variable: { + getValue: fake.resolves('result'), + }, + event: { + emit: fake.returns(null), + }, + }; + const mqttHandler = new MqttHandler(gladys, MockedMqttClient, 'faea9c35-759a-44d5-bcc9-2af1de37b8b4'); + await mqttHandler.connect(); + mqttHandler.mqttClient.emit('error', { test: 'test' }); + assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.MQTT.ERROR, + payload: { test: 'test' }, + }); + }); + it('should connect and receive offline', async () => { + const gladys = { + variable: { + getValue: fake.resolves('result'), + }, + event: { + emit: fake.returns(null), + }, + }; + const mqttHandler = new MqttHandler(gladys, MockedMqttClient, 'faea9c35-759a-44d5-bcc9-2af1de37b8b4'); + await mqttHandler.connect(); + mqttHandler.mqttClient.emit('offline'); + assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, { + type: WEBSOCKET_MESSAGE_TYPES.MQTT.ERROR, + payload: 'DISCONNECTED', + }); + }); +});