Skip to content

Commit

Permalink
Merge cac115f into 5c4a0a3
Browse files Browse the repository at this point in the history
  • Loading branch information
ottoszika authored Dec 10, 2019
2 parents 5c4a0a3 + cac115f commit b758726
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"ewelink-generic-device": "src/generic-device/generic-device.js",
"ewelink-firmware-version": "src/firmware-version/firmware-version.js",
"ewelink-power-state": "src/power-state/power-state.js",
"ewelink-channel-count": "src/channel-count/channel-count.js"
"ewelink-channel-count": "src/channel-count/channel-count.js",
"ewelink-event-listener": "src/event-listener/event-listener.js"
}
},
"dependencies": {
Expand Down
34 changes: 34 additions & 0 deletions src/event-listener/event-listener.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script type="text/javascript">
RED.nodes.registerType('ewelink-event-listener', {
category: 'eWeLink',
color: '#a6bbcf',
defaults: {
name: { value: '' },
deviceId: { value: '' },
auth: { value: '', type: 'ewelink-credentials' }
},
outputs: 1,
label: function () {
return this.name || 'eWeLink Event Listener';
}
});
</script>

<script type="text/x-red" data-template-name="ewelink-event-listener">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Device ID</label>
<input type="text" id="node-input-deviceId" placeholder="Device ID">
</div>
<div class="form-row">
<label for="node-input-auth"><i class="icon-tag"></i> eWeLink Credentials</label>
<input type="text" id="node-input-auth" placeholder="eWeLink Credentials">
</div>
</script>

<script type="text/x-red" data-help-name="ewelink-event-listener">
<p>Listen for all or a specific device events</p>
</script>
32 changes: 32 additions & 0 deletions src/event-listener/event-listener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const eWeLinkConnect = require('../utils/ewelink-connect');

/**
* Event listener node.
*/
module.exports = (RED) => {
/**
* Event listener node constructor.
*
* @param {object} config The node configuration.
*/
function EventListenerNode(config) {
// Create the node
RED.nodes.createNode(this, config);

// Login
eWeLinkConnect.login(RED, this, config).then(connection => {
// Open websocket for real-time events
connection.openWebSocket(data => {
// Return the payload if the device ID was not set or the event
// is related to the selected one. Also handle only objects
// to ignore control packages such as "pong".
if (typeof data === 'object' && (!config.deviceId || data['deviceid'] === config.deviceId)) {
this.send({ payload: data });
}
});
}).catch((error) => this.error(error));
}

// Register node
RED.nodes.registerType('ewelink-event-listener', EventListenerNode);
}
210 changes: 210 additions & 0 deletions test/event-listener_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const helper = require('node-red-node-test-helper');
const credentialsNode = require('../src/credentials/credentials');
const eventListenerNode = require('../src/event-listener/event-listener');
const eWeLinkConnect = require('../src/utils/ewelink-connect');

describe('Event Listener Node', () => {

const credentials = {
n1: {
email: 'dummy@dummy.tld',
password: 'abcd',
region: 'eu'
}
};

beforeEach(done => {
helper.startServer(done);
});

afterEach(done => {
helper.unload().then(() => {
helper.stopServer(done);
});
});

it('should be loaded', done => {
const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '', name: 'Device 123' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
const n2 = helper.getNode('n2');

n2.should.have.property('name', 'Device 123');

done();
});
});

it('should open a websocket connection', done => {
const connection = { openWebSocket() { } };

const openWebSocketStub = sinon.stub(connection, 'openWebSocket')
.callsFake((callback) => callback({ someData: 'new event' }));

const loginStub = sinon.stub(eWeLinkConnect, 'login')
.callsFake(() => Promise.resolve(connection));

const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
setTimeout(() => {
sinon.assert.calledOnce(openWebSocketStub);
sinon.assert.calledOnce(loginStub);

openWebSocketStub.restore();
loginStub.restore();

done();
});
});
});

it('should return any events if device id is not set', done => {
const connection = { openWebSocket() { } };

const openWebSocketStub = sinon.stub(connection, 'openWebSocket')
.callsFake((callback) => callback({ deviceid: '1234' }));

const loginStub = sinon.stub(eWeLinkConnect, 'login')
.callsFake(() => Promise.resolve(connection));

const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '', wires: [['n3']] },
{ id: 'n3', type: 'helper' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
const n3 = helper.getNode('n3');

n3.on('input', msg => {
expect(msg).to.deep.include({ payload: { deviceid: '1234' } });

done();
});

setTimeout(() => {
sinon.assert.calledOnce(openWebSocketStub);
sinon.assert.calledOnce(loginStub);

openWebSocketStub.restore();
loginStub.restore();
});
});
});

it('should not return event if device id does not match', done => {
const connection = { openWebSocket() { } };

const openWebSocketStub = sinon.stub(connection, 'openWebSocket')
.callsFake((callback) => callback({ deviceid: '1234' }));

const loginStub = sinon.stub(eWeLinkConnect, 'login')
.callsFake(() => Promise.resolve(connection));

const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '1234abc', wires: [['n3']] },
{ id: 'n3', type: 'helper' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
const n3 = helper.getNode('n3');

let handled = false;
n3.on('input', () => {
handled = true;
});

setTimeout(() => {
sinon.assert.calledOnce(openWebSocketStub);
sinon.assert.calledOnce(loginStub);

openWebSocketStub.restore();
loginStub.restore();

setTimeout(() => {
if (! handled) {
done();
}
});
});
});
});

it('should not return control messages like "pong"', done => {
const connection = { openWebSocket() { } };

const openWebSocketStub = sinon.stub(connection, 'openWebSocket')
.callsFake((callback) => callback('pong'));

const loginStub = sinon.stub(eWeLinkConnect, 'login')
.callsFake(() => Promise.resolve(connection));

const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '', wires: [['n3']] },
{ id: 'n3', type: 'helper' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
const n3 = helper.getNode('n3');

let handled = false;
n3.on('input', () => {
handled = true;
});

setTimeout(() => {
sinon.assert.calledOnce(openWebSocketStub);
sinon.assert.calledOnce(loginStub);

openWebSocketStub.restore();
loginStub.restore();

setTimeout(() => {
if (! handled) {
done();
}
});
});
});
});

it('should return event if device id matches', done => {
const connection = { openWebSocket() { } };

const openWebSocketStub = sinon.stub(connection, 'openWebSocket')
.callsFake((callback) => callback({ deviceid: '1234' }));

const loginStub = sinon.stub(eWeLinkConnect, 'login')
.callsFake(() => Promise.resolve(connection));

const flow = [
{ id: 'n1', type: 'ewelink-credentials' },
{ id: 'n2', type: 'ewelink-event-listener', auth: 'n1', deviceId: '1234', wires: [['n3']] },
{ id: 'n3', type: 'helper' }
];
helper.load([credentialsNode, eventListenerNode], flow, credentials, () => {
const n3 = helper.getNode('n3');

n3.on('input', msg => {
expect(msg).to.deep.include({ payload: { deviceid: '1234' } });

done();
});

setTimeout(() => {
sinon.assert.calledOnce(openWebSocketStub);
sinon.assert.calledOnce(loginStub);

openWebSocketStub.restore();
loginStub.restore();
});
});
});
});

0 comments on commit b758726

Please sign in to comment.