@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.
Files changed (162) hide show
  1. package/.github/workflows/node.js.yml +31 -0
  2. package/.nyc_output/ecb1b16f-015c-458b-96d5-32cb9569c1e8.json +1 -0
  3. package/.nyc_output/processinfo/ecb1b16f-015c-458b-96d5-32cb9569c1e8.json +1 -0
  4. package/.nyc_output/processinfo/index.json +1 -0
  5. package/.vscode/settings.json +15 -0
  6. package/.vscode/sftp.json +11 -0
  7. package/LICENSE +201 -0
  8. package/README.md +340 -0
  9. package/Todo.txt +18 -0
  10. package/coverage/BaseNode.js.html +379 -0
  11. package/coverage/base.css +224 -0
  12. package/coverage/block-navigation.js +87 -0
  13. package/coverage/favicon.png +0 -0
  14. package/coverage/index.html +101 -0
  15. package/coverage/lcov-report/base.css +224 -0
  16. package/coverage/lcov-report/block-navigation.js +87 -0
  17. package/coverage/lcov-report/favicon.png +0 -0
  18. package/coverage/lcov-report/index.html +161 -0
  19. package/coverage/lcov-report/prettify.css +1 -0
  20. package/coverage/lcov-report/prettify.js +2 -0
  21. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  22. package/coverage/lcov-report/sorter.js +196 -0
  23. package/coverage/lcov-report/src/RestApi.js.html +499 -0
  24. package/coverage/lcov-report/src/all.js.html +208 -0
  25. package/coverage/lcov-report/src/clip/ClipApi.js.html +1096 -0
  26. package/coverage/lcov-report/src/clip/Resource.js.html +481 -0
  27. package/coverage/lcov-report/src/clip/index.html +131 -0
  28. package/coverage/lcov-report/src/debug.js.html +253 -0
  29. package/coverage/lcov-report/src/files.js.html +118 -0
  30. package/coverage/lcov-report/src/index.html +161 -0
  31. package/coverage/lcov-report/src/nodes/BaseNode.js.html +381 -0
  32. package/coverage/lcov-report/src/nodes/BridgeConfigNode.js.html +817 -0
  33. package/coverage/lcov-report/src/nodes/ButtonNode.js.html +241 -0
  34. package/coverage/lcov-report/src/nodes/DevicePowerNode.js.html +199 -0
  35. package/coverage/lcov-report/src/nodes/GroupedLightNode.js.html +226 -0
  36. package/coverage/lcov-report/src/nodes/LightLevelNode.js.html +205 -0
  37. package/coverage/lcov-report/src/nodes/LightNode.js.html +226 -0
  38. package/coverage/lcov-report/src/nodes/MotionNode.js.html +205 -0
  39. package/coverage/lcov-report/src/nodes/RelativeRotaryNode.js.html +253 -0
  40. package/coverage/lcov-report/src/nodes/ResourceNode.js.html +388 -0
  41. package/coverage/lcov-report/src/nodes/SceneNode.js.html +124 -0
  42. package/coverage/lcov-report/src/nodes/ServiceNode.js.html +124 -0
  43. package/coverage/lcov-report/src/nodes/TemperatureNode.js.html +205 -0
  44. package/coverage/lcov-report/src/nodes/ZigbeeConnectivityNode.js.html +199 -0
  45. package/coverage/lcov-report/src/nodes/index.html +311 -0
  46. package/coverage/lcov-report/src/ui/BaseUI.js.html +484 -0
  47. package/coverage/lcov-report/src/ui/BridgeConfigUI.js.html +475 -0
  48. package/coverage/lcov-report/src/ui/ButtonUI.js.html +166 -0
  49. package/coverage/lcov-report/src/ui/DevicePowerUI.js.html +166 -0
  50. package/coverage/lcov-report/src/ui/GroupedLightUI.js.html +178 -0
  51. package/coverage/lcov-report/src/ui/LightLevelUI.js.html +166 -0
  52. package/coverage/lcov-report/src/ui/LightUI.js.html +178 -0
  53. package/coverage/lcov-report/src/ui/MotionUI.js.html +166 -0
  54. package/coverage/lcov-report/src/ui/RelativeRotaryUI.js.html +166 -0
  55. package/coverage/lcov-report/src/ui/ResourceUI.js.html +511 -0
  56. package/coverage/lcov-report/src/ui/SceneUI.js.html +160 -0
  57. package/coverage/lcov-report/src/ui/ServiceUI.js.html +868 -0
  58. package/coverage/lcov-report/src/ui/TemperatureUI.js.html +166 -0
  59. package/coverage/lcov-report/src/ui/ZigbeeConnectivityUI.js.html +166 -0
  60. package/coverage/lcov-report/src/ui/index.html +311 -0
  61. package/coverage/lcov.info +2217 -0
  62. package/coverage/prettify.css +1 -0
  63. package/coverage/prettify.js +2 -0
  64. package/coverage/sort-arrow-sprite.png +0 -0
  65. package/coverage/sorter.js +196 -0
  66. package/coverage/src/RestApi.js.html +499 -0
  67. package/coverage/src/all.js.html +208 -0
  68. package/coverage/src/clip/ClipApi.js.html +1183 -0
  69. package/coverage/src/clip/Resource.js.html +481 -0
  70. package/coverage/src/clip/index.html +131 -0
  71. package/coverage/src/debug.js.html +253 -0
  72. package/coverage/src/files.js.html +118 -0
  73. package/coverage/src/index.html +116 -0
  74. package/coverage/src/nodes/BaseNode.js.html +379 -0
  75. package/coverage/src/nodes/BridgeConfigNode.js.html +517 -0
  76. package/coverage/src/nodes/ButtonNode.js.html +238 -0
  77. package/coverage/src/nodes/DevicePowerNode.js.html +196 -0
  78. package/coverage/src/nodes/GroupedLightNode.js.html +223 -0
  79. package/coverage/src/nodes/LightLevelNode.js.html +202 -0
  80. package/coverage/src/nodes/LightNode.js.html +223 -0
  81. package/coverage/src/nodes/MotionNode.js.html +202 -0
  82. package/coverage/src/nodes/RelativeRotaryNode.js.html +250 -0
  83. package/coverage/src/nodes/ResourceNode.js.html +373 -0
  84. package/coverage/src/nodes/SceneNode.js.html +124 -0
  85. package/coverage/src/nodes/ServiceNode.js.html +124 -0
  86. package/coverage/src/nodes/TemperatureNode.js.html +202 -0
  87. package/coverage/src/nodes/ZigbeeConnectivityNode.js.html +199 -0
  88. package/coverage/src/nodes/index.html +311 -0
  89. package/coverage/src/ui/BaseUI.js.html +484 -0
  90. package/coverage/src/ui/BridgeConfigUI.js.html +475 -0
  91. package/coverage/src/ui/ButtonUI.js.html +166 -0
  92. package/coverage/src/ui/DevicePowerUI.js.html +166 -0
  93. package/coverage/src/ui/GroupedLightUI.js.html +178 -0
  94. package/coverage/src/ui/LightLevelUI.js.html +166 -0
  95. package/coverage/src/ui/LightUI.js.html +178 -0
  96. package/coverage/src/ui/MotionUI.js.html +166 -0
  97. package/coverage/src/ui/RelativeRotaryUI.js.html +166 -0
  98. package/coverage/src/ui/ResourceUI.js.html +511 -0
  99. package/coverage/src/ui/SceneUI.js.html +160 -0
  100. package/coverage/src/ui/ServiceUI.js.html +868 -0
  101. package/coverage/src/ui/TemperatureUI.js.html +166 -0
  102. package/coverage/src/ui/ZigbeeConnectivityUI.js.html +166 -0
  103. package/coverage/src/ui/index.html +311 -0
  104. package/examples/flows.json +959 -0
  105. package/package.json +53 -0
  106. package/screenshots/Screenshot from 2022-11-29 00-29-23.png +0 -0
  107. package/screenshots/Screenshot from 2022-11-29 00-30-16.png +0 -0
  108. package/screenshots/Screenshot from 2022-11-29 00-30-58.png +0 -0
  109. package/screenshots/Screenshot from 2022-11-29 00-31-29.png +0 -0
  110. package/src/RestApi.js +138 -0
  111. package/src/all.html +52 -0
  112. package/src/all.js +42 -0
  113. package/src/clip/ClipApi.js +367 -0
  114. package/src/clip/Resource.js +132 -0
  115. package/src/debug.js +56 -0
  116. package/src/files.js +12 -0
  117. package/src/hue.js +98 -0
  118. package/src/nodes/BaseNode.js +98 -0
  119. package/src/nodes/BridgeConfigNode.js +144 -0
  120. package/src/nodes/ButtonNode.js +51 -0
  121. package/src/nodes/DevicePowerNode.js +37 -0
  122. package/src/nodes/GroupedLightNode.js +46 -0
  123. package/src/nodes/LightLevelNode.js +39 -0
  124. package/src/nodes/LightNode.js +46 -0
  125. package/src/nodes/MotionNode.js +39 -0
  126. package/src/nodes/RelativeRotaryNode.js +55 -0
  127. package/src/nodes/ResourceNode.js +95 -0
  128. package/src/nodes/SceneNode.js +13 -0
  129. package/src/nodes/ServiceNode.js +13 -0
  130. package/src/nodes/TemperatureNode.js +39 -0
  131. package/src/nodes/ZigbeeConnectivityNode.js +38 -0
  132. package/src/ui/BaseUI.js +133 -0
  133. package/src/ui/BridgeConfigUI.js +130 -0
  134. package/src/ui/ButtonUI.js +27 -0
  135. package/src/ui/DevicePowerUI.js +27 -0
  136. package/src/ui/GroupedLightUI.js +31 -0
  137. package/src/ui/LightLevelUI.js +27 -0
  138. package/src/ui/LightUI.js +31 -0
  139. package/src/ui/MotionUI.js +27 -0
  140. package/src/ui/RelativeRotaryUI.js +27 -0
  141. package/src/ui/ResourceUI.js +142 -0
  142. package/src/ui/SceneUI.js +25 -0
  143. package/src/ui/ServiceUI.js +261 -0
  144. package/src/ui/TemperatureUI.js +27 -0
  145. package/src/ui/ZigbeeConnectivityUI.js +27 -0
  146. package/test/BaseNode_spec.js +246 -0
  147. package/test/BaseUI_spec.js +17 -0
  148. package/test/BridgeConfigNode_spec.js +184 -0
  149. package/test/ButtonNode_spec.js +178 -0
  150. package/test/ClipApi_spec.js +769 -0
  151. package/test/DevicePowerNode_spec.js +186 -0
  152. package/test/GroupedLightNode_spec.js +218 -0
  153. package/test/LightLevelNode_spec.js +154 -0
  154. package/test/LightNode_spec.js +218 -0
  155. package/test/MotionNode_spec.js +186 -0
  156. package/test/RelativeRotataryNode_spec.js +325 -0
  157. package/test/ResourceNode_spec.js +308 -0
  158. package/test/Resource_spec.js +392 -0
  159. package/test/SceneNode_spec.js +33 -0
  160. package/test/ServiceNode_spec.js +33 -0
  161. package/test/TemperatureNode_spec.js +153 -0
  162. 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;