@hurenkam/hue-services 0.6.4 → 0.6.6

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 (121) hide show
  1. package/.nyc_output/ccf277a0-5927-457e-9598-5bb4939612f5.json +1 -0
  2. package/.nyc_output/processinfo/ccf277a0-5927-457e-9598-5bb4939612f5.json +1 -0
  3. package/README.md +16 -5
  4. package/acquire_token_failed.log +14 -0
  5. package/attic-bridge-v2-key.txt +2 -0
  6. package/bridge_v3_grouped_light.log +43 -0
  7. package/cross_room_onoff.log +42 -0
  8. package/package.json +1 -1
  9. package/select_fire_effect.log +59 -0
  10. package/src/RestApi.js +21 -5
  11. package/src/all.html +4 -0
  12. package/src/all.js +4 -0
  13. package/src/clip/ClipApi.js +4 -35
  14. package/src/clip/Resource.js +1 -1
  15. package/src/nodes/BridgeConfigNode.js +1 -1
  16. package/src/nodes/CameraMotionNode.js +39 -0
  17. package/src/nodes/ContactNode.js +52 -0
  18. package/src/nodes/ResourceNode.js +21 -12
  19. package/src/ui/CameraMotionUI.js +27 -0
  20. package/src/ui/ContactUI.js +27 -0
  21. package/.nyc_output/badbd0a1-e95b-4210-b112-988173b5b131.json +0 -1
  22. package/.nyc_output/processinfo/badbd0a1-e95b-4210-b112-988173b5b131.json +0 -1
  23. package/.nyc_output/processinfo/index.json +0 -1
  24. package/.vscode/settings.json +0 -15
  25. package/.vscode/sftp.json +0 -11
  26. package/Todo.txt +0 -18
  27. package/coverage/BaseNode.js.html +0 -379
  28. package/coverage/base.css +0 -224
  29. package/coverage/block-navigation.js +0 -87
  30. package/coverage/favicon.png +0 -0
  31. package/coverage/index.html +0 -146
  32. package/coverage/lcov-report/base.css +0 -224
  33. package/coverage/lcov-report/block-navigation.js +0 -87
  34. package/coverage/lcov-report/favicon.png +0 -0
  35. package/coverage/lcov-report/index.html +0 -161
  36. package/coverage/lcov-report/prettify.css +0 -1
  37. package/coverage/lcov-report/prettify.js +0 -2
  38. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  39. package/coverage/lcov-report/sorter.js +0 -196
  40. package/coverage/lcov-report/src/RestApi.js.html +0 -499
  41. package/coverage/lcov-report/src/all.js.html +0 -208
  42. package/coverage/lcov-report/src/clip/ClipApi.js.html +0 -1096
  43. package/coverage/lcov-report/src/clip/Resource.js.html +0 -481
  44. package/coverage/lcov-report/src/clip/index.html +0 -131
  45. package/coverage/lcov-report/src/debug.js.html +0 -253
  46. package/coverage/lcov-report/src/files.js.html +0 -118
  47. package/coverage/lcov-report/src/index.html +0 -161
  48. package/coverage/lcov-report/src/nodes/BaseNode.js.html +0 -381
  49. package/coverage/lcov-report/src/nodes/BridgeConfigNode.js.html +0 -817
  50. package/coverage/lcov-report/src/nodes/ButtonNode.js.html +0 -241
  51. package/coverage/lcov-report/src/nodes/DevicePowerNode.js.html +0 -199
  52. package/coverage/lcov-report/src/nodes/GroupedLightNode.js.html +0 -226
  53. package/coverage/lcov-report/src/nodes/LightLevelNode.js.html +0 -205
  54. package/coverage/lcov-report/src/nodes/LightNode.js.html +0 -226
  55. package/coverage/lcov-report/src/nodes/MotionNode.js.html +0 -205
  56. package/coverage/lcov-report/src/nodes/RelativeRotaryNode.js.html +0 -253
  57. package/coverage/lcov-report/src/nodes/ResourceNode.js.html +0 -388
  58. package/coverage/lcov-report/src/nodes/SceneNode.js.html +0 -124
  59. package/coverage/lcov-report/src/nodes/ServiceNode.js.html +0 -124
  60. package/coverage/lcov-report/src/nodes/TemperatureNode.js.html +0 -205
  61. package/coverage/lcov-report/src/nodes/ZigbeeConnectivityNode.js.html +0 -199
  62. package/coverage/lcov-report/src/nodes/index.html +0 -311
  63. package/coverage/lcov-report/src/ui/BaseUI.js.html +0 -484
  64. package/coverage/lcov-report/src/ui/BridgeConfigUI.js.html +0 -475
  65. package/coverage/lcov-report/src/ui/ButtonUI.js.html +0 -166
  66. package/coverage/lcov-report/src/ui/DevicePowerUI.js.html +0 -166
  67. package/coverage/lcov-report/src/ui/GroupedLightUI.js.html +0 -178
  68. package/coverage/lcov-report/src/ui/LightLevelUI.js.html +0 -166
  69. package/coverage/lcov-report/src/ui/LightUI.js.html +0 -178
  70. package/coverage/lcov-report/src/ui/MotionUI.js.html +0 -166
  71. package/coverage/lcov-report/src/ui/RelativeRotaryUI.js.html +0 -166
  72. package/coverage/lcov-report/src/ui/ResourceUI.js.html +0 -511
  73. package/coverage/lcov-report/src/ui/SceneUI.js.html +0 -160
  74. package/coverage/lcov-report/src/ui/ServiceUI.js.html +0 -868
  75. package/coverage/lcov-report/src/ui/TemperatureUI.js.html +0 -166
  76. package/coverage/lcov-report/src/ui/ZigbeeConnectivityUI.js.html +0 -166
  77. package/coverage/lcov-report/src/ui/index.html +0 -311
  78. package/coverage/lcov.info +0 -2217
  79. package/coverage/prettify.css +0 -1
  80. package/coverage/prettify.js +0 -2
  81. package/coverage/sort-arrow-sprite.png +0 -0
  82. package/coverage/sorter.js +0 -196
  83. package/coverage/src/RestApi.js.html +0 -499
  84. package/coverage/src/all.js.html +0 -208
  85. package/coverage/src/clip/ClipApi.js.html +0 -1183
  86. package/coverage/src/clip/Resource.js.html +0 -481
  87. package/coverage/src/clip/index.html +0 -131
  88. package/coverage/src/debug.js.html +0 -253
  89. package/coverage/src/files.js.html +0 -118
  90. package/coverage/src/index.html +0 -116
  91. package/coverage/src/nodes/BaseNode.js.html +0 -379
  92. package/coverage/src/nodes/BridgeConfigNode.js.html +0 -517
  93. package/coverage/src/nodes/ButtonNode.js.html +0 -238
  94. package/coverage/src/nodes/DevicePowerNode.js.html +0 -196
  95. package/coverage/src/nodes/GroupedLightNode.js.html +0 -223
  96. package/coverage/src/nodes/LightLevelNode.js.html +0 -202
  97. package/coverage/src/nodes/LightNode.js.html +0 -223
  98. package/coverage/src/nodes/MotionNode.js.html +0 -202
  99. package/coverage/src/nodes/RelativeRotaryNode.js.html +0 -250
  100. package/coverage/src/nodes/ResourceNode.js.html +0 -409
  101. package/coverage/src/nodes/SceneNode.js.html +0 -124
  102. package/coverage/src/nodes/ServiceNode.js.html +0 -124
  103. package/coverage/src/nodes/TemperatureNode.js.html +0 -202
  104. package/coverage/src/nodes/ZigbeeConnectivityNode.js.html +0 -199
  105. package/coverage/src/nodes/index.html +0 -311
  106. package/coverage/src/ui/BaseUI.js.html +0 -484
  107. package/coverage/src/ui/BridgeConfigUI.js.html +0 -475
  108. package/coverage/src/ui/ButtonUI.js.html +0 -166
  109. package/coverage/src/ui/DevicePowerUI.js.html +0 -166
  110. package/coverage/src/ui/GroupedLightUI.js.html +0 -178
  111. package/coverage/src/ui/LightLevelUI.js.html +0 -166
  112. package/coverage/src/ui/LightUI.js.html +0 -178
  113. package/coverage/src/ui/MotionUI.js.html +0 -166
  114. package/coverage/src/ui/RelativeRotaryUI.js.html +0 -166
  115. package/coverage/src/ui/ResourceUI.js.html +0 -511
  116. package/coverage/src/ui/SceneUI.js.html +0 -160
  117. package/coverage/src/ui/ServiceUI.js.html +0 -868
  118. package/coverage/src/ui/TemperatureUI.js.html +0 -166
  119. package/coverage/src/ui/ZigbeeConnectivityUI.js.html +0 -166
  120. package/coverage/src/ui/index.html +0 -311
  121. package/test/BaseUI_spec.js +0 -17
@@ -0,0 +1 @@
1
+ {"parent":null,"pid":232236,"argv":["/usr/bin/node","undefined"],"execArgv":["-e","\nconst pid = parseInt(process.argv[1], 10)\nprocess.title = 'node (foreground-child watchdog pid=' + pid + ')'\nif (!isNaN(pid)) {\n let barked = false\n // keepalive\n const interval = setInterval(() => {}, 60000)\n const bark = () => {\n clearInterval(interval)\n if (barked) return\n barked = true\n process.removeListener('SIGHUP', bark)\n setTimeout(() => {\n try {\n process.kill(pid, 'SIGKILL')\n setTimeout(() => process.exit(), 200)\n } catch (_) {}\n }, 500)\n })\n process.on('SIGHUP', bark)\n}\n"],"cwd":"/home/hurenkam/Workspace/node-red-hue-services","time":1766507688103,"ppid":1613,"coverageFilename":"/home/hurenkam/Workspace/node-red-hue-services/.nyc_output/ccf277a0-5927-457e-9598-5bb4939612f5.json","externalId":"","uuid":"ccf277a0-5927-457e-9598-5bb4939612f5","files":[]}
package/README.md CHANGED
@@ -6,15 +6,19 @@ node-red palette to access hue bridge through clip v2 api
6
6
 
7
7
  # Status
8
8
 
9
- (Last update: 2022/12/16)
9
+ (Last update: 2025/12/20)
10
10
  This is not complete, but seems to work consistent and reliable for the services I regularly use.
11
- (motion/temperature/light_level/light/grouped_light/button/relative_rotary/scene).
11
+ (motion/temperature/light_level/light/grouped_light/button/relative_rotary/scene/contact/camera_motion).
12
12
 
13
13
  Unit tests are in place for the server side nodes.
14
14
 
15
15
  Editor/UI functionality is currently not being tested apart from my own use in the editor, so your mileage may vary.
16
16
 
17
17
  # Changelog
18
+ v0.6.6: Support bridge v3 acquire key; better error handling on put requests.
19
+
20
+ v0.6.5: Added nodes for camera and contact sensor. Improved generic service usability.
21
+
18
22
  v0.6.4: Fix checkbox to work with Safari
19
23
 
20
24
  v0.6.3: Added option for nodes to generate a status event on startup (see issue #3)
@@ -30,6 +34,8 @@ This should address issue #1.
30
34
  ## Services:
31
35
  - Generic Service, in case you need some service that is not supported below, you can use this.
32
36
  - Button
37
+ - Camera Motion
38
+ - Contact
33
39
  - Device Power
34
40
  - Grouped Light
35
41
  - Light
@@ -50,14 +56,19 @@ This should address issue #1.
50
56
  - ~~Upload package to node-red library~~
51
57
  - ~~Unit Tests for Clip~~
52
58
  - ~~Unit Tests for Nodes~~
59
+ - ~~Contact sensor~~
60
+ - ~~Camera~~
61
+ - Geofencing
62
+ - Smart Scenes
53
63
  - Unit Tests for UI
54
64
 
65
+
55
66
  ## Postponed until after 0.6.x release
56
67
  - Improve 'smart' modes for Switch and Motion devices
57
- - Improve the generic device node (which allows using as of yet unsupported devices)
68
+ - ~~Improve the generic device node (which allows using as of yet unsupported devices)~~
58
69
  - Support more devices
59
- - smart button (should be easy to do, but i don't have one to test)
60
- - tap dial switch (should be similar to a lutron aurora, but i don't have one to test)
70
+ - ~~smart button (should be easy to do, but i don't have one to test)~~
71
+ - ~~tap dial switch (should be similar to a lutron aurora, but i don't have one to test)~~
61
72
 
62
73
  # Use
63
74
  Using these nodes requires a bit of knowledge on the clip v2 api, as i designed this palette
@@ -0,0 +1,14 @@
1
+ info:hue /AcquireApplicationKey +4m
2
+ trace:hue { ip: '192.168.1.83' } +4m
3
+ info:BridgeConfigNode AcquireApplicationKey(192.168.1.83) +4m
4
+ trace:BridgeConfigNode AcquireApplicationKey(192.168.1.83) [
5
+ {
6
+ error: {
7
+ type: 4,
8
+ address: '/',
9
+ description: 'method, GET, not available for resource, /'
10
+ }
11
+ }
12
+ ] +4m
13
+ error:hue /AcquireApplicationKey Error: undefined undefined +4m
14
+
@@ -0,0 +1,2 @@
1
+ 2ml0TLJEUc-j-byMhxIhymRSOUPUlnzRrZUiseoq
2
+
@@ -0,0 +1,43 @@
1
+ 2025-12-23T14:12:54.496Z trace:ClipApi:[Bridge v3a] constructor() found resource: {
2
+ id: '273c5261-178b-46ce-b339-b0dc5a99954e',
3
+ id_v1: '/groups/0',
4
+ owner: { rid: 'b832a485-c296-4e90-8efd-a3cbc84b690d', rtype: 'bridge_home' },
5
+ on: { on: true },
6
+ dimming: { brightness: 23.25333333333333 },
7
+ dimming_delta: {},
8
+ color_temperature: {},
9
+ color_temperature_delta: {},
10
+ color: {},
11
+ alert: { action_values: [ 'breathe' ] },
12
+ signaling: {
13
+ signal_values: [ 'alternating', 'no_signal', 'on_off', 'on_off_color' ]
14
+ },
15
+ dynamics: {},
16
+ type: 'grouped_light'
17
+ }
18
+ 2025-12-23T14:12:54.496Z info:Resource:[grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e] constructor()
19
+ 2025-12-23T14:12:54.496Z trace:ClipApi:[Bridge v3a] #registerResource( grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e )
20
+ 2025-12-23T14:12:54.496Z trace:ClipApi:[Bridge v3a] constructor() found resource: {
21
+ id: '43892de0-48e4-4210-91b0-9015fa64361c',
22
+ id_v1: '/groups/83',
23
+ owner: {
24
+ rid: 'a4e51f59-52c4-4049-8b8e-aba53e4db3f2',
25
+ rtype: 'private_group'
26
+ },
27
+ on: { on: true },
28
+ dimming: { brightness: 6.586666666666667 },
29
+ dimming_delta: {},
30
+ color_temperature: {},
31
+ color_temperature_delta: {},
32
+ color: {},
33
+ alert: { action_values: [ 'breathe' ] },
34
+ signaling: {
35
+ signal_values: [ 'alternating', 'no_signal', 'on_off', 'on_off_color' ]
36
+ },
37
+ dynamics: {},
38
+ type: 'grouped_light'
39
+ }
40
+
41
+
42
+
43
+
@@ -0,0 +1,42 @@
1
+ trace:BaseNode:[Living room v3a] onInput( {
2
+ _msgid: '0a227c9acf8aed5f',
3
+ payload: { on: { on: true } },
4
+ rtypes: [ 'light', 'grouped_light' ]
5
+ } ) +0ms
6
+ trace:Resource:[grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e] put( { on: { on: true } } ) +0ms
7
+ trace:ClipApi:[Bridge v3a] put( grouped_light , 273c5261-178b-46ce-b339-b0dc5a99954e , { on: { on: true } } ) +9s
8
+ trace:RestApi:[Bridge v3a] put(/clip/v2/resource/grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e) +9s
9
+ info:RestApi:[Bridge v3a] _handleRequest() pending: 1 +9s
10
+ info:RestApi:[Bridge v3a] _handleRequest() url: /clip/v2/resource/grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e method: PUT data: [object Object] +0ms
11
+ info:RestApi:[Bridge v3a] _request(https://192.168.1.83/clip/v2/resource/grouped_light/273c5261-178b-46ce-b339-b0dc5a99954e, PUT, { on: { on: true } } ) +0ms
12
+ trace:LightNode:[Hue go table lamp black] onUpdate( {
13
+ id: '80f763f6-990f-4dad-af00-a7c9b4dc6ce9',
14
+ id_v1: '/lights/2',
15
+ on: { on: true },
16
+ owner: { rid: 'f71e85e3-3f4a-4fb4-99d0-65827b0d3031', rtype: 'device' },
17
+ service_id: 0,
18
+ type: 'light'
19
+ } ) +9s
20
+ trace:LightNode:[Hue go table lamp black] updateStatus() +0ms
21
+ trace:ResourceNode:[Hue go table lamp black] onUpdate() +0ms
22
+ trace:GroupedLightNode:[Living room v3a] onUpdate( {
23
+ dimming: { brightness: 23.25333333333333 },
24
+ id: '273c5261-178b-46ce-b339-b0dc5a99954e',
25
+ id_v1: '/groups/0',
26
+ on: { on: true },
27
+ owner: { rid: 'b832a485-c296-4e90-8efd-a3cbc84b690d', rtype: 'bridge_home' },
28
+ type: 'grouped_light'
29
+ } ) +11s
30
+ trace:GroupedLightNode:[Living room v3a] updateStatus() +1ms
31
+ trace:ResourceNode:[Living room v3a] onUpdate() +0ms
32
+ trace:GroupedLightNode:[Bedroom v3a] onUpdate( {
33
+ dimming: { brightness: 7.904000000000001 },
34
+ id: 'ab4bf27c-30ed-46ed-ae41-3d08ea119383',
35
+ id_v1: '/groups/82',
36
+ on: { on: true },
37
+ owner: { rid: '785bb0cc-0e26-45ac-8556-4c63f7a4f176', rtype: 'room' },
38
+ type: 'grouped_light'
39
+ } ) +11s
40
+ trace:GroupedLightNode:[Bedroom v3a] updateStatus() +0ms
41
+ trace:ResourceNode:[Bedroom v3a] onUpdate() +0ms
42
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hurenkam/hue-services",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Custom node-red nodes for interfacing with philips hue devices",
5
5
  "author": "Mark Hurenkamp",
6
6
  "license": "Apache 2.0",
@@ -0,0 +1,59 @@
1
+ trace:ResourceNode:[Hue go table lamp black] onInput( { dimming: { brightness: 84.19 } } ) +3m
2
+ trace:Resource:[light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9 (Go table lamp 2)] put( { dimming: { brightness: 84.19 } } ) +3m
3
+ trace:ClipApi:[Bridge v3a] put( light , 80f763f6-990f-4dad-af00-a7c9b4dc6ce9 , { dimming: { brightness: 84.19 } } ) +3m
4
+ trace:RestApi:[Bridge v3a] put(/clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9) +3m
5
+ trace:BaseNode:[Hue go table lamp black] onInput( {
6
+ payload: { dimming: { brightness: 84.19 } },
7
+ _msgid: '512053756ae9b1ae',
8
+ rtypes: [ 'light', 'grouped_light' ]
9
+ } ) +3m
10
+ trace:ResourceNode:[Hue go table lamp black] onInput( {
11
+ effects: { status: 'fire' },
12
+ effects_v2: { status: { effect: 'fire', parameters: [Object] } }
13
+ } ) +0ms
14
+ trace:Resource:[light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9 (Go table lamp 2)] put( {
15
+ effects: { status: 'fire' },
16
+ effects_v2: { status: { effect: 'fire', parameters: [Object] } }
17
+ } ) +0ms
18
+ trace:ClipApi:[Bridge v3a] put( light , 80f763f6-990f-4dad-af00-a7c9b4dc6ce9 , {
19
+ effects: { status: 'fire' },
20
+ effects_v2: { status: { effect: 'fire', parameters: [Object] } }
21
+ } ) +0ms
22
+ trace:RestApi:[Bridge v3a] put(/clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9) +0ms
23
+ trace:BaseNode:[Hue go table lamp black] onInput( {
24
+ payload: { effects: { status: 'fire' }, effects_v2: { status: [Object] } },
25
+ _msgid: '75d3c53a12c48866',
26
+ rtypes: [ 'light', 'grouped_light' ]
27
+ } ) +0ms
28
+ trace:ResourceNode:[Hue go table lamp black] onInput( {
29
+ color: { xy: { x: 0.58, y: 0.38 } },
30
+ color_temperature: { mirek_valid: false }
31
+ } ) +1ms
32
+ trace:Resource:[light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9 (Go table lamp 2)] put( {
33
+ color: { xy: { x: 0.58, y: 0.38 } },
34
+ color_temperature: { mirek_valid: false }
35
+ } ) +1ms
36
+ trace:ClipApi:[Bridge v3a] put( light , 80f763f6-990f-4dad-af00-a7c9b4dc6ce9 , {
37
+ color: { xy: { x: 0.58, y: 0.38 } },
38
+ color_temperature: { mirek_valid: false }
39
+ } ) +1ms
40
+ trace:RestApi:[Bridge v3a] put(/clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9) +1ms
41
+ trace:BaseNode:[Hue go table lamp black] onInput( {
42
+ payload: {
43
+ color: { xy: [Object] },
44
+ color_temperature: { mirek_valid: false }
45
+ },
46
+ _msgid: 'c7dd57e7101190f9',
47
+ rtypes: [ 'light', 'grouped_light' ]
48
+ } ) +1ms
49
+ info:RestApi:[Bridge v3a] _handleRequest() pending: 3 +3m
50
+ info:RestApi:[Bridge v3a] _handleRequest() url: /clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9 method: PUT data: [object Object] +0ms
51
+ info:RestApi:[Bridge v3a] _request(https://192.168.1.83/clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9, PUT, { dimming: { brightness: 84.19 } } ) +0ms
52
+ info:RestApi:[Bridge v3a] _handleRequest() pending: 2 +118ms
53
+ info:RestApi:[Bridge v3a] _handleRequest() url: /clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9 method: PUT data: [object Object] +0ms
54
+ info:RestApi:[Bridge v3a] _request(https://192.168.1.83/clip/v2/resource/light/80f763f6-990f-4dad-af00-a7c9b4dc6ce9, PUT, {
55
+ effects: { status: 'fire' },
56
+ effects_v2: { status: { effect: 'fire', parameters: [Object] } }
57
+ } ) +0ms
58
+
59
+
package/src/RestApi.js CHANGED
@@ -81,6 +81,7 @@ class RestApi {
81
81
  var local = this;
82
82
 
83
83
  var request = this.#requestQ.shift();
84
+ this.#info("_handleRequest() url: " + request.url + " method: " + request.method + " data: " + request.data);
84
85
  this.#request(request.url, request.method, request.data)
85
86
  .then(async (result) => {
86
87
  await local.#limiter.removeTokens(1,()=>{});
@@ -90,7 +91,10 @@ class RestApi {
90
91
  .catch((error) => {
91
92
  this.#error("_handleRequest(" + request.url + ") error: ");
92
93
  this.#error(error);
93
- request.reject();
94
+ var info = {};
95
+ info.error = error;
96
+ info.request = request;
97
+ request.reject(info);
94
98
 
95
99
  // back off for a few seconds, just in case we ran into a 429 error.
96
100
  local.#timeout = setTimeout(local.#handleRequest.bind(local), 5000);
@@ -105,33 +109,45 @@ class RestApi {
105
109
  get(url) {
106
110
  this.#trace("get(" + url + ")");
107
111
  var local = this;
108
- return new Promise(function (resolve, reject) {
112
+
113
+ let promise = new Promise((resolve, reject) => {
109
114
  local.#requestQ.push({ url: url, method: "GET", data: null, resolve: resolve, reject: reject });
110
115
  });
116
+
117
+ return promise;
111
118
  }
112
119
 
113
120
  put(url, data) {
114
121
  this.#trace("put(" + url + ")");
115
122
  var local = this;
116
- return new Promise(function (resolve, reject) {
123
+
124
+ let promise = new Promise((resolve, reject) => {
117
125
  local.#requestQ.push({ url: url, method: "PUT", data: data, resolve: resolve, reject: reject });
118
126
  });
127
+
128
+ return promise;
119
129
  }
120
130
 
121
131
  post(url, data) {
122
132
  this.#trace(".post(" + url + ")");
123
133
  var local = this;
124
- return new Promise(function (resolve, reject) {
134
+
135
+ let promise = new Promise((resolve, reject) => {
125
136
  local.#requestQ.push({ url: url, method: "POST", data: data, resolve: resolve, reject: reject });
126
137
  });
138
+
139
+ return promise;
127
140
  }
128
141
 
129
142
  delete(url, data) {
130
143
  this.#trace(".delete(" + url + ")");
131
144
  var local = this;
132
- return new Promise(function (resolve, reject) {
145
+
146
+ let promise = new Promise((resolve, reject) => {
133
147
  local.#requestQ.push({ url: url, method: "DELETE", data: data, resolve: resolve, reject: reject });
134
148
  });
149
+
150
+ return promise;
135
151
  }
136
152
  }
137
153
 
package/src/all.html CHANGED
@@ -3,6 +3,8 @@
3
3
  import { BridgeConfigUI } from "/src/ui/BridgeConfigUI.js";
4
4
 
5
5
  import { ButtonUI } from "/src/ui/ButtonUI.js";
6
+ import { CameraMotionUI } from "/src/ui/CameraMotionUI.js";
7
+ import { ContactUI } from "/src/ui/ContactUI.js";
6
8
  import { DevicePowerUI } from "/src/ui/DevicePowerUI.js";
7
9
  import { GroupedLightUI } from "/src/ui/GroupedLightUI.js";
8
10
  import { LightUI } from "/src/ui/LightUI.js";
@@ -18,6 +20,8 @@
18
20
  "BridgeConfigNode": BridgeConfigUI,
19
21
 
20
22
  "ButtonNode": ButtonUI,
23
+ "CameraMotionNode": CameraMotionUI,
24
+ "ContactNode": ContactUI,
21
25
  "DevicePowerNode": DevicePowerUI,
22
26
  "GroupedLightNode": GroupedLightUI,
23
27
  "LightNode": LightUI,
package/src/all.js CHANGED
@@ -7,6 +7,8 @@ module.exports = function(RED) {
7
7
  const BridgeConfigNode = require('./nodes/BridgeConfigNode');
8
8
 
9
9
  const ButtonNode = require('./nodes/ButtonNode');
10
+ const CameraMotionNode = require('./nodes/CameraMotionNode');
11
+ const ContactNode = require('./nodes/ContactNode');
10
12
  const DevicePowerNode = require('./nodes/DevicePowerNode');
11
13
  const GroupedLightNode = require('./nodes/GroupedLightNode');
12
14
  const LightNode = require('./nodes/LightNode');
@@ -23,6 +25,8 @@ module.exports = function(RED) {
23
25
  "BridgeConfigNode": BridgeConfigNode,
24
26
 
25
27
  "ButtonNode": ButtonNode,
28
+ "CameraMotionNode": CameraMotionNode,
29
+ "ContactNode": ContactNode,
26
30
  "DevicePowerNode": DevicePowerNode,
27
31
  "GroupedLightNode": GroupedLightNode,
28
32
  "LightNode": LightNode,
@@ -24,33 +24,6 @@ class ClipApi extends events.EventEmitter {
24
24
  #info;
25
25
  #trace;
26
26
 
27
- #factory = {
28
- "behavior_script": Resource,
29
- "behavior_instance": Resource,
30
- "bridge": Resource,
31
- "bridge_home": Resource,
32
- "button": Resource,
33
- "device": Resource,
34
- "device_power": Resource,
35
- "entertainment": Resource,
36
- "entertainment_configuration": Resource,
37
- "geolocation": Resource,
38
- "grouped_light": Resource,
39
- "homekit": Resource,
40
- "light": Resource,
41
- "light_level": Resource,
42
- "matter": Resource,
43
- "motion": Resource,
44
- "relative_rotary": Resource,
45
- "room": Resource,
46
- "scene": Resource,
47
- "temperature": Resource,
48
- "zgp_connectivity": Resource,
49
- "zigbee_connectivity": Resource,
50
- "zigbee_device_discovery": Resource,
51
- "zone": Resource
52
- };
53
-
54
27
  constructor(ip,key,name) {
55
28
  super();
56
29
 
@@ -145,13 +118,9 @@ class ClipApi extends events.EventEmitter {
145
118
  response.data.forEach((item) => {
146
119
  this.#trace("constructor() found resource:", item);
147
120
 
148
- if (Object.keys(this.#factory).includes(item.type)) {
149
- if (!this._isResourceRegistered(item.id)) {
150
- var resource = new this.#factory[item.type](item,this);
151
- this._registerResource(resource);
152
- }
153
- } else {
154
- this.#warn("constructor(): Missing factory for type", item.type);
121
+ if (!this._isResourceRegistered(item.id)) {
122
+ var resource = new Resource(item,this);
123
+ this._registerResource(resource);
155
124
  }
156
125
  });
157
126
 
@@ -364,4 +333,4 @@ class ClipApi extends events.EventEmitter {
364
333
  }
365
334
  }
366
335
 
367
- module.exports = ClipApi
336
+ module.exports = ClipApi
@@ -107,7 +107,7 @@ class Resource extends events.EventEmitter {
107
107
 
108
108
  put(data) {
109
109
  this.#trace("put(",data,")");
110
- this.clip().put(this.rtype(),this.rid(), data);
110
+ return this.clip().put(this.rtype(),this.rid(), data);
111
111
  }
112
112
 
113
113
  onEvent(event) {
@@ -119,7 +119,7 @@ class BridgeConfigNode extends BaseNode {
119
119
  var id = "BridgeConfig (" + Math.floor((Math.random() * 100) + 1) + ")";
120
120
  var request = {
121
121
  "method": "POST",
122
- "url": "http://" + ip + "/api",
122
+ "url": "https://" + ip + "/api",
123
123
  "headers": { "Content-Type": "application/json; charset=utf-8" },
124
124
  "data": { "devicetype": id },
125
125
  "httpsAgent": new https.Agent({ rejectUnauthorized: false })
@@ -0,0 +1,39 @@
1
+ ResourceNode = require("./ResourceNode");
2
+
3
+ class CameraMotionNode extends ResourceNode {
4
+ #info;
5
+ #trace;
6
+
7
+ constructor(config) {
8
+ super(config);
9
+ this.#info = require('debug')('info').extend('CameraMotionNode').extend("["+this.logid()+"]");
10
+ this.#trace = require('debug')('trace').extend('CameraMotionNode').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().motion)) {
29
+ if (resource.data().motion.motion!=null) {
30
+ fill = (resource.data().motion.motion==true)? "blue" : "grey";
31
+ text = (resource.data().motion.motion==true)? "motion" : "no motion";
32
+ }
33
+ }
34
+
35
+ this.status({fill: fill, shape: shape, text: text});
36
+ }
37
+ }
38
+
39
+ module.exports = CameraMotionNode;
@@ -0,0 +1,52 @@
1
+ ResourceNode = require("./ResourceNode");
2
+
3
+ class ContactNode extends ResourceNode {
4
+ #fill;
5
+
6
+ #info;
7
+ #trace;
8
+
9
+ constructor(config) {
10
+ super(config);
11
+
12
+ this.#info = require('debug')('info').extend('ContactNode').extend("["+this.logid()+"]");
13
+ this.#trace = require('debug')('trace').extend('ContactNode').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().contact_report)) {
42
+ if (resource.data().contact_report.state!=null) {
43
+ fill = (resource.data().contact_report.state=="contact")? "grey" : "blue";
44
+ text = (resource.data().contact_report.state=="contact")? "closed" : "open";
45
+ }
46
+ }
47
+
48
+ this.status({fill: fill, shape: shape, text: text});
49
+ }
50
+ }
51
+
52
+ module.exports = ContactNode;
@@ -82,26 +82,35 @@ class ResourceNode extends BaseNode {
82
82
  }
83
83
 
84
84
  onInput(msg) {
85
+ super.onInput(msg);
86
+
85
87
  var resource = this.resource();
86
88
  if (!resource) {
87
89
  this.#trace("onInput(): Resource not found",this.rid());
90
+ return;
88
91
  }
89
92
 
90
- if (msg.rtypes) {
91
- if ((resource) && (msg.rtypes.includes(resource.rtype()))) {
92
- this.#trace("onInput(",msg.payload,")");
93
- resource.put(msg.payload);
94
- }
93
+ if (msg.rtypes && msg.rtypes.includes(resource.rtype())) {
94
+ resource.put(msg.payload).then(result => {
95
+ return;
96
+ }, info => {
97
+ var error = info.error;
98
+ var request = info.request;
99
+ this.error("ResourceNode::onInput() request: "+JSON.stringify(request)+" error: " + error);
100
+ return;
101
+ });
95
102
  }
96
103
 
97
- if (msg.rids) {
98
- if ((resource) && (msg.rids.includes(resource.rid()))) {
99
- this.#trace("onInput(",msg.payload,")");
100
- resource.put(msg.payload);
101
- }
104
+ if (msg.rids && msg.rids.includes(resource.rid())) {
105
+ resource.put(msg.payload).then(result => {
106
+ return;
107
+ }, info => {
108
+ var error = info.error;
109
+ var request = info.request;
110
+ this.error("ResourceNode::onInput() request: "+JSON.stringify(request)+" error: " + error);
111
+ return;
112
+ });
102
113
  }
103
-
104
- super.onInput(msg);
105
114
  }
106
115
  }
107
116
 
@@ -0,0 +1,27 @@
1
+ import { ServiceUI } from "./ServiceUI.js"
2
+
3
+ export class CameraMotionUI extends ServiceUI {
4
+ constructor() {
5
+ super("Camera Motion","hue services","camera_motion");
6
+ console.log("CameraMotionUI.constructor()");
7
+
8
+ this.config.color = "#FFC0FF";
9
+ this.config.icon = "font-awesome/fa-rss";
10
+ }
11
+
12
+ buildHelp() {
13
+ var help = super.buildHelp();
14
+ help["Outputs"] += "\
15
+ The CameraMotion node only has one output. It will listen to events for the specified service \
16
+ and forward them to the output as `msg.payload` which contains the motion events in a \
17
+ JSON format conform the clip v2 specification.\n\n\
18
+ Please see the Hue CLIP API documentation: \n\n\
19
+ https://developers.meethue.com/develop/hue-api-v2";
20
+ return help;
21
+ }
22
+
23
+ onEditPrepare(config) {
24
+ super.onEditPrepare(config);
25
+ $('#node-container-rtype').hide();
26
+ }
27
+ }
@@ -0,0 +1,27 @@
1
+ import { ServiceUI } from "./ServiceUI.js"
2
+
3
+ export class ContactUI extends ServiceUI {
4
+ constructor() {
5
+ super("Contact","hue services","contact");
6
+ console.log("ContactUI.constructor()");
7
+
8
+ this.config.color = "#FFD8FF";
9
+ this.config.icon = "font-awesome/fa-step-forward";
10
+ }
11
+
12
+ buildHelp() {
13
+ var help = super.buildHelp();
14
+ help["Outputs"] += "\
15
+ The Contact node only has one output. It will listen to events for the specified service \
16
+ and forward them to the output as `msg.payload` which contains the contact events in a \
17
+ JSON format conform the clip v2 specification.\n\n\
18
+ Please see the Hue CLIP API documentation: \n\n\
19
+ https://developers.meethue.com/develop/hue-api-v2";
20
+ return help;
21
+ }
22
+
23
+ onEditPrepare(config) {
24
+ super.onEditPrepare(config);
25
+ $('#node-container-rtype').hide();
26
+ }
27
+ }