This commit is contained in:
Fredrik Åhs 2018-01-07 23:21:26 +01:00
parent 6e37268879
commit c0c97aed87
6 changed files with 173 additions and 130 deletions

View File

@ -23,7 +23,7 @@ Lights can be controlled by sending an objet with one or more of the following p
* `saturation` `number` `[0,100]` Sets the saturation of the light. Only for CWS. (UNTESTED) * `saturation` `number` `[0,100]` Sets the saturation of the light. Only for CWS. (UNTESTED)
### Output ### Output
If the node is set to observe and the target light is updated or if triggered manually by sending a `"status"` request as `msg.payload` to the node, the node will send a `msg.payload` for which the `light` property is the current status of the light. If the node is set to observe and the target light is updated or if triggered manually by sending a `"status"` request as `msg.payload` to the node, the node will send a `msg.payload` with the current status of the light.
* `id` `number` The id of the light. * `id` `number` The id of the light.
* `name` `string` The given name of the light. * `name` `string` The given name of the light.
* `model` `string` The model of the light. * `model` `string` The model of the light.
@ -40,3 +40,13 @@ If the node is set to observe and the target light is updated or if triggered ma
* `seen` `number` When the light was last interacted with by the gateway (or similar), measured in epoch time. * `seen` `number` When the light was last interacted with by the gateway (or similar), measured in epoch time.
* `type` `number` The type of device where 2 is light. * `type` `number` The type of device where 2 is light.
* `power` `number` The type of power source powering the light. Will most likely always be 1. * `power` `number` The type of power source powering the light. Will most likely always be 1.
## Changelog
### 0.1.2
* Moved output status object from `msg.payload.light` to `msg.payload`.
* Updated security code, identity and PSK to be saved as credentials in config.
* Updated info panels and tweaked node appearance.
### 0.1.1
* Published to NPM

105
dist/node-tradfri.html vendored
View File

@ -1,13 +1,15 @@
<!-- ======= CONNECTION ========= --> <!-- Config Node -->
<script type="text/javascript"> <script type="text/javascript">
RED.nodes.registerType('tradfri-connection',{ RED.nodes.registerType('tradfri-connection',{
category: 'config', category: 'config',
defaults: { defaults: {
name: { value:"" },
address: { value:"", required:true }, address: { value:"", required:true },
securityCode: { value:"" }, name: { value:"" }
identity: { value:"" }, },
psk: { value:"" }, credentials: {
securityCode: { type:"text" },
identity: { type:"text" },
psk: { type:"text" }
}, },
label: function() { label: function() {
return this.name || "tradfri@" + this.address; return this.name || "tradfri@" + this.address;
@ -17,28 +19,37 @@
<script type="text/x-red" data-template-name="tradfri-connection"> <script type="text/x-red" data-template-name="tradfri-connection">
<div class="form-row"> <div class="form-row">
<label for="node-config-input-name"><i class="icon-bookmark"></i> Name</label> <label for="node-config-input-address"><i class="fa fa-globe"></i> Address</label>
<input type="text" id="node-config-input-name">
</div>
<div class="form-row">
<label for="node-config-input-address"><i class="icon-bookmark"></i> Address</label>
<input type="text" id="node-config-input-address"> <input type="text" id="node-config-input-address">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-securityCode"><i class="icon-bookmark"></i> Security Code</label> <label for="node-config-input-securityCode"><i class="fa fa-gears"></i> Security Code</label>
<input type="text" id="node-config-input-securityCode"> <input type="text" id="node-config-input-securityCode">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-identity"><i class="icon-bookmark"></i> Identity</label> <label for="node-config-input-identity"><i class="fa fa-address-card-o"></i> Identity</label>
<input type="text" id="node-config-input-identity"> <input type="text" id="node-config-input-identity">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-psk"><i class="icon-bookmark"></i> Pre-shared key</label> <label for="node-config-input-psk"><i class="fa fa-lock"></i> Pre-shared key</label>
<input type="text" id="node-config-input-psk"> <input type="text" id="node-config-input-psk">
</div> </div>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name">
</div>
</script> </script>
<!-- ======= THREADFREE ========= --> <script type="text/x-red" data-help-name="tradfri-connection">
<ul>
<li><i class="fa fa-globe"></i> <b>Address:</b> The address to your gateway.</li>
<li><i class="fa fa-gears"></i> <b>Security code:</b> The security code on the backside of your gateway. You only need to enter this if you <em>don't</em> have an identiy and PSK. In that case identity and PSK will be generated for you using the security key.</li>
<li><i class="fa fa-address-card-o"></i> <b>Address:</b> The identiy used to access the gateway. Used together with a PSK.</li>
<li><i class="fa fa-lock"></i> <b>Pre-shared key:</b> The passkey used to access the gateway. Used together with an identity.</li>
</ul>
</script>
<!--- I/O Node -->
<script type="text/javascript"> <script type="text/javascript">
var updateDevices = (currentDeviceId) => { var updateDevices = (currentDeviceId) => {
let configNodeId = $('#node-input-connection').find(":selected").val(); let configNodeId = $('#node-input-connection').find(":selected").val();
@ -61,7 +72,7 @@
RED.nodes.registerType('tradfri',{ RED.nodes.registerType('tradfri',{
category: 'function', category: 'function',
color: '#84E87A', color: '#9FE597',
defaults: { defaults: {
name: {value:""}, name: {value:""},
deviceId: {value: "", required:true, validate:RED.validators.number()}, deviceId: {value: "", required:true, validate:RED.validators.number()},
@ -69,9 +80,10 @@
connection: {value:"", type:"tradfri-connection"}, connection: {value:"", type:"tradfri-connection"},
observe: { value:true, required: true }, observe: { value:true, required: true },
}, },
align: 'right',
inputs:1, inputs:1,
outputs:1, outputs:1,
icon: "light.png", icon: 'light.png',
label: function() { label: function() {
return this.deviceName || "tradfri"; return this.deviceName || "tradfri";
}, },
@ -96,11 +108,7 @@
<script type="text/x-red" data-template-name="tradfri"> <script type="text/x-red" data-template-name="tradfri">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-connection"><i class="fa fa-cog"></i> Connection</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-connection"><i class="fa fa-bookmark"></i> Connection</label>
<input type="text" id="node-input-connection" placeholder="Connection"> <input type="text" id="node-input-connection" placeholder="Connection">
</div> </div>
<div class="form-row"> <div class="form-row">
@ -120,27 +128,50 @@
<input type="checkbox" id="node-input-observe" style="display: inline-block; width: auto; vertical-align: top;"> <input type="checkbox" id="node-input-observe" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-observe" style="width: 70%;" >Observe device?</label> <label for="node-input-observe" style="width: 70%;" >Observe device?</label>
</div> </div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script> </script>
<script type="text/x-red" data-help-name="tradfri"> <script type="text/x-red" data-help-name="tradfri">
<p>Interface for IKEA Tradfri devices</p> <p>Interface for IKEA Tradfri devices</p>
<h3>Inputs</h3> <h3>Inputs</h3>
<dl class="message-properties"> <dl class="message-properties">
<dt> payload <span class="property-type"> string </span> </dt> <dt> payload <span class="property-type"> string | object</span> </dt>
<dd> A single command can be sent as a string to the node. <dd> <dd> The payload can either be a single command to the node (such as a status update request) sent as a string or an object with one or more commands targeting the light.</dd>
<dt> payload <span class="property-type"> object </span> </dt>
<dd> An object with one or more commands targeting the light. </dd>
</dl> </dl>
<h3>Outputs</h3> <h3>Outputs</h3>
<dl class="message-properties"> <dl class="message-properties">
<dt> payload.light <span class="property-type"> object </span></dt> <dt> payload <span class="property-type"> object </span></dt>
<dd> The status of the light. </dd> <dd> The status of the light. </dd>
</dl> </dl>
<h3>Details</h3> <h3>Details</h3>
The tradfri node acts as both input and output for a IKEA Tradfri lighbulb. The tradfri node acts as both input and output for a IKEA Tradfri lighbulb.
If the node is set to observe it will send a message with the lights current properties as payload every time the light is updated: <h2>Controlling the node</h2>
Nodes can be programmatically controlled by sending a message with <code>msg.payload</code> set to one of the following strings:
<ul>
<li><code>"status"</code> The node will output the current status of its target light.
</ul>
<h2>Controlling the lights</h2>
Lights can be controlled by sending an objet with one or more of the following properties as <code>msg.payload</code> to the node.
<ul>
<!-- <li><code>id</code><code>number</code> The id of the light <li>
<li><code>name</code><code>string</code> The given name of the light <li> -->
<li><code>on</code><code>boolean</code><li> Turn the light on or off.</li>
<li><code>brightness</code><code>number</code> The brightness of the light [0,100]</li>
<li><code>colorTemperature</code><code>number</code> The color temperature of the light [0,100]</li>
<li><code>color</code><code>string</code> Sets the color of the light. For WS-bulbs, <code>F5FAF6</code>, <code>F1E0B5</code> and <code>EFD275</code> will set the light to the default cold, normal and warm temperatures respectively.</li>
<li><code>transition</code><code>number</code> The default transition time for operations. Will only work for single operation commands and not for on/off. Defaults to 0. </li>
<li><code>hue</code><code>number</code> Sets the hue of the light. Only for CWS. [0,365] (UNTESTED)</li>
<li><code>saturation</code><code>number</code> Sets the saturation of the light. Only for CWS. [0,100] (UNTESTED)</li>
</ul>
<h2>Observing</h2>
If the node is set to observe it will send a message with the lights current properties as payload every time the light is updated:
<ul> <ul>
<li><code>id</code><code>number</code> The id of the light </li> <li><code>id</code><code>number</code> The id of the light </li>
<li><code>name</code><code>string</code> The given name of the light </li> <li><code>name</code><code>string</code> The given name of the light </li>
@ -160,24 +191,4 @@
<li><code>power</code><code>number</code> The type of power source powering the light. Will most likely always be 1.</li> <li><code>power</code><code>number</code> The type of power source powering the light. Will most likely always be 1.</li>
</ul> </ul>
<h2>Controlling the node</h2>
The can be programmatically controlled by sending a message with <code>msg.payload</code> set to one of the following:
<ul>
<li><code>status</code> The node will output the current status of its target light.
</ul>
<h2>Controlling the lights</h2>
Lights can be controlled by sending an objet with one or more of the following properties as <code>msg.payload</code> to the node.
<ul>
<!-- <li><code>id</code><code>number</code> The id of the light <li>
<li><code>name</code><code>string</code> The given name of the light <li> -->
<li><code>on</code><code>boolean</code><li> Turn the light on or off.</li>
<li><code>brightness</code><code>number</code> The brightness of the light [0,100]</li>
<li><code>colorTemperature</code><code>number</code> The color temperature of the light [0,100]</li>
<li><code>color</code><code>string</code> Sets the color of the light. For WS-bulbs, <code>F5FAF6</code>, <code>F1E0B5</code> and <code>EFD275</code> will set the light to the default cold, normal and warm temperatures respectively.</li>
<li><code>transition</code><code>number</code> The default transition time for operations. Will only work for single operation commands and not for on/off. Defaults to 0. </li>
<li><code>hue</code><code>number</code> Sets the hue of the light. Only for CWS. [0,365] (UNTESTED)</li>
<li><code>saturation</code><code>number</code> Sets the saturation of the light. Only for CWS. [0,100] (UNTESTED)</li>
</ul>
</script> </script>

39
dist/node-tradfri.js vendored
View File

@ -60,9 +60,9 @@ module.exports = function (RED) {
RED.nodes.createNode(node, config); RED.nodes.createNode(node, config);
node.name = config.name; node.name = config.name;
node.address = config.address; node.address = config.address;
node.securityCode = config.securityCode; node.securityCode = node.credentials.securityCode;
node.identity = config.identity; node.identity = node.credentials.identity;
node.psk = config.psk; node.psk = node.credentials.psk;
if ((node.identity == null && node.psk != null) || (node.identity != null && node.psk == null)) { if ((node.identity == null && node.psk != null) || (node.identity != null && node.psk == null)) {
RED.log.error("Must provide both identity and PSK or leave both blank to generate new credentials from security code."); RED.log.error("Must provide both identity and PSK or leave both blank to generate new credentials from security code.");
} }
@ -169,7 +169,7 @@ module.exports = function (RED) {
for (let instanceId in _listeners) { for (let instanceId in _listeners) {
if (_listeners[instanceId].hasOwnProperty(nodeId)) { if (_listeners[instanceId].hasOwnProperty(nodeId)) {
delete _listeners[instanceId][nodeId]; delete _listeners[instanceId][nodeId];
RED.log.debug(`[Tradfri: ${nodeId}] unregistered event listeners`); RED.log.info(`[Tradfri: ${nodeId}] unregistered event listeners`);
} }
} }
}; };
@ -179,7 +179,13 @@ module.exports = function (RED) {
RED.log.debug(`[Tradfri: ${node.id}] Config was closed`); RED.log.debug(`[Tradfri: ${node.id}] Config was closed`);
}); });
} }
RED.nodes.registerType("tradfri-connection", TradfriConnectionNode); RED.nodes.registerType("tradfri-connection", TradfriConnectionNode, {
credentials: {
securityCode: { type: "text" },
identity: { type: "text" },
psk: { type: "text" }
}
});
function TradfriNode(config) { function TradfriNode(config) {
var node = this; var node = this;
RED.nodes.createNode(node, config); RED.nodes.createNode(node, config);
@ -189,6 +195,9 @@ module.exports = function (RED) {
node.observe = config.observe; node.observe = config.observe;
var _config = RED.nodes.getNode(config.connection); var _config = RED.nodes.getNode(config.connection);
var _prev = {}; var _prev = {};
var _send = (payload) => {
node.send({ topic: "tradfri", payload: payload });
};
var _getPayload = (accessory) => { var _getPayload = (accessory) => {
let light = lightFromAccessory(accessory); let light = lightFromAccessory(accessory);
light['prev'] = Object.assign({}, _prev); light['prev'] = Object.assign({}, _prev);
@ -197,7 +206,7 @@ module.exports = function (RED) {
var _deviceUpdated = (accessory) => { var _deviceUpdated = (accessory) => {
let ret = _getPayload(accessory); let ret = _getPayload(accessory);
_prev = lightFromAccessory(accessory); _prev = lightFromAccessory(accessory);
node.send({ payload: { light: ret } }); _send(ret);
RED.log.trace(`[Tradfri: ${node.id}] recieved update for '${accessory.name}' (${accessory.instanceId})`); RED.log.trace(`[Tradfri: ${node.id}] recieved update for '${accessory.name}' (${accessory.instanceId})`);
}; };
var _getTargetId = (msg) => { var _getTargetId = (msg) => {
@ -215,24 +224,20 @@ module.exports = function (RED) {
throw new Error('No valid target device'); throw new Error('No valid target device');
} }
}; };
var _handleDirectStatus = (msg) => __awaiter(this, void 0, void 0, function* () { var _handleDirectStatus = () => __awaiter(this, void 0, void 0, function* () {
try { try {
let client = yield _config.getClient(); let client = yield _config.getClient();
let res = yield client.request('15001/' + node.deviceId, 'get'); let res = yield client.request('15001/' + node.deviceId, 'get');
msg.payload = res; _send(res);
node.send(msg);
} }
catch (e) { catch (e) {
msg.payload = e; _send(e);
node.send(msg);
} }
}); });
var _handleStatus = (msg) => __awaiter(this, void 0, void 0, function* () { var _handleStatus = () => __awaiter(this, void 0, void 0, function* () {
try { try {
let accessory = yield _config.getLight(node.deviceId); let accessory = yield _config.getLight(node.deviceId);
msg.payload.light = _getPayload(accessory); _send(_getPayload(accessory));
delete msg.payload.status;
node.send(msg);
RED.log.trace(`[Tradfri: ${node.id}] Status request successful`); RED.log.trace(`[Tradfri: ${node.id}] Status request successful`);
} }
catch (e) { catch (e) {
@ -290,10 +295,10 @@ module.exports = function (RED) {
let isDirect = msg.payload.hasOwnProperty('direct'); let isDirect = msg.payload.hasOwnProperty('direct');
let isStatus = msg.payload.hasOwnProperty('status'); let isStatus = msg.payload.hasOwnProperty('status');
if (isDirect && isStatus) { if (isDirect && isStatus) {
_handleDirectStatus(msg); _handleDirectStatus();
} }
else if (isStatus) { else if (isStatus) {
_handleStatus(msg); _handleStatus();
} }
else if (isDirect) { else if (isDirect) {
_handleDirectLightOp(msg.payload); _handleDirectLightOp(msg.payload);

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red-contrib-node-tradfri", "name": "node-red-contrib-node-tradfri",
"version": "0.1.1", "version": "0.1.2",
"description": "Node-RED node to utilize IKEA Trådfri devices. Fully implemented in Node.js.", "description": "Node-RED node to utilize IKEA Trådfri devices. Fully implemented in Node.js.",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

View File

@ -1,13 +1,15 @@
<!-- ======= CONNECTION ========= --> <!-- Config Node -->
<script type="text/javascript"> <script type="text/javascript">
RED.nodes.registerType('tradfri-connection',{ RED.nodes.registerType('tradfri-connection',{
category: 'config', category: 'config',
defaults: { defaults: {
name: { value:"" },
address: { value:"", required:true }, address: { value:"", required:true },
securityCode: { value:"" }, name: { value:"" }
identity: { value:"" }, },
psk: { value:"" }, credentials: {
securityCode: { type:"text" },
identity: { type:"text" },
psk: { type:"text" }
}, },
label: function() { label: function() {
return this.name || "tradfri@" + this.address; return this.name || "tradfri@" + this.address;
@ -17,28 +19,37 @@
<script type="text/x-red" data-template-name="tradfri-connection"> <script type="text/x-red" data-template-name="tradfri-connection">
<div class="form-row"> <div class="form-row">
<label for="node-config-input-name"><i class="icon-bookmark"></i> Name</label> <label for="node-config-input-address"><i class="fa fa-globe"></i> Address</label>
<input type="text" id="node-config-input-name">
</div>
<div class="form-row">
<label for="node-config-input-address"><i class="icon-bookmark"></i> Address</label>
<input type="text" id="node-config-input-address"> <input type="text" id="node-config-input-address">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-securityCode"><i class="icon-bookmark"></i> Security Code</label> <label for="node-config-input-securityCode"><i class="fa fa-gears"></i> Security Code</label>
<input type="text" id="node-config-input-securityCode"> <input type="text" id="node-config-input-securityCode">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-identity"><i class="icon-bookmark"></i> Identity</label> <label for="node-config-input-identity"><i class="fa fa-address-card-o"></i> Identity</label>
<input type="text" id="node-config-input-identity"> <input type="text" id="node-config-input-identity">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-config-input-psk"><i class="icon-bookmark"></i> Pre-shared key</label> <label for="node-config-input-psk"><i class="fa fa-lock"></i> Pre-shared key</label>
<input type="text" id="node-config-input-psk"> <input type="text" id="node-config-input-psk">
</div> </div>
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name">
</div>
</script> </script>
<!-- ======= THREADFREE ========= --> <script type="text/x-red" data-help-name="tradfri-connection">
<ul>
<li><i class="fa fa-globe"></i> <b>Address:</b> The address to your gateway.</li>
<li><i class="fa fa-gears"></i> <b>Security code:</b> The security code on the backside of your gateway. You only need to enter this if you <em>don't</em> have an identiy and PSK. In that case identity and PSK will be generated for you using the security key.</li>
<li><i class="fa fa-address-card-o"></i> <b>Address:</b> The identiy used to access the gateway. Used together with a PSK.</li>
<li><i class="fa fa-lock"></i> <b>Pre-shared key:</b> The passkey used to access the gateway. Used together with an identity.</li>
</ul>
</script>
<!--- I/O Node -->
<script type="text/javascript"> <script type="text/javascript">
var updateDevices = (currentDeviceId) => { var updateDevices = (currentDeviceId) => {
let configNodeId = $('#node-input-connection').find(":selected").val(); let configNodeId = $('#node-input-connection').find(":selected").val();
@ -61,7 +72,7 @@
RED.nodes.registerType('tradfri',{ RED.nodes.registerType('tradfri',{
category: 'function', category: 'function',
color: '#84E87A', color: '#9FE597',
defaults: { defaults: {
name: {value:""}, name: {value:""},
deviceId: {value: "", required:true, validate:RED.validators.number()}, deviceId: {value: "", required:true, validate:RED.validators.number()},
@ -69,9 +80,10 @@
connection: {value:"", type:"tradfri-connection"}, connection: {value:"", type:"tradfri-connection"},
observe: { value:true, required: true }, observe: { value:true, required: true },
}, },
align: 'right',
inputs:1, inputs:1,
outputs:1, outputs:1,
icon: "light.png", icon: 'light.png',
label: function() { label: function() {
return this.deviceName || "tradfri"; return this.deviceName || "tradfri";
}, },
@ -96,11 +108,7 @@
<script type="text/x-red" data-template-name="tradfri"> <script type="text/x-red" data-template-name="tradfri">
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i> Name</label> <label for="node-input-connection"><i class="fa fa-cog"></i> Connection</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-connection"><i class="fa fa-bookmark"></i> Connection</label>
<input type="text" id="node-input-connection" placeholder="Connection"> <input type="text" id="node-input-connection" placeholder="Connection">
</div> </div>
<div class="form-row"> <div class="form-row">
@ -120,27 +128,50 @@
<input type="checkbox" id="node-input-observe" style="display: inline-block; width: auto; vertical-align: top;"> <input type="checkbox" id="node-input-observe" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-observe" style="width: 70%;" >Observe device?</label> <label for="node-input-observe" style="width: 70%;" >Observe device?</label>
</div> </div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script> </script>
<script type="text/x-red" data-help-name="tradfri"> <script type="text/x-red" data-help-name="tradfri">
<p>Interface for IKEA Tradfri devices</p> <p>Interface for IKEA Tradfri devices</p>
<h3>Inputs</h3> <h3>Inputs</h3>
<dl class="message-properties"> <dl class="message-properties">
<dt> payload <span class="property-type"> string </span> </dt> <dt> payload <span class="property-type"> string | object</span> </dt>
<dd> A single command can be sent as a string to the node. <dd> <dd> The payload can either be a single command to the node (such as a status update request) sent as a string or an object with one or more commands targeting the light.</dd>
<dt> payload <span class="property-type"> object </span> </dt>
<dd> An object with one or more commands targeting the light. </dd>
</dl> </dl>
<h3>Outputs</h3> <h3>Outputs</h3>
<dl class="message-properties"> <dl class="message-properties">
<dt> payload.light <span class="property-type"> object </span></dt> <dt> payload <span class="property-type"> object </span></dt>
<dd> The status of the light. </dd> <dd> The status of the light. </dd>
</dl> </dl>
<h3>Details</h3> <h3>Details</h3>
The tradfri node acts as both input and output for a IKEA Tradfri lighbulb. The tradfri node acts as both input and output for a IKEA Tradfri lighbulb.
If the node is set to observe it will send a message with the lights current properties as payload every time the light is updated: <h2>Controlling the node</h2>
Nodes can be programmatically controlled by sending a message with <code>msg.payload</code> set to one of the following strings:
<ul>
<li><code>"status"</code> The node will output the current status of its target light.
</ul>
<h2>Controlling the lights</h2>
Lights can be controlled by sending an objet with one or more of the following properties as <code>msg.payload</code> to the node.
<ul>
<!-- <li><code>id</code><code>number</code> The id of the light <li>
<li><code>name</code><code>string</code> The given name of the light <li> -->
<li><code>on</code><code>boolean</code><li> Turn the light on or off.</li>
<li><code>brightness</code><code>number</code> The brightness of the light [0,100]</li>
<li><code>colorTemperature</code><code>number</code> The color temperature of the light [0,100]</li>
<li><code>color</code><code>string</code> Sets the color of the light. For WS-bulbs, <code>F5FAF6</code>, <code>F1E0B5</code> and <code>EFD275</code> will set the light to the default cold, normal and warm temperatures respectively.</li>
<li><code>transition</code><code>number</code> The default transition time for operations. Will only work for single operation commands and not for on/off. Defaults to 0. </li>
<li><code>hue</code><code>number</code> Sets the hue of the light. Only for CWS. [0,365] (UNTESTED)</li>
<li><code>saturation</code><code>number</code> Sets the saturation of the light. Only for CWS. [0,100] (UNTESTED)</li>
</ul>
<h2>Observing</h2>
If the node is set to observe it will send a message with the lights current properties as payload every time the light is updated:
<ul> <ul>
<li><code>id</code><code>number</code> The id of the light </li> <li><code>id</code><code>number</code> The id of the light </li>
<li><code>name</code><code>string</code> The given name of the light </li> <li><code>name</code><code>string</code> The given name of the light </li>
@ -160,24 +191,4 @@
<li><code>power</code><code>number</code> The type of power source powering the light. Will most likely always be 1.</li> <li><code>power</code><code>number</code> The type of power source powering the light. Will most likely always be 1.</li>
</ul> </ul>
<h2>Controlling the node</h2>
The can be programmatically controlled by sending a message with <code>msg.payload</code> set to one of the following:
<ul>
<li><code>status</code> The node will output the current status of its target light.
</ul>
<h2>Controlling the lights</h2>
Lights can be controlled by sending an objet with one or more of the following properties as <code>msg.payload</code> to the node.
<ul>
<!-- <li><code>id</code><code>number</code> The id of the light <li>
<li><code>name</code><code>string</code> The given name of the light <li> -->
<li><code>on</code><code>boolean</code><li> Turn the light on or off.</li>
<li><code>brightness</code><code>number</code> The brightness of the light [0,100]</li>
<li><code>colorTemperature</code><code>number</code> The color temperature of the light [0,100]</li>
<li><code>color</code><code>string</code> Sets the color of the light. For WS-bulbs, <code>F5FAF6</code>, <code>F1E0B5</code> and <code>EFD275</code> will set the light to the default cold, normal and warm temperatures respectively.</li>
<li><code>transition</code><code>number</code> The default transition time for operations. Will only work for single operation commands and not for on/off. Defaults to 0. </li>
<li><code>hue</code><code>number</code> Sets the hue of the light. Only for CWS. [0,365] (UNTESTED)</li>
<li><code>saturation</code><code>number</code> Sets the saturation of the light. Only for CWS. [0,100] (UNTESTED)</li>
</ul>
</script> </script>

View File

@ -55,9 +55,9 @@ module.exports = function(RED) {
RED.nodes.createNode(node, config); RED.nodes.createNode(node, config);
node.name = config.name; node.name = config.name;
node.address = config.address; node.address = config.address;
node.securityCode = config.securityCode; node.securityCode = node.credentials.securityCode;
node.identity = config.identity; node.identity = node.credentials.identity;
node.psk = config.psk; node.psk = node.credentials.psk;
if ((node.identity == null && node.psk != null) || (node.identity != null && node.psk == null)) { if ((node.identity == null && node.psk != null) || (node.identity != null && node.psk == null)) {
RED.log.error("Must provide both identity and PSK or leave both blank to generate new credentials from security code."); RED.log.error("Must provide both identity and PSK or leave both blank to generate new credentials from security code.");
@ -171,7 +171,7 @@ module.exports = function(RED) {
for (let instanceId in _listeners) { for (let instanceId in _listeners) {
if (_listeners[instanceId].hasOwnProperty(nodeId)) { if (_listeners[instanceId].hasOwnProperty(nodeId)) {
delete _listeners[instanceId][nodeId]; delete _listeners[instanceId][nodeId];
RED.log.debug(`[Tradfri: ${nodeId}] unregistered event listeners`); RED.log.info(`[Tradfri: ${nodeId}] unregistered event listeners`);
} }
} }
} }
@ -184,7 +184,13 @@ module.exports = function(RED) {
} }
RED.nodes.registerType("tradfri-connection", TradfriConnectionNode); RED.nodes.registerType("tradfri-connection", TradfriConnectionNode, {
credentials: {
securityCode: {type:"text"},
identity: {type:"text"},
psk: {type:"text"}
}
});
function TradfriNode(config) { function TradfriNode(config) {
var node = this; var node = this;
@ -196,6 +202,10 @@ module.exports = function(RED) {
var _config = RED.nodes.getNode(config.connection); var _config = RED.nodes.getNode(config.connection);
var _prev = {}; var _prev = {};
var _send = (payload: any) => {
node.send({topic:"tradfri", payload:payload});
}
var _getPayload = (accessory: tradfri.Accessory) => { var _getPayload = (accessory: tradfri.Accessory) => {
let light = lightFromAccessory(accessory); let light = lightFromAccessory(accessory);
light['prev'] = Object.assign({}, _prev); light['prev'] = Object.assign({}, _prev);
@ -205,7 +215,7 @@ module.exports = function(RED) {
var _deviceUpdated = (accessory: tradfri.Accessory) => { var _deviceUpdated = (accessory: tradfri.Accessory) => {
let ret = _getPayload(accessory); let ret = _getPayload(accessory);
_prev = lightFromAccessory(accessory); _prev = lightFromAccessory(accessory);
node.send({payload: {light: ret}}); _send(ret);
RED.log.trace(`[Tradfri: ${node.id}] recieved update for '${accessory.name}' (${accessory.instanceId})`); RED.log.trace(`[Tradfri: ${node.id}] recieved update for '${accessory.name}' (${accessory.instanceId})`);
} }
@ -222,24 +232,20 @@ module.exports = function(RED) {
} }
} }
var _handleDirectStatus = async (msg: any) => { var _handleDirectStatus = async () => {
try { try {
let client = await _config.getClient(); let client = await _config.getClient();
let res = await client.request('15001/' + node.deviceId, 'get'); let res = await client.request('15001/' + node.deviceId, 'get');
msg.payload = res; _send(res);
node.send(msg);
} catch (e) { } catch (e) {
msg.payload = e; _send(e);
node.send(msg);
} }
} }
var _handleStatus = async (msg: any) => { var _handleStatus = async () => {
try { try {
let accessory = await _config.getLight(node.deviceId); let accessory = await _config.getLight(node.deviceId);
msg.payload.light = _getPayload(accessory); _send(_getPayload(accessory));
delete msg.payload.status;
node.send(msg);
RED.log.trace(`[Tradfri: ${node.id}] Status request successful`); RED.log.trace(`[Tradfri: ${node.id}] Status request successful`);
} catch (e) { } catch (e) {
RED.log.info(`[Tradfri: ${node.id}] Status request unsuccessful, '${e.toString()}'`); RED.log.info(`[Tradfri: ${node.id}] Status request unsuccessful, '${e.toString()}'`);
@ -304,9 +310,9 @@ module.exports = function(RED) {
let isStatus = msg.payload.hasOwnProperty('status'); let isStatus = msg.payload.hasOwnProperty('status');
if (isDirect && isStatus) { if (isDirect && isStatus) {
_handleDirectStatus(msg); _handleDirectStatus();
} else if (isStatus) { } else if (isStatus) {
_handleStatus(msg); _handleStatus();
} else if (isDirect) { } else if (isDirect) {
_handleDirectLightOp(msg.payload); _handleDirectLightOp(msg.payload);
} else { } else {