Skip to content

Commit 24170ed

Browse files
Added interactive selection screen for light groups
1 parent 6721842 commit 24170ed

File tree

4 files changed

+126
-76
lines changed

4 files changed

+126
-76
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ dist
102102

103103
# TernJS port file
104104
.tern-port
105+
106+
.node-persist
107+
config.json

config.default.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"bridgeIp": "192.168.0.4",
3+
"userId": "xyzzy",
4+
"aPin": 17,
5+
"bPin": 18,
6+
"togglePin": 27
7+
}

index.js

+111-73
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ const Gpio = require('onoff').Gpio;
33
const http = require('http');
44
const fork = require('child_process').fork;
55
const path = require('path');
6+
const storage = require('node-persist');
7+
const {
8+
bridgeIp,
9+
userId,
10+
aPin, bPin, togglePin
11+
} = require('./config.json');
12+
13+
const FIELDS = {'bri': 8, 'hue': 1024, 'sat': 8};
614

715
class RotaryEncoder extends EventEmitter {
816
constructor({a,b,toggle}) {
@@ -189,101 +197,131 @@ class Worker {
189197
}
190198
}
191199

192-
let groupState = null;
193-
194-
195200
class LightGroupController {
196201
constructor(groupId, api, worker) {
197202
this.groupId = groupId;
198203
this.api = api;
199204
this.worker = worker;
200205
this.groupState = null;
206+
this.fieldIndex = 0;
201207
}
202208

203-
refresh() {
204-
return this.api.getGroup(this.groupId).then(({ statusCode, body}) => {
205-
this.groupState = body.action;
206-
this.worker.send('lightgroup_control', body.action);
207-
return this.groupState;
208-
});
209+
async init() {
210+
const {statusCode, body} = await this.api.getGroup(this.groupId);
211+
this.groupState = body.action;
212+
this.worker.send('lightgroup_control', body.action);
213+
return this.groupState;
209214
}
210-
update(change) {
211-
if (this.groupState) {
212-
Object.keys(change).forEach(k => {
213-
if (typeof change[k] === 'number') {
214-
this.groupState[k] += change[k]
215-
} else {
216-
this.groupState[k] = change[k]
217-
}
218-
});
219-
this.worker.send('lightgroup_control',this.groupState);
220-
}
221215

222-
return this.api.putGroup(this.groupId, Object.keys(change).reduce((prev, next) => {
223-
const value = change[next];
224-
prev[next + (typeof value === 'number' ? '_inc' : '')] = value;
225-
return prev;
226-
}, {})).then(response => {
227-
this.refresh();
228-
});
216+
async onEvent(event, args) {
217+
switch (event) {
218+
case 'rotation': {
219+
const field = Object.keys(FIELDS)[this.fieldIndex];
220+
const change = {
221+
[field]: FIELDS[field] * args,
222+
'on': true
223+
};
224+
if (this.groupState) {
225+
Object.keys(change).forEach(k => {
226+
if (typeof change[k] === 'number') {
227+
this.groupState[k] += change[k]
228+
} else {
229+
this.groupState[k] = change[k]
230+
}
231+
});
232+
this.worker.send('lightgroup_control',this.groupState);
233+
}
234+
235+
const response = await this.api.putGroup(this.groupId, Object.keys(change).reduce((prev, next) => {
236+
const value = change[next];
237+
prev[next + (typeof value === 'number' ? '_inc' : '')] = value;
238+
return prev;
239+
}, {}));
240+
await this.init();
241+
break;
242+
}
243+
244+
case 'toggle': {
245+
this.fieldIndex++;
246+
if (this.fieldIndex >= Object.keys(FIELDS).length) {
247+
this.fieldIndex = 0;
248+
}
249+
break;
250+
}
251+
}
229252
}
230253
}
231254

232-
const BRIDGE = '192.168.0.4';
233-
const USER_ID = 'O7nK3Cv1WUSGeOtiuzWbPCsxbjxCdIwmRFWPo72Z';
234-
const GROUP_ID = 8;
235-
const FIELDS = {'bri': 8, 'hue': 1024, 'sat': 8};
236-
237-
const api = new HueAPI(BRIDGE, USER_ID);
255+
class LightGroupSelector {
256+
constructor(api, uiWorker) {
257+
this.api = api;
258+
this.worker = uiWorker;
259+
this.selected = 0;
238260

239-
const uiWorker = new Worker();
261+
}
262+
async init() {
263+
const response = await this.api.getGroups();
264+
this.options = Object.keys(response.body).map(i => ({ key: parseInt(i, 10), value: response.body[i].name }));
265+
this.worker.send('lightgroup_select', {
266+
selected: this.selected,
267+
options: this.options
268+
});
269+
}
240270

241-
api.getGroups().then((response) => uiWorker.send('lightgroup_select', {
242-
selected: 0,
243-
options: Object.keys(response.body).map(i => response.body[i].name)
244-
}));
245-
/**
271+
async onEvent(event, args) {
272+
switch (event) {
273+
case 'rotation': {
274+
this.selected += (args > 0 ? 1: -1);
275+
if (this.selected >= this.options.length) {
276+
this.selected = this.options.length - 1;
277+
} else if (this.selected < 0) {
278+
this.selected = 0;
279+
}
280+
this.worker.send('lightgroup_select', {
281+
selected: this.selected,
282+
options: this.options
283+
});
284+
break;
285+
}
246286

287+
case 'toggle': {
288+
const groupId = this.options[this.selected].key;
289+
console.log('Selected groupId: ' + groupId);
290+
await storage.setItem('groupId', groupId);
291+
const newController = new LightGroupController(groupId, this.api, this.worker);
292+
await newController.init();
293+
controller = newController;
294+
break;
295+
}
296+
}
297+
}
298+
}
247299

248-
const controller = new LightGroupController(GROUP_ID, api, uiWorker);
300+
let controller = null;
249301

250-
controller.refresh().then((state) => {
251-
let fieldIndex = 0;
302+
storage.init().then(async () => {
303+
const api = new HueAPI(bridgeIp, userId);
304+
const uiWorker = new Worker();
305+
const encoder = new RotaryEncoder({
306+
a: aPin,
307+
b: bPin,
308+
toggle: togglePin
309+
});
252310

253-
const encoder = new RotaryEncoder({
254-
a: 17,
255-
b: 18,
256-
toggle: 27,
257-
inc: 32
258-
});
311+
const groupId = await storage.getItem('groupId');
312+
if (!groupId) {
313+
controller = new LightGroupSelector(api, uiWorker);
314+
} else {
315+
console.log('Controlling groupId: ' + groupId);
316+
controller = new LightGroupController(groupId, api, uiWorker);
317+
}
318+
await controller.init();
259319

260-
encoder.on('rotation', throttlePromise((value) => {
261-
const field = Object.keys(FIELDS)[fieldIndex];
262-
const change = FIELDS[field] * value;
263-
return controller.update({
264-
[field]: change,
265-
'on': true
266-
});
267-
}, {
268-
debounce: 10,
320+
encoder.on('rotation', throttlePromise((value) => controller.onEvent('rotation', value), {
321+
debounce: 100,
269322
delay: 500,
270323
reduce: (prev, next) => [prev[0] + next[0]]
271324
}));
272325

273-
encoder.on('toggle',() => {// throttlePromise(() => {
274-
fieldIndex++;
275-
if (fieldIndex >= Object.keys(FIELDS).length) {
276-
fieldIndex = 0;
277-
}
278-
console.log('Selected ' + Object.keys(FIELDS)[fieldIndex]);
279-
/**
280-
on = !on;
281-
return api.putGroup(GROUP_ID, {
282-
on: on
283-
}).then(response => {
284-
console.log(response.body);
285-
return updateGroupUI();
286-
});
287-
});
326+
encoder.on('toggle',() => controller.onEvent('toggle'));
288327
});
289-
*/

worker.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,16 @@ const uiRenderer = new UIRenderer()
149149
const start = Math.max(0, selectedIndex - 2);
150150
const end = Math.min(next.options.length, start + 5);
151151

152+
oled.setCursor(1, 1);
153+
oled.writeString(font, 1, 'Choose light group:', false);
152154
let i = 0;
153155
for (let index = start; index < end; ++index) {
154156
if (index === selectedIndex) {
155-
oled.setCursor(1, 11 + (7 * i));
157+
oled.setCursor(1, 18 + (8 * i));
156158
oled.writeString(font, 1, '>', false);
157159
}
158-
oled.setCursor(6, 11 + (7 * i++));
159-
oled.writeString(font,1, next.options[index], 1, false);
160+
oled.setCursor(8, 18 + (8 * i++));
161+
oled.writeString(font,1, next.options[index].value, 1, false);
160162
}
161163

162164
oled.update();

0 commit comments

Comments
 (0)