@hurenkam/hue-services 0.6.5 → 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.
@@ -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
@@ -15,6 +15,8 @@ Unit tests are in place for the server side nodes.
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
+
18
20
  v0.6.5: Added nodes for camera and contact sensor. Improved generic service usability.
19
21
 
20
22
  v0.6.4: Fix checkbox to work with Safari
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hurenkam/hue-services",
3
- "version": "0.6.5",
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
 
@@ -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 })
@@ -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