From 735d57d308c38b577c115d2a031ae7d4ca92304d Mon Sep 17 00:00:00 2001 From: Ristomatti Airo Date: Sun, 6 Aug 2017 01:07:21 +0300 Subject: [PATCH] Add support for device commands --- README.md | 24 ++++- harmony/harmony-server.js | 65 ++++++++++++ harmony/harmony.html | 212 ++++++++++++++++++++++++++++++++++---- harmony/harmony.js | 36 +++++++ 4 files changed, 310 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6e0a0a7..67e9445 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ You can install the extension simply in Node-RED in your browser, by default und ## Usage / Available nodes -Three nodes are available in Node-RED: **H command**, **H activity** and **H observe**, located in the group **harmony**. +Four nodes are available in Node-RED: **H command**, **H activity**, **H device command** and **H observe**, located in the group **harmony**. ### H command @@ -55,22 +55,36 @@ The command configured in the node will be triggered by any input injected into A node to activate an **Activity** on a Harmony Hub through Node-RED -A Harmony **Hub** needs to be selected from the list or created by clicking on the edit button. The Harmony Hub +A Harmony **Hub** needs to be selected from the list or created by clicking on the edit button. The Harmony Hub **IP** address can be autodetected by clicking on the search button in the configuration node. -An **Activity** that is set up on the Harmony Hub needs to be provided, it's identified by its *ID*. Clicking on the -search button loads the available activities from the provided **Hub**, which can then be selected from a dropdown list. Switching +An **Activity** that is set up on the Harmony Hub needs to be provided, it's identified by its *ID*. Clicking on the +search button loads the available activities from the provided **Hub**, which can then be selected from a dropdown list. Switching back to the input field will show the *ID* in the field. The **Label** field below will show the **Activity** label. To switch off, select *PowerOff* from the **Activity** dropdown list, or enter *"-1"* into the field. The command configured in the node will be triggered by any input injected into the node, the output slot will return *msg.payload = true* if the command was sent successfully. +### H device command + +A node to send a **Device Command** to a Harmony Hub through Node-RED. + +A Harmony **Hub** needs to be selected from the list or created by clicking on the edit button. The Harmony Hub **IP** address can be autodetected by clicking on the search button in the configuration node. + +A **Device** that is set up on the Harmony Hub needs to be provided, it's identified by its *ID*. Clicking on the search button loads the available devices from the provided **Hub**, which can then be selected from a dropdown list. Switching back to the imput field will show the *ID* in the field. The **Label** field below will show the **Device** label. + +A **Command** from the selected **Device** needs to be provided, it's a stanza *query*. Clicking on the search button loads the available commands from the provided **Device**, that can then be selected from a list. Switching back to the input field will show the *query* string in the field. + +The **Repeat** field allows for the command to be repeated. The default is *1*, meaning the command is send once. For example entering *10* will send the command exactly 10 times. This can be helpful when using commands for volume or channels. + +The command configured in the node will be triggered by any input injected into the node, the output slot will return *msg.payload = true* if the command was sent successfully + ### H observe A node to observe an **Activity** being triggered on a Harmony Hub through Node-RED -A Harmony **Hub** needs to be selected from the list or created by clicking on the edit button. The Harmony Hub +A Harmony **Hub** needs to be selected from the list or created by clicking on the edit button. The Harmony Hub **IP** address can be autodetected by clicking on the search button in the configuration node. When an **Activity** is switched on the Harmony Hub, the node sends an object with a payload to the output: diff --git a/harmony/harmony-server.js b/harmony/harmony-server.js index 7bc630b..8110fa1 100644 --- a/harmony/harmony-server.js +++ b/harmony/harmony-server.js @@ -101,4 +101,69 @@ module.exports = function (RED) { }) } }) + + RED.httpAdmin.get('/harmony/devices', function (req, res, next) { + if (!req.query.ip) { + res.status(400).send('Missing argument IP') + } else { + harmonyClient(req.query.ip) + .then(function (harmony) { + harmony.getAvailableCommands() + .then(function (commands) { + var devices = commands.device + .filter(function (device) { + return device.controlGroup.length > 0 + }) + .map(function (device) { + return {id: device.id, label: device.label} + }) + harmony.end() + res.status(200).send(JSON.stringify(devices)) + }).fail(function (err) { + harmony.end() + res.status(500).send('Request failed.') + if (err) throw err + }) + }).fail(function (err) { + res.status(500).send('Request failed.') + if (err) throw err + }) + } + }) + + RED.httpAdmin.get('/harmony/device-commands', function (req, res, next) { + if (!req.query.ip || !req.query.deviceId) { + res.status(400).send('Missing argument.') + } else { + harmonyClient(req.query.ip) + .then(function (harmony) { + harmony.getAvailableCommands() + .then(function (commands) { + var device = commands.device.filter(function(device) { + return device.id === req.query.deviceId + }).pop() + var deviceCommands = device.controlGroup + .map(function (group) { + return group.function + }) + .reduce(function (prev, curr) { + return prev.concat(curr) + }) + .map(function (fn) { + return {action: fn.action, label: fn.label} + }) + harmony.end() + res.status(200).send(JSON.stringify(deviceCommands)) + }).fail(function (err) { + harmony.end() + res.status(500).send('Request failed.') + if (err) throw err + }) + + }).fail(function (err) { + res.status(500).send('Request failed.') + if (err) throw err + }) + } + }) } diff --git a/harmony/harmony.html b/harmony/harmony.html index cc9588e..ea78e14 100644 --- a/harmony/harmony.html +++ b/harmony/harmony.html @@ -31,26 +31,26 @@ @@ -96,13 +96,13 @@ if (config && config.ip && act_id) { $.get('harmony/commands', { ip: config.ip, activity: act_id } ) - .done(function(data) { + .done(function(data) { var comms = JSON.parse(data); if(!comms || comms.length <= 0) { return; } $('#node-input-commandscan').html(""); - $('#node-input-command').replaceWith(''); + $('#node-input-command').replaceWith(''); comms.forEach(function(comm){ comm.function.forEach(function(item){ $('#node-input-command').append(''); @@ -125,7 +125,7 @@ function toggleInput() { var current = $('#node-input-activity').val(); $('#node-input-activity').val("Loading activities...") - + if (config && config.ip) { $.get('harmony/activities', { ip: config.ip } ) .done(function(data) { @@ -138,7 +138,7 @@ if(!acts || acts.length <= 0) { RED.notify("No activities found.", "error"); return; - } + } acts.forEach(function(act){ $('#node-input-activity').append(''); }); @@ -193,19 +193,19 @@ @@ -240,7 +240,7 @@ function toggleInput() { var current = $('#node-input-activity').val(); $('#node-input-activity').val("Loading activities...") - + if (config && config.ip) { $.get('harmony/activities', { ip: config.ip } ) .done(function(data) { @@ -253,7 +253,7 @@ if(!acts || acts.length <= 0) { RED.notify("No activities found.", "error"); return; - } + } acts.forEach(function(act){ $('#node-input-activity').append(''); }); @@ -291,12 +291,12 @@ @@ -317,4 +317,172 @@ } }); - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/harmony/harmony.js b/harmony/harmony.js index 51080d0..ca5eb2a 100644 --- a/harmony/harmony.js +++ b/harmony/harmony.js @@ -108,4 +108,40 @@ module.exports = function (RED) { }, 2000) } RED.nodes.registerType('H observe', HarmonyObserve) + + function HarmonySendDeviceCommand (n) { + RED.nodes.createNode(this, n) + var node = this + + node.server = RED.nodes.getNode(n.server) + node.deviceId = n.device + node.label = n.label + node.command = n.command + node.repeat = Number.parseInt(n.repeat) || 1 + + if (!node.server) return + + var action = decodeURI(node.command) + + node.on('input', function (msg) { + try { + msg.payload = JSON.parse(msg.payload) + } catch (err) { } + + if (!node.command || !node.server) { + node.send({payload: false}) + return; + } + + for (var i = 0; i < node.repeat; i++) { + setTimeout(function () { + node.server.harmony.send('holdAction', 'action=' + action + ':status=press').then(function () { + node.server.harmony.send('holdAction', 'action=' + action + ':status=release') + }) + }, 100) + } + node.send({payload: true}) + }) + } + RED.nodes.registerType('H device command', HarmonySendDeviceCommand) }