@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.
- package/.nyc_output/ccf277a0-5927-457e-9598-5bb4939612f5.json +1 -0
- package/.nyc_output/processinfo/ccf277a0-5927-457e-9598-5bb4939612f5.json +1 -0
- package/README.md +16 -5
- package/acquire_token_failed.log +14 -0
- package/attic-bridge-v2-key.txt +2 -0
- package/bridge_v3_grouped_light.log +43 -0
- package/cross_room_onoff.log +42 -0
- package/package.json +1 -1
- package/select_fire_effect.log +59 -0
- package/src/RestApi.js +21 -5
- package/src/all.html +4 -0
- package/src/all.js +4 -0
- package/src/clip/ClipApi.js +4 -35
- package/src/clip/Resource.js +1 -1
- package/src/nodes/BridgeConfigNode.js +1 -1
- package/src/nodes/CameraMotionNode.js +39 -0
- package/src/nodes/ContactNode.js +52 -0
- package/src/nodes/ResourceNode.js +21 -12
- package/src/ui/CameraMotionUI.js +27 -0
- package/src/ui/ContactUI.js +27 -0
- package/.nyc_output/badbd0a1-e95b-4210-b112-988173b5b131.json +0 -1
- package/.nyc_output/processinfo/badbd0a1-e95b-4210-b112-988173b5b131.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/.vscode/settings.json +0 -15
- package/.vscode/sftp.json +0 -11
- package/Todo.txt +0 -18
- package/coverage/BaseNode.js.html +0 -379
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -146
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -161
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/RestApi.js.html +0 -499
- package/coverage/lcov-report/src/all.js.html +0 -208
- package/coverage/lcov-report/src/clip/ClipApi.js.html +0 -1096
- package/coverage/lcov-report/src/clip/Resource.js.html +0 -481
- package/coverage/lcov-report/src/clip/index.html +0 -131
- package/coverage/lcov-report/src/debug.js.html +0 -253
- package/coverage/lcov-report/src/files.js.html +0 -118
- package/coverage/lcov-report/src/index.html +0 -161
- package/coverage/lcov-report/src/nodes/BaseNode.js.html +0 -381
- package/coverage/lcov-report/src/nodes/BridgeConfigNode.js.html +0 -817
- package/coverage/lcov-report/src/nodes/ButtonNode.js.html +0 -241
- package/coverage/lcov-report/src/nodes/DevicePowerNode.js.html +0 -199
- package/coverage/lcov-report/src/nodes/GroupedLightNode.js.html +0 -226
- package/coverage/lcov-report/src/nodes/LightLevelNode.js.html +0 -205
- package/coverage/lcov-report/src/nodes/LightNode.js.html +0 -226
- package/coverage/lcov-report/src/nodes/MotionNode.js.html +0 -205
- package/coverage/lcov-report/src/nodes/RelativeRotaryNode.js.html +0 -253
- package/coverage/lcov-report/src/nodes/ResourceNode.js.html +0 -388
- package/coverage/lcov-report/src/nodes/SceneNode.js.html +0 -124
- package/coverage/lcov-report/src/nodes/ServiceNode.js.html +0 -124
- package/coverage/lcov-report/src/nodes/TemperatureNode.js.html +0 -205
- package/coverage/lcov-report/src/nodes/ZigbeeConnectivityNode.js.html +0 -199
- package/coverage/lcov-report/src/nodes/index.html +0 -311
- package/coverage/lcov-report/src/ui/BaseUI.js.html +0 -484
- package/coverage/lcov-report/src/ui/BridgeConfigUI.js.html +0 -475
- package/coverage/lcov-report/src/ui/ButtonUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/DevicePowerUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/GroupedLightUI.js.html +0 -178
- package/coverage/lcov-report/src/ui/LightLevelUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/LightUI.js.html +0 -178
- package/coverage/lcov-report/src/ui/MotionUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/RelativeRotaryUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/ResourceUI.js.html +0 -511
- package/coverage/lcov-report/src/ui/SceneUI.js.html +0 -160
- package/coverage/lcov-report/src/ui/ServiceUI.js.html +0 -868
- package/coverage/lcov-report/src/ui/TemperatureUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/ZigbeeConnectivityUI.js.html +0 -166
- package/coverage/lcov-report/src/ui/index.html +0 -311
- package/coverage/lcov.info +0 -2217
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -196
- package/coverage/src/RestApi.js.html +0 -499
- package/coverage/src/all.js.html +0 -208
- package/coverage/src/clip/ClipApi.js.html +0 -1183
- package/coverage/src/clip/Resource.js.html +0 -481
- package/coverage/src/clip/index.html +0 -131
- package/coverage/src/debug.js.html +0 -253
- package/coverage/src/files.js.html +0 -118
- package/coverage/src/index.html +0 -116
- package/coverage/src/nodes/BaseNode.js.html +0 -379
- package/coverage/src/nodes/BridgeConfigNode.js.html +0 -517
- package/coverage/src/nodes/ButtonNode.js.html +0 -238
- package/coverage/src/nodes/DevicePowerNode.js.html +0 -196
- package/coverage/src/nodes/GroupedLightNode.js.html +0 -223
- package/coverage/src/nodes/LightLevelNode.js.html +0 -202
- package/coverage/src/nodes/LightNode.js.html +0 -223
- package/coverage/src/nodes/MotionNode.js.html +0 -202
- package/coverage/src/nodes/RelativeRotaryNode.js.html +0 -250
- package/coverage/src/nodes/ResourceNode.js.html +0 -409
- package/coverage/src/nodes/SceneNode.js.html +0 -124
- package/coverage/src/nodes/ServiceNode.js.html +0 -124
- package/coverage/src/nodes/TemperatureNode.js.html +0 -202
- package/coverage/src/nodes/ZigbeeConnectivityNode.js.html +0 -199
- package/coverage/src/nodes/index.html +0 -311
- package/coverage/src/ui/BaseUI.js.html +0 -484
- package/coverage/src/ui/BridgeConfigUI.js.html +0 -475
- package/coverage/src/ui/ButtonUI.js.html +0 -166
- package/coverage/src/ui/DevicePowerUI.js.html +0 -166
- package/coverage/src/ui/GroupedLightUI.js.html +0 -178
- package/coverage/src/ui/LightLevelUI.js.html +0 -166
- package/coverage/src/ui/LightUI.js.html +0 -178
- package/coverage/src/ui/MotionUI.js.html +0 -166
- package/coverage/src/ui/RelativeRotaryUI.js.html +0 -166
- package/coverage/src/ui/ResourceUI.js.html +0 -511
- package/coverage/src/ui/SceneUI.js.html +0 -160
- package/coverage/src/ui/ServiceUI.js.html +0 -868
- package/coverage/src/ui/TemperatureUI.js.html +0 -166
- package/coverage/src/ui/ZigbeeConnectivityUI.js.html +0 -166
- package/coverage/src/ui/index.html +0 -311
- package/test/BaseUI_spec.js +0 -17
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -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:
|
|
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,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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/src/clip/ClipApi.js
CHANGED
|
@@ -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 (
|
|
149
|
-
|
|
150
|
-
|
|
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
|
package/src/clip/Resource.js
CHANGED
|
@@ -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": "
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
+
}
|