hueget 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ See the [Readme file](https://github.com/jsiegenthaler/hueget/blob/master/README
4
4
 
5
5
  # Bug Fixes and Improvements
6
6
 
7
+ ## 0.7.0 (2024-01-06)
8
+ * Added toggle capability for lights and groups
9
+ * Bumped dependencies: "axios": "^1.6.5"
10
+
11
+ ## 0.6.6 (2023-11-27)
12
+ * Added some more README.md improvements
13
+
7
14
  ## 0.6.5 (2023-11-27)
8
15
  * Added error handling to catch listener errors (e.g. port in use)
9
16
  * Added some more README.md improvements
package/README.md CHANGED
@@ -28,6 +28,9 @@ Flash your lights in the entire house when the doorbell rings. I have a Shelly1
28
28
  ## Control Hue Lights directly from Shelly Motion Sensors
29
29
  Anything that can call a url when triggered - such as a Shelly Motion Sensor - can be used to turn the lights on and off again. Make sure the motion sensor calls a url to turn lights on, and a url to turn lights off. The Shelly Motion Sensor is ideal for this, as you can activate call urls for different motion triggers.
30
30
 
31
+ ## Toggle lights from a Shelly Button 1
32
+ Toggle a light or group of lights from a button that sends a non-changing URL. The ```toggle``` command is perfect for any pushbutton controller that does not know (or can not know) the current light state, and only sends a non-changing static URL, such as a Shelly Button 1.
33
+
31
34
  ## Be Home Soon Alert
32
35
  Flash lights in a room or in any group (zone, room) when someone comes home. The ```alert=lselect``` command is perfect to generate a 15 second long flash without any extra programming. Just call the URL from Apple HomeKit automations when a person arrives in your geofence.
33
36
 
@@ -35,19 +38,20 @@ Flash lights in a room or in any group (zone, room) when someone comes home. The
35
38
  # Installing hueget
36
39
  I run hueget on my raspberry pi. To install the latest version with NPM:
37
40
  ```
38
- $ sudo npm install -g hueget
41
+ $ npm install hueget
39
42
  ```
40
43
  Or for the latest beta version:
41
44
  ```
42
- $ sudo npm install -g hueget@beta
45
+ $ npm install hueget@beta
43
46
  ```
44
47
 
45
- npm installs hueget in `/usr/local/lib/node_modules/hueget/`.
48
+ You need to know where hueget was installed. Use `find -name hueget.js` to find the location of hueget.
49
+ I prefer to install locally. In my case, on my Raspberry Pi and using the default user pi, hueget installs in `/home/pi/node_modules/hueget/`
46
50
 
47
51
  # Updating hueget
48
52
  To update hueget to the latest version:
49
53
  ```
50
- $ sudo npm update -g hueget
54
+ $ npm update hueget
51
55
  ```
52
56
 
53
57
 
@@ -56,11 +60,12 @@ The following examples assume you have hueget in a folder that your system can f
56
60
 
57
61
  To see the help text, start hueget without any arguments as follows:
58
62
  ```
59
- $ node /usr/local/lib/node_modules/hueget/hueget.js
63
+ $ node /home/pi/node_modules/hueget/hueget.js
60
64
  ```
61
65
 
62
66
  hueget shows the following response:
63
67
  ```
68
+ Missing option: "--ip"
64
69
  USAGE: node hueget.js [OPTION1] [OPTION2]... arg1 arg2...
65
70
  The following options are supported:
66
71
  -i, --ip <ARG1> Philips Hue bridge IP address (required)
@@ -71,15 +76,15 @@ Note that options can be entered in any order.
71
76
 
72
77
  Example to run hueget on a raspberry pi to connect to a Philips Hue bridge with ip address `192.168.0.101`, default port `3000`, and with a Hue username of `UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj`:
73
78
  ```
74
- $ node /usr/local/lib/node_modules/hueget/hueget.js -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj
79
+ $ node /home/pi/node_modules/hueget/hueget.js -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj
75
80
  ```
76
81
  The same again, but using port `1234`:
77
82
  ```
78
- $ node /usr/local/lib/node_modules/hueget/hueget.js -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 1234
83
+ $ node /home/pi/node_modules/hueget/hueget.js -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 1234
79
84
  ```
80
85
  A successful start of hueget (using the above command to specify ip address 192.168.0.100 and port 1234) will show:
81
86
  ```
82
- hueget v0.6.2
87
+ hueget v0.7.0
83
88
  commands will be sent to 192.168.0.101 with username UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj
84
89
  listening on port 1234
85
90
  ```
@@ -91,7 +96,7 @@ $ pm2 startup
91
96
 
92
97
  To start hueget with pm2, and have it daemonized, monitored and kept alive forever:
93
98
  ```
94
- $ pm2 start /usr/local/lib/node_modules/hueget/hueget.js -- -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 3000
99
+ $ pm2 start /home/pi/node_modules/hueget/hueget.js -- -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 3000
95
100
  ```
96
101
  Check that hueget has started:
97
102
  ```
@@ -105,7 +110,7 @@ $ pm2 save
105
110
  Managing hueget in pm2 is straightforward:
106
111
  ```
107
112
  $ pm2 status
108
- $ pm2 start /usr/local/lib/node_modules/hueget/hueget.js -- -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 3000
113
+ $ pm2 start /home/pi/node_modules/hueget/hueget.js.js -- -i 192.168.0.101 -u UBxWZChHseyjeFwAkwgbdQ08x9XASWpanZZVg-mj -p 3000
109
114
  $ pm2 save
110
115
  $ pm2 stop hueget
111
116
  $ pm2 restart hueget
@@ -142,6 +147,7 @@ Examples:
142
147
  * Turn light 31 on at 50% brightness: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/state?on=true&bri=50
143
148
  * Turn light 31 on at 100% brightness: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/state?on=true&bri=100
144
149
  * Turn light 31 on at 100% brightness, 0.5,0.6 xy: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/state?on=true&bri=100&xy=[0.5%2c0.6]
150
+ * Toggle light 31: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/toggle
145
151
  * Identify light 31 with a single blink: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/state?alert=select
146
152
  * Identify light 31 with 15 seconds of blinking: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/31/state?alert=lselect
147
153
 
@@ -149,12 +155,14 @@ Examples:
149
155
  ### Group 0 (a special group for all lights in your home)
150
156
  * Turn group 0 on: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/0/action?on=true
151
157
  * Turn group 0 off: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/0/action?on=false
158
+ * Toggle group 0: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/0/toggle
152
159
  * Identify group 0 with 15 seconds of blinking: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/0/action?alert=lselect
153
160
 
154
161
 
155
162
  ### Group 2 (example)
156
163
  * Turn group 2 on: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/action?on=true
157
164
  * Turn group 2 off: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/action?on=false
165
+ * Toggle group 2: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/toggle
158
166
  * Turn group 2 on at 50% brightness: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/action?on=true&bri=50
159
167
  * Turn group 2 on at 100% brightness: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/action?on=true&bri=100
160
168
  * Turn group 2 on at 100% brightness, 0.5,0.6 xy: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/state?on=true&bri=100&xy=[0.5%2c0.6]
@@ -163,7 +171,15 @@ Examples:
163
171
 
164
172
  Groups are collections of lights, and are used for Rooms and Zones in the Hue app.
165
173
 
166
- # Supported Keywords
174
+ ## Special Commands
175
+ The hueget server supports a special toggle command, which does not exist natively in the Philips Hue bridge. This toggles (changes the state) of a specified light or a group, allowing you to toggle the light/group state with a single URL.
176
+
177
+ Syntax:
178
+ * Toggle light 1: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/lights/1/toggle
179
+ * Toggle group 2: http://192.168.0.101:3000/api/yourPhilipsHueBridgeUsername/groups/2/toggle
180
+
181
+
182
+ ## Supported Keywords
167
183
  The API is transparent to all Philips Hue keywords. It expects all name=value pairs to be separated by a comma. If any comma is required inside a value, eg: for the xy command which expects a value array, then you must url encode the comma to %2c.
168
184
 
169
185
  The full JSON response for a light looks like this:
@@ -176,12 +192,12 @@ The full JSON response for a group looks like this:
176
192
  {"name":"Lounge","lights":["9","1","2"],"sensors":[],"type":"Room","state":{"all_on":false,"any_on":false},"recycle":false,"class":"Lounge","action":{"on":false,"bri":0,"hue":7800,"sat":138,"effect":"none","xy":[0.5302,0.392],"ct":153,"alert":"select","colormode":"xy"}}
177
193
  ```
178
194
  The most common action keywords for state or group are:
179
- on, bri, hue, sat, effect, xy, ct, alert, colormode, mode (lights only)
195
+ on, bri, hue, sat, effect, xy, ct, alert, colormode, mode (lights only).
180
196
  More keywords exist, see the [API documentation](#api-documentation).
181
197
 
182
198
  ## on (get and set)
183
199
  Turn a light on or off. On=true, Off=false.
184
- Valid for light or group.
200
+ Valid for light or group. A group also supports all_on and any_on.
185
201
 
186
202
  ## bri (get and set)
187
203
  The brightness value to set the light to. Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum).
@@ -245,7 +261,7 @@ See the [API documentation](#api-documentation).
245
261
 
246
262
  ## API Documentation
247
263
  For full details of the control capabilities, please see the [official Philips Hue API reference](https://developers.meethue.com/develop/hue-api/).
248
- An [alternative unoffical reference](http://www.burgestrand.se/hue-api/), somewhat outdated also exists.
264
+ An [alternative unoffical reference](http://www.burgestrand.se/hue-api/), somewhat outdated, also exists.
249
265
 
250
266
 
251
267
  # Finding your Light or Group ids
@@ -263,6 +279,6 @@ Go backwards in the text until you find the keyword **state**, this is at the st
263
279
  ... ,"31":{"state":{"on":true,"bri":100,"hue":65396 ...
264
280
  ```
265
281
 
266
- Use the same method for groups to find the group id of the room you wish to control. Note that group id 0 is a special group containing all lights in the system, and is not returned by the ‘get all groups’ command. Group 0 is not visible, and cannot be created, modified or deleted using the API.
282
+ Use the same method for groups to find the group id of the room you wish to control. Note that group id 0 is a special group containing all lights in the system, and is not returned by the ‘get all groups’ command. Group 0 is not visible, and cannot be created, modified or deleted using the API, but group 0 can be controlled by the API.
267
283
 
268
284
 
package/hueget.js CHANGED
@@ -96,9 +96,12 @@ app.use('/api/' + options.username, (req, res) => {
96
96
  break;
97
97
  }
98
98
  //console.log('expectedCommand', expectedCommand );
99
- if (!command.startsWith(expectedCommand)) { throw errPrefix + 'unknown command "' + command + '", expecting "' + expectedCommand + '": "' + req.url + '"'; }
100
- if (!command.includes(expectedCommand + '?')) { throw errPrefix + 'query character "?" missing in "' + command + '", expecting "' + expectedCommand + '?<query>": "' + req.url + '"'; }
101
- if (command.endsWith(expectedCommand + '?')) { throw errPrefix + 'query missing in "' + command + '", expecting "' + expectedCommand + '?<query>": "' + req.url + '"'; }
99
+ // toggle is a special case, raise error for anything else that does not fit the syntax
100
+ if (command != 'toggle') {
101
+ if (!command.startsWith(expectedCommand)) { throw errPrefix + 'unknown command "' + command + '", expecting "' + expectedCommand + '": "' + req.url + '"'; }
102
+ if (!command.includes(expectedCommand + '?')) { throw errPrefix + 'query character "?" missing in "' + command + '", expecting "' + expectedCommand + '?<query>": "' + req.url + '"'; }
103
+ if (command.endsWith(expectedCommand + '?')) { throw errPrefix + 'query missing in "' + command + '", expecting "' + expectedCommand + '?<query>": "' + req.url + '"'; }
104
+ }
102
105
  }
103
106
 
104
107
 
@@ -137,41 +140,91 @@ app.use('/api/' + options.username, (req, res) => {
137
140
  result = result + ',"' + pair[0] + '":' + pairValue;
138
141
  });
139
142
  result = result.replace('{,','{') + '}'; // clean up, add brackets
140
- //console.log('result', result );
143
+ console.log('result', result );
141
144
  dataObj = JSON.parse(result);
142
145
  }
143
146
 
144
147
 
148
+
149
+
145
150
  // if a dataObj exists, send PUT; otherwise, send a GET
146
151
  // GET http://192.168.0.101/api/<username>/lights/31
147
152
  // PUT http://192.168.0.101/api/<username>/lights/31/state --data "{""on"":true}"
148
153
  var url = 'http://' + options.ip + '/api/' + options.username + '/' + resource;
149
154
  if (id) { url = url + '/' + id; } // add id if supplied
150
- if (dataObj){
151
- console.log('sending PUT: %s %s', url + '/' + expectedCommand, dataObj || '');
152
- axios.put(url + '/' + expectedCommand, dataObj)
155
+
156
+
157
+ // special handling for toggle command, this toggles a light or group state
158
+ if (command == 'toggle') {
159
+ console.log('toggling current state')
160
+ // Get actual state
161
+ console.log('sending GET: %s', url);
162
+ axios.get(url)
153
163
  .then(response => {
154
- console.log('PUT response:', response.status, response.statusText, JSON.stringify(response.data) );
155
- res.json(response.data);
156
- })
164
+ // for lights /lights/<id> state = on true/false
165
+ // for groups, /groups/<id> state = all_on true/false
166
+ switch(resource) {
167
+ case 'lights':
168
+ console.log('GET response:', response.status, response.statusText, "state:on="+response.data["state"]["on"] );
169
+ state = !response.data["state"]["on"] // get the current on state , as a boolean, and invert it
170
+ expectedCommand = 'state'
171
+ break;
172
+ case 'groups':
173
+ console.log('GET response:', response.status, response.statusText, "state:all_on="+response.data["state"]["all_on"] );
174
+ state = !response.data["state"]["all_on"] // get the current all_on state, as a boolean, and invert it
175
+ expectedCommand = 'action'
176
+ break;
177
+ }
178
+ // toggle light or group state
179
+ // lights: http://localhost:3000/api/<username>/lights/31/state?on=true
180
+ // groups: http://localhost:3000/api/<username>/groups/0/action?on=true
181
+ console.log('sending PUT: %s%s', url + '/' + "state?on=", state.toString() || '');
182
+ axios.put(url + '/' + expectedCommand,'{"on":' + state.toString() + '}')
183
+ .then(response => {
184
+ console.log('PUT response:', response.status, response.statusText, JSON.stringify(response.data) );
185
+ res.json(response.data);
186
+ })
187
+ .catch(error => {
188
+ const errText = error.syscall + ' ' + error.code + ' ' + error.address + ':' + error.port;
189
+ console.log('PUT error:', errText);
190
+ res.json({ error: errText });
191
+ });
192
+ })
157
193
  .catch(error => {
158
194
  const errText = error.syscall + ' ' + error.code + ' ' + error.address + ':' + error.port;
159
- console.log('PUT error:', errText);
195
+ console.log('GET error:', errText);
160
196
  res.json({ error: errText });
161
197
  });
162
- } else {
163
- console.log('sending GET: %s', url);
164
- axios.get(url)
198
+
199
+
200
+ // normal handling for non-toggle commands
201
+ } else {
202
+ if (dataObj){
203
+ console.log('sending PUT: %s %s', url + '/' + expectedCommand, dataObj || '');
204
+ axios.put(url + '/' + expectedCommand, dataObj)
165
205
  .then(response => {
166
- console.log('GET response:', response.status, response.statusText, JSON.stringify(response.data) );
206
+ console.log('PUT response:', response.status, response.statusText, JSON.stringify(response.data) );
167
207
  res.json(response.data);
168
208
  })
169
209
  .catch(error => {
170
210
  const errText = error.syscall + ' ' + error.code + ' ' + error.address + ':' + error.port;
171
- console.log('GET error:', errText);
211
+ console.log('PUT error:', errText);
172
212
  res.json({ error: errText });
173
213
  });
214
+ } else {
215
+ console.log('sending GET: %s', url);
216
+ axios.get(url)
217
+ .then(response => {
218
+ console.log('GET response:', response.status, response.statusText, JSON.stringify(response.data) );
219
+ res.json(response.data);
220
+ })
221
+ .catch(error => {
222
+ const errText = error.syscall + ' ' + error.code + ' ' + error.address + ':' + error.port;
223
+ console.log('GET error:', errText);
224
+ res.json({ error: errText });
225
+ });
174
226
  }
227
+ }
175
228
  return;
176
229
 
177
230
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hueget",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "description": "A simple API that converts the Philips Hue API PUT to GET requests, allowing control of Philips Hue lights via http GET requests",
5
5
  "main": "hueget.js",
6
6
  "scripts": {
@@ -25,7 +25,7 @@
25
25
  "author": "Jochen Siegenthaler (https://github.com/jsiegenthaler)",
26
26
  "license": "ISC",
27
27
  "dependencies": {
28
- "axios": "^1.6.2",
28
+ "axios": "^1.6.5",
29
29
  "express": "^4.18.2",
30
30
  "stdio": "^2.1.1"
31
31
  },