Skip to content

Commit

Permalink
Fix GladysAssistant#540: In MQTT service UI, display broker configura…
Browse files Browse the repository at this point in the history
…tion errors (By Atrovato) (GladysAssistant#667)

* fix GladysAssistant#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>
  • Loading branch information
Pierre-Gilles and atrovato authored Mar 2, 2020
1 parent d23dac8 commit ce9baab
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 10 deletions.
3 changes: 2 additions & 1 deletion front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
3 changes: 1 addition & 2 deletions front/src/routes/integration/all/mqtt/device-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -15,7 +14,7 @@ class MqttDevicePage extends Component {

render(props, {}) {
return (
<MqttPage integration={integrationConfig[props.user.language].mqtt}>
<MqttPage>
<DeviceTab {...props} />
</MqttPage>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -169,7 +168,7 @@ class MqttDeviceSetupPage extends Component {

render(props, state) {
return (
<MqttPage integration={integrationConfig[props.user.language].mqtt}>
<MqttPage>
<FeatureTab
{...props}
{...state}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const SetupTab = ({ children, ...props }) => {
)}
{props.mqttConnectionError && (
<p class="alert alert-danger">
<Text id="integration.mqtt.setup.connectionError" /> - {props.mqttConnectionError}
<Text id="integration.mqtt.setup.connectionError" />
</p>
)}
<form>
Expand Down
9 changes: 7 additions & 2 deletions front/src/routes/integration/all/mqtt/setup-page/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand All @@ -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,
Expand All @@ -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(
() =>
Expand All @@ -79,6 +83,7 @@ const createActions = store => {
displayMqttError(state, error) {
store.setState({
mqttConnected: false,
connectMqttStatus: undefined,
mqttConnectionError: error
});
}
Expand Down
3 changes: 1 addition & 2 deletions front/src/routes/integration/all/mqtt/setup-page/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -21,7 +20,7 @@ class MqttNodePage extends Component {

render(props, {}) {
return (
<MqttPage integration={integrationConfig[props.user.language].mqtt}>
<MqttPage>
<SetupTab {...props} />
</MqttPage>
);
Expand Down
8 changes: 8 additions & 0 deletions server/services/mqtt/lib/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
Expand Down
60 changes: 60 additions & 0 deletions server/test/services/mqtt/lib/connect.test.js
Original file line number Diff line number Diff line change
@@ -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',
});
});
});

0 comments on commit ce9baab

Please sign in to comment.