@hurenkam/hue-services 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +31 -0
- package/.nyc_output/ecb1b16f-015c-458b-96d5-32cb9569c1e8.json +1 -0
- package/.nyc_output/processinfo/ecb1b16f-015c-458b-96d5-32cb9569c1e8.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.vscode/settings.json +15 -0
- package/.vscode/sftp.json +11 -0
- package/LICENSE +201 -0
- package/README.md +340 -0
- package/Todo.txt +18 -0
- package/coverage/BaseNode.js.html +379 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +101 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +161 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/RestApi.js.html +499 -0
- package/coverage/lcov-report/src/all.js.html +208 -0
- package/coverage/lcov-report/src/clip/ClipApi.js.html +1096 -0
- package/coverage/lcov-report/src/clip/Resource.js.html +481 -0
- package/coverage/lcov-report/src/clip/index.html +131 -0
- package/coverage/lcov-report/src/debug.js.html +253 -0
- package/coverage/lcov-report/src/files.js.html +118 -0
- package/coverage/lcov-report/src/index.html +161 -0
- package/coverage/lcov-report/src/nodes/BaseNode.js.html +381 -0
- package/coverage/lcov-report/src/nodes/BridgeConfigNode.js.html +817 -0
- package/coverage/lcov-report/src/nodes/ButtonNode.js.html +241 -0
- package/coverage/lcov-report/src/nodes/DevicePowerNode.js.html +199 -0
- package/coverage/lcov-report/src/nodes/GroupedLightNode.js.html +226 -0
- package/coverage/lcov-report/src/nodes/LightLevelNode.js.html +205 -0
- package/coverage/lcov-report/src/nodes/LightNode.js.html +226 -0
- package/coverage/lcov-report/src/nodes/MotionNode.js.html +205 -0
- package/coverage/lcov-report/src/nodes/RelativeRotaryNode.js.html +253 -0
- package/coverage/lcov-report/src/nodes/ResourceNode.js.html +388 -0
- package/coverage/lcov-report/src/nodes/SceneNode.js.html +124 -0
- package/coverage/lcov-report/src/nodes/ServiceNode.js.html +124 -0
- package/coverage/lcov-report/src/nodes/TemperatureNode.js.html +205 -0
- package/coverage/lcov-report/src/nodes/ZigbeeConnectivityNode.js.html +199 -0
- package/coverage/lcov-report/src/nodes/index.html +311 -0
- package/coverage/lcov-report/src/ui/BaseUI.js.html +484 -0
- package/coverage/lcov-report/src/ui/BridgeConfigUI.js.html +475 -0
- package/coverage/lcov-report/src/ui/ButtonUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/DevicePowerUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/GroupedLightUI.js.html +178 -0
- package/coverage/lcov-report/src/ui/LightLevelUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/LightUI.js.html +178 -0
- package/coverage/lcov-report/src/ui/MotionUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/RelativeRotaryUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/ResourceUI.js.html +511 -0
- package/coverage/lcov-report/src/ui/SceneUI.js.html +160 -0
- package/coverage/lcov-report/src/ui/ServiceUI.js.html +868 -0
- package/coverage/lcov-report/src/ui/TemperatureUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/ZigbeeConnectivityUI.js.html +166 -0
- package/coverage/lcov-report/src/ui/index.html +311 -0
- package/coverage/lcov.info +2217 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/src/RestApi.js.html +499 -0
- package/coverage/src/all.js.html +208 -0
- package/coverage/src/clip/ClipApi.js.html +1183 -0
- package/coverage/src/clip/Resource.js.html +481 -0
- package/coverage/src/clip/index.html +131 -0
- package/coverage/src/debug.js.html +253 -0
- package/coverage/src/files.js.html +118 -0
- package/coverage/src/index.html +116 -0
- package/coverage/src/nodes/BaseNode.js.html +379 -0
- package/coverage/src/nodes/BridgeConfigNode.js.html +517 -0
- package/coverage/src/nodes/ButtonNode.js.html +238 -0
- package/coverage/src/nodes/DevicePowerNode.js.html +196 -0
- package/coverage/src/nodes/GroupedLightNode.js.html +223 -0
- package/coverage/src/nodes/LightLevelNode.js.html +202 -0
- package/coverage/src/nodes/LightNode.js.html +223 -0
- package/coverage/src/nodes/MotionNode.js.html +202 -0
- package/coverage/src/nodes/RelativeRotaryNode.js.html +250 -0
- package/coverage/src/nodes/ResourceNode.js.html +373 -0
- package/coverage/src/nodes/SceneNode.js.html +124 -0
- package/coverage/src/nodes/ServiceNode.js.html +124 -0
- package/coverage/src/nodes/TemperatureNode.js.html +202 -0
- package/coverage/src/nodes/ZigbeeConnectivityNode.js.html +199 -0
- package/coverage/src/nodes/index.html +311 -0
- package/coverage/src/ui/BaseUI.js.html +484 -0
- package/coverage/src/ui/BridgeConfigUI.js.html +475 -0
- package/coverage/src/ui/ButtonUI.js.html +166 -0
- package/coverage/src/ui/DevicePowerUI.js.html +166 -0
- package/coverage/src/ui/GroupedLightUI.js.html +178 -0
- package/coverage/src/ui/LightLevelUI.js.html +166 -0
- package/coverage/src/ui/LightUI.js.html +178 -0
- package/coverage/src/ui/MotionUI.js.html +166 -0
- package/coverage/src/ui/RelativeRotaryUI.js.html +166 -0
- package/coverage/src/ui/ResourceUI.js.html +511 -0
- package/coverage/src/ui/SceneUI.js.html +160 -0
- package/coverage/src/ui/ServiceUI.js.html +868 -0
- package/coverage/src/ui/TemperatureUI.js.html +166 -0
- package/coverage/src/ui/ZigbeeConnectivityUI.js.html +166 -0
- package/coverage/src/ui/index.html +311 -0
- package/examples/flows.json +959 -0
- package/package.json +53 -0
- package/screenshots/Screenshot from 2022-11-29 00-29-23.png +0 -0
- package/screenshots/Screenshot from 2022-11-29 00-30-16.png +0 -0
- package/screenshots/Screenshot from 2022-11-29 00-30-58.png +0 -0
- package/screenshots/Screenshot from 2022-11-29 00-31-29.png +0 -0
- package/src/RestApi.js +138 -0
- package/src/all.html +52 -0
- package/src/all.js +42 -0
- package/src/clip/ClipApi.js +367 -0
- package/src/clip/Resource.js +132 -0
- package/src/debug.js +56 -0
- package/src/files.js +12 -0
- package/src/hue.js +98 -0
- package/src/nodes/BaseNode.js +98 -0
- package/src/nodes/BridgeConfigNode.js +144 -0
- package/src/nodes/ButtonNode.js +51 -0
- package/src/nodes/DevicePowerNode.js +37 -0
- package/src/nodes/GroupedLightNode.js +46 -0
- package/src/nodes/LightLevelNode.js +39 -0
- package/src/nodes/LightNode.js +46 -0
- package/src/nodes/MotionNode.js +39 -0
- package/src/nodes/RelativeRotaryNode.js +55 -0
- package/src/nodes/ResourceNode.js +95 -0
- package/src/nodes/SceneNode.js +13 -0
- package/src/nodes/ServiceNode.js +13 -0
- package/src/nodes/TemperatureNode.js +39 -0
- package/src/nodes/ZigbeeConnectivityNode.js +38 -0
- package/src/ui/BaseUI.js +133 -0
- package/src/ui/BridgeConfigUI.js +130 -0
- package/src/ui/ButtonUI.js +27 -0
- package/src/ui/DevicePowerUI.js +27 -0
- package/src/ui/GroupedLightUI.js +31 -0
- package/src/ui/LightLevelUI.js +27 -0
- package/src/ui/LightUI.js +31 -0
- package/src/ui/MotionUI.js +27 -0
- package/src/ui/RelativeRotaryUI.js +27 -0
- package/src/ui/ResourceUI.js +142 -0
- package/src/ui/SceneUI.js +25 -0
- package/src/ui/ServiceUI.js +261 -0
- package/src/ui/TemperatureUI.js +27 -0
- package/src/ui/ZigbeeConnectivityUI.js +27 -0
- package/test/BaseNode_spec.js +246 -0
- package/test/BaseUI_spec.js +17 -0
- package/test/BridgeConfigNode_spec.js +184 -0
- package/test/ButtonNode_spec.js +178 -0
- package/test/ClipApi_spec.js +769 -0
- package/test/DevicePowerNode_spec.js +186 -0
- package/test/GroupedLightNode_spec.js +218 -0
- package/test/LightLevelNode_spec.js +154 -0
- package/test/LightNode_spec.js +218 -0
- package/test/MotionNode_spec.js +186 -0
- package/test/RelativeRotataryNode_spec.js +325 -0
- package/test/ResourceNode_spec.js +308 -0
- package/test/Resource_spec.js +392 -0
- package/test/SceneNode_spec.js +33 -0
- package/test/ServiceNode_spec.js +33 -0
- package/test/TemperatureNode_spec.js +153 -0
- package/test/ZigbeeConnectivityNode_spec.js +186 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const events = require('events');
|
|
2
|
+
|
|
3
|
+
const _error = require('debug')('error').extend('Resource');
|
|
4
|
+
const _warn = require('debug')('warn').extend('Resource');
|
|
5
|
+
const _info = require('debug')('info').extend('Resource');
|
|
6
|
+
const _trace = require('debug')('trace').extend('Resource');
|
|
7
|
+
|
|
8
|
+
class Resource extends events.EventEmitter {
|
|
9
|
+
#clip;
|
|
10
|
+
#data;
|
|
11
|
+
|
|
12
|
+
#error;
|
|
13
|
+
#warn;
|
|
14
|
+
#info;
|
|
15
|
+
#trace;
|
|
16
|
+
|
|
17
|
+
constructor(data,clip) {
|
|
18
|
+
super();
|
|
19
|
+
this.#data = data;
|
|
20
|
+
this.#clip = clip;
|
|
21
|
+
|
|
22
|
+
this.#error = _error.extend("["+this.id()+"]");
|
|
23
|
+
this.#warn = _warn. extend("["+this.id()+"]");
|
|
24
|
+
this.#info = _info. extend("["+this.id()+"]");
|
|
25
|
+
this.#trace = _trace.extend("["+this.id()+"]");
|
|
26
|
+
|
|
27
|
+
this.#info("constructor()");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
destructor() {
|
|
31
|
+
this.#info("destructor()");
|
|
32
|
+
this.removeAllListeners();
|
|
33
|
+
this.#clip = null;
|
|
34
|
+
this.#data = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
clip() {
|
|
38
|
+
return this.#clip;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
data() {
|
|
42
|
+
return this.#data;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
id() {
|
|
46
|
+
var id = this.rtype()+"/"+this.rid()
|
|
47
|
+
var name = this.name();
|
|
48
|
+
return id + (name? " ("+name+")":"");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rid() {
|
|
52
|
+
return this.data().id;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
rtype() {
|
|
56
|
+
return this.data().type;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
owner() {
|
|
60
|
+
var result = null;
|
|
61
|
+
|
|
62
|
+
if (this.data().owner) {
|
|
63
|
+
result = this.clip().getResource(this.data().owner.rid);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
name() {
|
|
70
|
+
if ((this.data().metadata) && (this.data().metadata.name))
|
|
71
|
+
return this.data().metadata.name;
|
|
72
|
+
|
|
73
|
+
if ((this.owner()) && (this.owner().name())) {
|
|
74
|
+
return this.owner().name();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
typeName() {
|
|
81
|
+
var result = this.rtype();
|
|
82
|
+
if ((this.#data.metadata) && (this.#data.metadata.control_id))
|
|
83
|
+
result += this.#data.metadata.control_id;
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
services() {
|
|
88
|
+
this.#info("services()");
|
|
89
|
+
var result = {};
|
|
90
|
+
if ((this.data()) && (this.data().services))
|
|
91
|
+
{
|
|
92
|
+
this.data().services.forEach(service => {
|
|
93
|
+
this.#trace("services() service:",service);
|
|
94
|
+
var resource = this.clip().getResource(service.rid);
|
|
95
|
+
if (resource) {
|
|
96
|
+
result[service.rid] = resource;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get() {
|
|
104
|
+
this.#trace("get()");
|
|
105
|
+
return this.clip().get(this.rtype(),this.rid());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
put(data) {
|
|
109
|
+
this.#trace("put(",data,")");
|
|
110
|
+
this.clip().put(this.rtype(),this.rid(), data);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onEvent(event) {
|
|
114
|
+
this.#trace("onEvent()",event);
|
|
115
|
+
this.updateStatus(event);
|
|
116
|
+
this.emit('update',event);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
updateStatus(event) {
|
|
120
|
+
this.#trace("updateStatus()",event);
|
|
121
|
+
const blacklist = ["id", "id_v1", "owner", "type"];
|
|
122
|
+
|
|
123
|
+
var instance = this;
|
|
124
|
+
Object.keys(event).forEach((key) => {
|
|
125
|
+
if (!blacklist.includes(key)) {
|
|
126
|
+
instance.#data[key] = event[key];
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = Resource;
|
package/src/debug.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ====================
|
|
2
|
+
// Manage debug logging
|
|
3
|
+
// ====================
|
|
4
|
+
|
|
5
|
+
module.exports = function (RED) {
|
|
6
|
+
|
|
7
|
+
const debug = require('debug');
|
|
8
|
+
|
|
9
|
+
// GET: Retrieves current setting, but beware:
|
|
10
|
+
// There seems to be a bug in the debug module
|
|
11
|
+
// where it not always accepts the same namespaces
|
|
12
|
+
// as input causing the GET sometimes to error and
|
|
13
|
+
// leave debugging disabled alltogether.
|
|
14
|
+
// See https://github.com/debug-js/debug/issues/918
|
|
15
|
+
//
|
|
16
|
+
// Use the following command to retrieve the settings:
|
|
17
|
+
// curl -i -H "Accept: application/json" 'localhost:1880/debug'
|
|
18
|
+
RED.httpAdmin.get('/debug', function (req, res) {
|
|
19
|
+
console.log("/debug",req.method);
|
|
20
|
+
var namespaces = debug.disable();
|
|
21
|
+
console.log("/debug",req.method,namespaces);
|
|
22
|
+
debug.enable(namespaces);
|
|
23
|
+
res.end(JSON.stringify(Object({ "namespaces": namespaces })));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// POST: Using POST you can set the debug level to a new
|
|
27
|
+
// namespaces setting.
|
|
28
|
+
// e.g. use the following command to enable error and warning logging:
|
|
29
|
+
// curl -i -H "Accept: application/json" 'localhost:1880/debug' -d "namespaces='error:*,warn:*'"
|
|
30
|
+
RED.httpAdmin.post('/debug', function (req, res) {
|
|
31
|
+
console.log("/debug",req.method,req.body);
|
|
32
|
+
|
|
33
|
+
if (req.method=="POST") {
|
|
34
|
+
var namespaces = req.body.namespaces;
|
|
35
|
+
if (namespaces) {
|
|
36
|
+
debug.enable(namespaces);
|
|
37
|
+
res.end(JSON.stringify(Object({ "namespaces": namespaces })));
|
|
38
|
+
} else {
|
|
39
|
+
res.end(JSON.stringify(Object({ "error": "Missing namespaces in request." })));
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
res.end(JSON.stringify(Object({ "error": "Only GET and PUT are supported." })));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// DELETE: Using DELETE you can disable the debug logging.
|
|
48
|
+
// e.g. use the following command:
|
|
49
|
+
// curl -i -H "Accept: application/json" 'localhost:1880/debug' -X "DELETE"
|
|
50
|
+
RED.httpAdmin.delete('/debug', function (req, res) {
|
|
51
|
+
console.log("/debug",req.method);
|
|
52
|
+
var namespaces = debug.disable();
|
|
53
|
+
console.log("/debug",req.method,namespaces);
|
|
54
|
+
res.end(JSON.stringify(Object({ "namespaces": namespaces })));
|
|
55
|
+
});
|
|
56
|
+
}
|
package/src/files.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
/**
|
|
3
|
+
* Enable http route to static ui files
|
|
4
|
+
*/
|
|
5
|
+
RED.httpAdmin.get('/src/ui/*', function (req, res) {
|
|
6
|
+
var options = {
|
|
7
|
+
root: __dirname + '/ui/',
|
|
8
|
+
dotfiles: 'deny'
|
|
9
|
+
};
|
|
10
|
+
res.sendFile(req.params[0], options);
|
|
11
|
+
});
|
|
12
|
+
}
|
package/src/hue.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ===================
|
|
2
|
+
// Manage bridge calls
|
|
3
|
+
// ===================
|
|
4
|
+
|
|
5
|
+
const _error = require('debug')('error').extend('hue');
|
|
6
|
+
const _warn = require('debug')('warn').extend('hue');
|
|
7
|
+
const _info = require('debug')('info').extend('hue');
|
|
8
|
+
const _trace = require('debug')('trace').extend('hue');
|
|
9
|
+
|
|
10
|
+
module.exports = function (RED) {
|
|
11
|
+
const BridgeConfigNode = require('./nodes/BridgeConfigNode');
|
|
12
|
+
|
|
13
|
+
RED.httpAdmin.get('/BridgeConfigNode/DiscoverBridges', async function (req, res, next) {
|
|
14
|
+
_info("/DiscoverBridges");
|
|
15
|
+
_trace(req.query);
|
|
16
|
+
var options = [];
|
|
17
|
+
|
|
18
|
+
BridgeConfigNode.DiscoverBridges()
|
|
19
|
+
.then(function(data) {
|
|
20
|
+
if (data) {
|
|
21
|
+
data.forEach((element) => {
|
|
22
|
+
options.push({ label: element.name, value: element.internalipaddress })
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
res.end(JSON.stringify(Object(options)));
|
|
26
|
+
})
|
|
27
|
+
.catch(function(error) {
|
|
28
|
+
_error("/DiscoverBridges Error:",error.message,error.stack);
|
|
29
|
+
res.end(JSON.stringify(Object(options)));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
RED.httpAdmin.get('/BridgeConfigNode/AcquireApplicationKey', async function (req, res, next) {
|
|
34
|
+
_info("/AcquireApplicationKey");
|
|
35
|
+
_trace(req.query);
|
|
36
|
+
|
|
37
|
+
if (!req.query.ip) {
|
|
38
|
+
return res.status(500).send("Missing bridge ip.");
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
BridgeConfigNode.AcquireApplicationKey(req.query.ip)
|
|
42
|
+
.then(function(data) {
|
|
43
|
+
_trace("/AcquireApplicationKey Key:",data);
|
|
44
|
+
res.end(JSON.stringify(Object({ key: data })));
|
|
45
|
+
})
|
|
46
|
+
.catch(function(error) {
|
|
47
|
+
_error("/AcquireApplicationKey Error:",error.message,error.stack);
|
|
48
|
+
res.end(JSON.stringify(Object({ error: error })));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
RED.httpAdmin.get('/BridgeConfigNode/GetBridgeOptions', async function (req, res, next) {
|
|
54
|
+
_info("/GetBridgeOptions");
|
|
55
|
+
_trace(req.query);
|
|
56
|
+
var options = [];
|
|
57
|
+
|
|
58
|
+
Object.keys(BridgeConfigNode.bridges()).forEach((key) => {
|
|
59
|
+
options.push({ label: BridgeConfigNode.bridges()[key].name, value: BridgeConfigNode.bridges()[key].id });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
res.end(JSON.stringify(Object(options)));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
RED.httpAdmin.get('/BridgeConfigNode/GetSortedResourceOptions', async function (req, res, next) {
|
|
66
|
+
_info("/GetSortedResourceOptions");
|
|
67
|
+
_trace(req.query);
|
|
68
|
+
var clip = BridgeConfigNode.bridges()[req.query.bridge_id].instance.clip();
|
|
69
|
+
var options = clip.getSortedResourceOptions(req.query.type, req.query.models);
|
|
70
|
+
res.end(JSON.stringify(Object(options)));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
RED.httpAdmin.get('/BridgeConfigNode/GetSortedTypeOptions', async function (req, res, next) {
|
|
74
|
+
_info("/GetSortedTypeOptions");
|
|
75
|
+
_trace(req.query);
|
|
76
|
+
var clip = BridgeConfigNode.bridges()[req.query.bridge_id].instance.clip();
|
|
77
|
+
var options = clip.getSortedTypeOptions();
|
|
78
|
+
res.end(JSON.stringify(Object(options)));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
RED.httpAdmin.get('/BridgeConfigNode/GetSortedOwnerOptions', async function (req, res, next) {
|
|
83
|
+
_info("/GetSortedOwnerOptions");
|
|
84
|
+
_trace(req.query);
|
|
85
|
+
var clip = BridgeConfigNode.bridges()[req.query.bridge_id].instance.clip();
|
|
86
|
+
var options = clip.getSortedOwnerOptions(req.query.rtype);
|
|
87
|
+
res.end(JSON.stringify(Object(options)));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
RED.httpAdmin.get('/BridgeConfigNode/GetSortedServiceOptions', async function (req, res, next) {
|
|
91
|
+
_info("/GetSortedServiceOptions");
|
|
92
|
+
_trace(req.query);
|
|
93
|
+
var clip = BridgeConfigNode.bridges()[req.query.bridge_id].instance.clip();
|
|
94
|
+
var options = clip.getSortedServiceOptions(req.query.owner,req.query.rtype);
|
|
95
|
+
res.end(JSON.stringify(Object(options)));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const _error = require('debug')('error').extend('BaseNode');
|
|
2
|
+
const _warn = require('debug')('warn').extend('BaseNode');
|
|
3
|
+
const _info = require('debug')('info').extend('BaseNode');
|
|
4
|
+
const _trace = require('debug')('trace').extend('BaseNode');
|
|
5
|
+
|
|
6
|
+
class BaseNode {
|
|
7
|
+
static nodeAPI = null;
|
|
8
|
+
#onInput;
|
|
9
|
+
#onClose;
|
|
10
|
+
|
|
11
|
+
#error;
|
|
12
|
+
#warn;
|
|
13
|
+
#info;
|
|
14
|
+
#trace;
|
|
15
|
+
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
|
|
19
|
+
this.#error = _error.extend("["+this.logid()+"]");
|
|
20
|
+
this.#warn = _warn. extend("["+this.logid()+"]");
|
|
21
|
+
this.#info = _info. extend("["+this.logid()+"]");
|
|
22
|
+
this.#trace = _trace.extend("["+this.logid()+"]");
|
|
23
|
+
|
|
24
|
+
this.#info("constructor()");
|
|
25
|
+
BaseNode.nodeAPI.nodes.createNode(this,config);
|
|
26
|
+
var instance = this;
|
|
27
|
+
|
|
28
|
+
this.#onInput = function (msg) {
|
|
29
|
+
try {
|
|
30
|
+
instance.onInput(msg);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
this.#error(error.message,error.stack);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.#onClose = function () {
|
|
37
|
+
try {
|
|
38
|
+
instance.destructor();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
this.#error(error.message,error.stack);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.on('input', this.#onInput);
|
|
45
|
+
this.on('close', this.#onClose);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
logid() {
|
|
49
|
+
return (this.config)? ((this.config.name)? this.config.name: this.config.id) : "<?>";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getStatusFill() {
|
|
53
|
+
this.#trace("getStatusFill()");
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getStatusText() {
|
|
58
|
+
this.#trace("getStatusText()");
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getStatusShape() {
|
|
63
|
+
this.#trace("getStatusShape()");
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
updateStatus() {
|
|
68
|
+
this.#trace("updateStatus()");
|
|
69
|
+
try {
|
|
70
|
+
var fill = this.getStatusFill();
|
|
71
|
+
var shape = this.getStatusShape();
|
|
72
|
+
var text = this.getStatusText();
|
|
73
|
+
|
|
74
|
+
if ((shape) && (!fill)) fill = "grey";
|
|
75
|
+
if ((fill) && (!shape)) shape = "dot";
|
|
76
|
+
|
|
77
|
+
this.status({
|
|
78
|
+
fill: fill,
|
|
79
|
+
shape: shape,
|
|
80
|
+
text: text
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
this.#error(error.message,error.stack);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
onInput(msg) {
|
|
88
|
+
this.#trace("onInput(",msg,")");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
destructor() {
|
|
92
|
+
this.#info("destructor()");
|
|
93
|
+
this.off('input',this.#onInput);
|
|
94
|
+
this.off('close',this.#onClose);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = BaseNode;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
var bridges = {};
|
|
2
|
+
|
|
3
|
+
const ClipApi = require('../clip/ClipApi');
|
|
4
|
+
const BaseNode = require('./BaseNode');
|
|
5
|
+
const axios = require('axios');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
const _error = require('debug')('error').extend('BridgeConfigNode');
|
|
9
|
+
const _warn = require('debug')('warn').extend('BridgeConfigNode');
|
|
10
|
+
const _info = require('debug')('info').extend('BridgeConfigNode');
|
|
11
|
+
const _trace = require('debug')('trace').extend('BridgeConfigNode');
|
|
12
|
+
|
|
13
|
+
class BridgeConfigNode extends BaseNode {
|
|
14
|
+
#onClose;
|
|
15
|
+
#onClipError;
|
|
16
|
+
#clip;
|
|
17
|
+
|
|
18
|
+
#error;
|
|
19
|
+
#warn;
|
|
20
|
+
#info;
|
|
21
|
+
#trace;
|
|
22
|
+
|
|
23
|
+
static bridges () { return bridges; }
|
|
24
|
+
|
|
25
|
+
constructor(config) {
|
|
26
|
+
super(config);
|
|
27
|
+
BaseNode.nodeAPI.nodes.createNode(this, config);
|
|
28
|
+
|
|
29
|
+
this.#error = _error.extend("["+this.logid()+"]");
|
|
30
|
+
this.#warn = _warn. extend("["+this.logid()+"]");
|
|
31
|
+
this.#info = _info. extend("["+this.logid()+"]");
|
|
32
|
+
this.#trace = _trace.extend("["+this.logid()+"]");
|
|
33
|
+
|
|
34
|
+
this.#info("constructor()");
|
|
35
|
+
var instance = this;
|
|
36
|
+
|
|
37
|
+
bridges[this.id] = { id: this.id, name: config.name, instance: this };
|
|
38
|
+
this.#clip = this._constructClip(config.ip,config.key,config.name);
|
|
39
|
+
|
|
40
|
+
this.#onClose = function() {
|
|
41
|
+
try {
|
|
42
|
+
instance.destructor();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
instance.#error(error.message,error.stack);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.on('close', this.#onClose);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
destructor() {
|
|
51
|
+
this._destructClip();
|
|
52
|
+
super.destructor();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_constructClip(ip,key,name) {
|
|
56
|
+
var instance = this;
|
|
57
|
+
this.#onClipError = function(event) {
|
|
58
|
+
instance.#error("onClipError()",error);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var clip = new ClipApi(ip,key,name);
|
|
62
|
+
clip.on('error',this.#onClipError);
|
|
63
|
+
return clip;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_destructClip() {
|
|
67
|
+
this.#clip.destructor();
|
|
68
|
+
this.#clip = null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
clip() {
|
|
72
|
+
return this.#clip;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
requestStartup(resource) {
|
|
76
|
+
if (this.clip()) {
|
|
77
|
+
this.clip().requestStartup(resource);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* istanbul ignore next */
|
|
82
|
+
static async _axios(request) {
|
|
83
|
+
return axios(request);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static async DiscoverBridges() {
|
|
87
|
+
_info("DiscoverBridges()");
|
|
88
|
+
var result = [];
|
|
89
|
+
|
|
90
|
+
var response = await BridgeConfigNode._axios({
|
|
91
|
+
"method": "GET",
|
|
92
|
+
"url": "https://discovery.meethue.com",
|
|
93
|
+
"headers": { "Content-Type": "application/json; charset=utf-8" },
|
|
94
|
+
"httpsAgent": new https.Agent({ rejectUnauthorized: false })
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const promises = response.data.map(async element => {
|
|
98
|
+
const config = await BridgeConfigNode._axios({
|
|
99
|
+
"method": "GET",
|
|
100
|
+
"url": "https://" + element.internalipaddress + "/api/config",
|
|
101
|
+
"headers": { "Content-Type": "application/json; charset=utf-8" },
|
|
102
|
+
"httpsAgent": new https.Agent({ rejectUnauthorized: false })
|
|
103
|
+
});
|
|
104
|
+
config.data.internalipaddress = element.internalipaddress;
|
|
105
|
+
return config
|
|
106
|
+
});
|
|
107
|
+
const configs = await Promise.all(promises);
|
|
108
|
+
|
|
109
|
+
configs.forEach((config) => {
|
|
110
|
+
result.push(config.data);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static async AcquireApplicationKey(ip) {
|
|
117
|
+
_info("AcquireApplicationKey("+ip+")");
|
|
118
|
+
return new Promise(function (resolve, reject) {
|
|
119
|
+
var id = "BridgeConfig (" + Math.floor((Math.random() * 100) + 1) + ")";
|
|
120
|
+
var request = {
|
|
121
|
+
"method": "POST",
|
|
122
|
+
"url": "http://" + ip + "/api",
|
|
123
|
+
"headers": { "Content-Type": "application/json; charset=utf-8" },
|
|
124
|
+
"data": { "devicetype": id },
|
|
125
|
+
"httpsAgent": new https.Agent({ rejectUnauthorized: false })
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
BridgeConfigNode._axios(request)
|
|
129
|
+
.then(function (response) {
|
|
130
|
+
_trace("AcquireApplicationKey("+ip+")", response.data);
|
|
131
|
+
if (Object.keys(response.data[0]).includes("success")) {
|
|
132
|
+
resolve(response.data[0].success.username);
|
|
133
|
+
} else {
|
|
134
|
+
reject(response.data[0].error);
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
.catch(function (error) {
|
|
138
|
+
reject(error);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = BridgeConfigNode;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
ResourceNode = require("./ResourceNode");
|
|
2
|
+
|
|
3
|
+
class ButtonNode extends ResourceNode {
|
|
4
|
+
#fill;
|
|
5
|
+
|
|
6
|
+
#info;
|
|
7
|
+
#trace;
|
|
8
|
+
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super(config);
|
|
11
|
+
|
|
12
|
+
this.#info = require('debug')('info').extend('ButtonNode').extend("["+this.logid()+"]");
|
|
13
|
+
this.#trace = require('debug')('trace').extend('ButtonNode').extend("["+this.logid()+"]");
|
|
14
|
+
this.#info("constructor()");
|
|
15
|
+
|
|
16
|
+
this.#fill = "grey";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onUpdate(event) {
|
|
20
|
+
this.#trace("onUpdate(",event,")");
|
|
21
|
+
this.#fill = "blue";
|
|
22
|
+
|
|
23
|
+
var instance = this;
|
|
24
|
+
setTimeout(()=>{
|
|
25
|
+
instance.#fill = "grey";
|
|
26
|
+
instance.updateStatus();
|
|
27
|
+
},1000);
|
|
28
|
+
|
|
29
|
+
this.updateStatus();
|
|
30
|
+
super.onUpdate(event);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
updateStatus() {
|
|
34
|
+
this.#trace("updateStatus()");
|
|
35
|
+
|
|
36
|
+
var fill = this.#fill;
|
|
37
|
+
var shape = "dot";
|
|
38
|
+
var text = "";
|
|
39
|
+
|
|
40
|
+
var resource = this.resource();
|
|
41
|
+
if ((resource) && (resource.data()) && (resource.data().button)) {
|
|
42
|
+
if (resource.data().button.last_event!=null) {
|
|
43
|
+
text = resource.data().button.last_event;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.status({fill: fill, shape: shape, text: text});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = ButtonNode;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
ResourceNode = require("./ResourceNode");
|
|
2
|
+
|
|
3
|
+
class DevicePowerNode extends ResourceNode {
|
|
4
|
+
#info;
|
|
5
|
+
#trace;
|
|
6
|
+
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.#info = require('debug')('info').extend('DevicePowerNode').extend("["+this.logid()+"]");
|
|
10
|
+
this.#trace = require('debug')('trace').extend('DevicePowerNode').extend("["+this.logid()+"]");
|
|
11
|
+
this.#info("constructor()");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onUpdate(event) {
|
|
15
|
+
this.#trace("onUpdate(",event,")");
|
|
16
|
+
this.updateStatus();
|
|
17
|
+
super.onUpdate(event);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
updateStatus() {
|
|
21
|
+
this.#trace("updateStatus()");
|
|
22
|
+
|
|
23
|
+
var fill = "grey";
|
|
24
|
+
var shape = "dot";
|
|
25
|
+
var text = "";
|
|
26
|
+
|
|
27
|
+
var resource = this.resource();
|
|
28
|
+
if ((resource) && (resource.data()) && (resource.data().power_state) && (resource.data().power_state.battery_level!=null)) {
|
|
29
|
+
fill = (resource.data().power_state.battery_level > 10)? "green" : "red";
|
|
30
|
+
text = resource.data().power_state.battery_level+"%";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.status({fill: fill, shape: shape, text: text});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = DevicePowerNode;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
ResourceNode = require("./ResourceNode");
|
|
2
|
+
|
|
3
|
+
class GroupedLightNode extends ResourceNode {
|
|
4
|
+
#info;
|
|
5
|
+
#trace;
|
|
6
|
+
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.#info = require('debug')('info').extend('GroupedLightNode').extend("["+this.logid()+"]");
|
|
10
|
+
this.#trace = require('debug')('trace').extend('GroupedLightNode').extend("["+this.logid()+"]");
|
|
11
|
+
this.#info("constructor()");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
onUpdate(event) {
|
|
15
|
+
this.#trace("onUpdate(",event,")");
|
|
16
|
+
this.updateStatus();
|
|
17
|
+
super.onUpdate(event);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
updateStatus() {
|
|
21
|
+
this.#trace("updateStatus()");
|
|
22
|
+
|
|
23
|
+
var fill = "grey";
|
|
24
|
+
var shape = "dot";
|
|
25
|
+
var text = "";
|
|
26
|
+
|
|
27
|
+
var resource = this.resource();
|
|
28
|
+
if ((resource) && (resource.data()) && (resource.data().on)) {
|
|
29
|
+
if (resource.data().on.on==true) {
|
|
30
|
+
fill = "yellow";
|
|
31
|
+
|
|
32
|
+
if (resource.data().dimming) {
|
|
33
|
+
text = resource.data().dimming.brightness+"%";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
} else if (resource.data().on.on==false) {
|
|
37
|
+
fill = "grey";
|
|
38
|
+
text = "off";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.status({fill: fill, shape: shape, text: text});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = GroupedLightNode;
|