Skip to content

Commit

Permalink
Feature/configurable address (#27)
Browse files Browse the repository at this point in the history
* Added dynamically configurable IP address via message property.
* Additional tests

Co-authored-by: Andrew Larcombe <[email protected]>
Co-authored-by: biddster
  • Loading branch information
biddster and andrewl authored Dec 21, 2022
1 parent b1e701c commit dd6980d
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 33 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Alternatively, use the Palette Manager in Node-RED.
Drag this node on to a worksheet and double click it. Enter the IP address of the plug on your
network. Save and deploy.

# Dynamic configuration of IP address

Alternatively you can dynamically set the IP address of the plug. To do this set the message property 'hs100_address' to the address of the plug that you wish to control.

# Actuations

This node supports a number of actuations that are invoked by sending a msg.topic or msg.payload
Expand Down
41 changes: 22 additions & 19 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
category: 'output',
color: '#f37a33',
defaults: {
name: {value: ''},
host: {value: ''}
name: { value: '' },
host: { value: '' },
},
inputs: 1,
outputs: 1,
Expand All @@ -37,7 +37,7 @@
return this.name || 'hs100';
},
paletteLabel: 'hs100',
align: 'right'
align: 'right',
});
</script>

Expand All @@ -51,35 +51,38 @@
<label for="node-input-host"><i class="icon-tag"></i>TP-LINK Switch IP Address</label>
<input type="text" id="node-input-host" placeholder="">
</div>

<div class="form-row">
<div class="form-tips" id="node-tip">Tip: Setting the message property 'hs100_address' overrides the Address field above, so you can leave it empty.</div>
</div>
</script>

<script type="text/x-red" data-help-name="hs100">
<h1>node-red-contrib-hs100</h1>
<h1>node-red-contrib-hs100</h1>

<p>This Node-RED node is for controlling tp-link Wi-Fi Smart Plug - Model HS100.</p>
<p>This Node-RED node is for controlling tp-link Wi-Fi Smart Plug - Model HS100.</p>

<p>This node has only been tested with a HS100(UK). The HS100 is also available in US and EU plug versions. We expect they will work too.</p>
<p>This node has only been tested with a HS100(UK). The HS100 is also available in US and EU plug versions. We expect they will work too.</p>

<p>This node simply wraps the excellent work here <a href="https://github.com/czjs2/hs100-api">https://github.com/czjs2/hs100-api</a>. </p>
<p>This node simply wraps the excellent work here <a href="https://github.com/czjs2/hs100-api">https://github.com/czjs2/hs100-api</a>. </p>

<h1>Configuration</h1>
<h1>Configuration</h1>

<p>Drag this node on to a worksheet and double click it. Enter the IP address of the plug on your network. Save and deploy.</p>
<p>Drag this node on to a worksheet and double click it. Enter the IP address of the plug on your network. Save and deploy.</p>

<h1>Actuation</h1>
<p>If the message property 'hs100_address' is set it will <strong>override</strong> whatever is set in the Address field on the node's configuration screen.</p>

<h2>Turn on</h2>
<h1>Actuation</h1>

<p>To turn the HS100 on, send a message to this node's input with the topic or payload set to <code>on</code>.</p>
<h2>Turn on</h2>

<h2>Turn off</h2>
<p>To turn the HS100 on, send a message to this node's input with the topic or payload set to <code>on</code>.</p>

<p>To turn the HS100 off, send a message to this node's input with the topic or payload set to <code>off</code>.</p>
<h2>Turn off</h2>

<h2>Obtain power consumption data</h2>
<p>To turn the HS100 off, send a message to this node's input with the topic or payload set to <code>off</code>.</p>

<p>To obtain the power consumption, send a message to this node's input with the topic or payload set to `consumption`. The consumption data will
be sent via this node's output in <code>msg.payload</code>.</p>
<h2>Obtain power consumption data</h2>

</script>
<p>To obtain the power consumption, send a message to this node's input with the topic or payload set to `consumption`. The consumption data will
be sent via this node's output in <code>msg.payload</code>.</p>
</script>
12 changes: 10 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ module.exports = function hs100(RED) {
// eslint-disable-next-line consistent-this
const node = this;
const client = hs100.newHs100Client();
const plug = client.getPlug({ host: config.host });
let plug = null;
if (config.host) {
plug = client.getPlug({ host: config.host });
}

const errorHandler = function (err) {
node.error(err);
Expand All @@ -87,7 +90,12 @@ module.exports = function hs100(RED) {
};

node.on('input', (msg) => {
if (msg.payload === 'on' || msg.topic === 'on') {
if (msg.hs100_address) {
plug = client.getPlug({ host: msg.hs100_address });
}
if (!plug) {
errorHandler(new Error('You must set config.host or msg.hs100_address'));
} else if (msg.payload === 'on' || msg.topic === 'on') {
setPowerState(true);
} else if (msg.payload === 'off' || msg.topic === 'off') {
setPowerState(false);
Expand Down
67 changes: 55 additions & 12 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const _ = require('lodash');
describe('hs100', function () {
this.timeout(60000);
it('should turn a socket on', function (done) {
const node = NewNode();
const node = NewNode({ host: 'localhost' });
node.emit('input', { payload: 'on' });
setTimeout(function () {
Assert.strictEqual(node.status().text, 'on');
Expand All @@ -42,8 +42,22 @@ describe('hs100', function () {
done();
}, 10);
});
// it('should turn a socket on after host reconfiguration', function (done) {
// const node = NewNode({ host: 'localhost' });
// node.emit('input', { hs100_address: 'localhost2', payload: 'on' });
// setTimeout(function () {
// Assert.strictEqual(node.status().text, 'on');
// Assert.strictEqual(node.status().shape, 'dot');
// Assert.deepEqual(node.sent(0).payload, {
// mocked: 'on',
// host: 'localhost',
// });
// node.emit('close');
// done();
// }, 10);
// });
it('should turn a socket off', function (done) {
const node = NewNode();
const node = NewNode({ host: 'localhost' });
node.emit('input', { payload: 'off' });
setTimeout(function () {
Assert.strictEqual(node.status().text, 'off');
Expand All @@ -53,52 +67,81 @@ describe('hs100', function () {
}, 10);
});
it('should emit consumption data', function (done) {
const node = NewNode();
const node = NewNode({ host: 'localhost' });
node.emit('input', { payload: 'consumption' });
setTimeout(function () {
Assert.deepEqual(node.sent(0).payload, { mocked: 'getConsumption' });
Assert.deepEqual(node.sent(0).payload, {
mocked: 'getConsumption',
host: 'localhost',
});
Assert.strictEqual(node.sent(0).topic, 'consumption');
node.emit('close');
done();
}, 10);
});
it('should emit sysinfo data', function (done) {
const node = NewNode();
const node = NewNode({ host: 'localhost' });
node.emit('input', { topic: 'SysInfo' });
setTimeout(function () {
Assert.deepEqual(node.sent(0).payload, { mocked: 'getSysInfo' });
Assert.deepEqual(node.sent(0).payload, { mocked: 'getSysInfo', host: 'localhost' });
Assert.strictEqual(node.sent(0).topic, 'SysInfo');
node.emit('close');
done();
}, 10);
});
it('should emit sysinfo data after hs100 address reconfiguration', function (done) {
const node = NewNode({ host: 'localhost' });
node.emit('input', { hs100_address: 'localhost2', topic: 'SysInfo' });
setTimeout(function () {
Assert.deepEqual(node.sent(0).payload, {
mocked: 'getSysInfo',
host: 'localhost2',
});
Assert.strictEqual(node.sent(0).topic, 'SysInfo');
node.emit('close');
done();
}, 10);
});
it('should handle errors', function (done) {
const node = NewNode();
const node = NewNode({ host: 'localhost' });
node.emit('input', { payload: 'wibble' });
setTimeout(function () {
Assert.isNotNull(node.error(0));
node.emit('close');
done();
}, 10);
});
it('should handle unconfigured host errors', function (done) {
const node = NewNode({});
node.emit('input', { payload: 'wibble' });
setTimeout(function () {
Assert.deepEqual(
node.error(0).message,
'You must set config.host or msg.hs100_address'
);
node.emit('close');
done();
}, 10);
});
});

function NewNode() {
return Mock(NodeRedModule, {}, null, function (module, node) {
function NewNode(config) {
return Mock(NodeRedModule, config, null, function (module, node) {
module.newHs100Client = function () {
return {
getPlug: function () {
getPlug: function (config) {
const plugConfig = config;
const plug = {};
_.values(module.supportedActuations).forEach(function (method) {
plug[method] = function () {
return new Promise(function (resolve, reject) {
resolve({ mocked: method });
resolve({ mocked: method, host: plugConfig.host });
});
};
});
plug.setPowerState = function (state) {
return new Promise(function (resolve, reject) {
resolve({ mocked: state });
resolve({ mocked: state, host: plugConfig.host });
});
};

Expand Down

0 comments on commit dd6980d

Please sign in to comment.