Skip to content

Commit

Permalink
Merge branch 'master' into 4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-murray authored Dec 27, 2019
2 parents ac814bb + a05b0c6 commit dbf5254
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 24 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

- name: npm test
run: |
npm ci
npm run test-types --if-present
npm run test-model --if-present
env:
CI: true
CI: true
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@

- Adding more in-depth tests to further increase coverage around types and models, and adding more edge case API level tests

## 3.4.3
- Long term fix for supporting older bridge types and creating new users. Issue #147

## 3.4.2
- Temporary fix for older bridges that do not support the entertainment API. Issue #147

## 3.4.1
- Fixing issue with the lookup for the Hue motion sensor, issue #146

Expand Down
43 changes: 26 additions & 17 deletions lib/api/Api.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ module.exports = class Api {
const self = this;

if (self.isSyncing()) {
return self._syncPromise.then(() => {return self._state;});
return self._syncPromise.then(() => {
return self._state;
});
} else {
return Promise.resolve(self._state);
}
Expand Down Expand Up @@ -150,20 +152,24 @@ module.exports = class Api {
syncWithBridge() {
const self = this;

// We can only sync if there is a username passed to us, which will not be the case if we are creating the user
// first.
if (self._config.username) {
if (!self.isSyncing()) {
self._syncPromise = self.configuration.getAll()
.then(data => {
self._syncPromise = null;
self._state = new StateCache(data);
self._lastSyncTime = new Date().getTime();
})
.catch(() => {
self._syncPromise = null;
});
if (!self.isSyncing()) {
if (self._config.username) {
// We can only sync if there is a username passed to us, which will not be the case if we are creating the user
// first.
self._syncPromise = self.configuration.getAll();
} else {
// We can only obtain the open config when no user is passed in
self._syncPromise = self.configuration.getUnauthenticatedConfig();
}

self._syncPromise = self._syncPromise.then(data => {
self._syncPromise = null;
self._state = new StateCache(data);
self._lastSyncTime = new Date().getTime();
})
.catch(() => {
self._syncPromise = null;
});
}
}

Expand All @@ -173,9 +179,10 @@ module.exports = class Api {
* @returns {Promise<Light>}
*/
getLightDefinition(id) {
return this.getCachedState().then(() => {
return this._state.getLight(id);
});
return this.getCachedState()
.then(() => {
return this._state.getLight(id);
});
}

_getConfig() {
Expand All @@ -189,4 +196,6 @@ module.exports = class Api {
_getRemote() {
return this._config.remote;
}


};
8 changes: 8 additions & 0 deletions lib/api/Configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ module.exports = class Configuration extends ApiDefinition {
return this.execute(configurationApi.getFullState);
}

/**
* Gets the aunauthenticated configuration data from the bridge.
* @returns {Promise<any>} The unautenticated configuration data
*/
getUnauthenticatedConfig() {
return this.execute(configurationApi.getUnauthenticatedConfig);
}

/**
* Updates a configuration value for the Hue Bridge.
* @param data {Object | BridgeConfiguration} An Object (or BridgeConfiguration) representing the data that is to be
Expand Down
8 changes: 7 additions & 1 deletion lib/api/Users.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ module.exports = class Users extends ApiDefinition {
* @returns {Promise<any>>}
*/
createUser(appName, deviceName) {
return this.execute(configurationApi.createUser, {appName: appName, deviceName: deviceName, generateKey: true});
return this.hueApi.getCachedState()
.then(state => {
//TODO may need to combine the modelid and API version, but am assuming that all newer bridges are kept up to
// date, as do not know the specific version number of the introduction of the generateclientkey parameter.
const oldBridge = state.modelid === 'BSB001';
return this.execute(configurationApi.createUser, {appName: appName, deviceName: deviceName, generateKey: !oldBridge});
});
}

deleteUser(username) {
Expand Down
18 changes: 14 additions & 4 deletions lib/api/Users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,33 @@ describe('Hue API #users', () => {

it('should create a user when the link button has been pressed', async () => {
const user = await unauthenticatedHue.users.createUser('node-hue-api', 'delete-tests');
expect(user).to.have.length.greaterThan(39);
expect(user).to.have.property('username').to.have.length.greaterThan(39);
expect(user).to.have.property('clientkey');
createdUser = user;
});


it('should not create a new user when link button not pressed', async () => {
try {
const user = await unauthenticatedHue.users.createUser('node-hue-api', 'node-hue-api-tests');
console.log(`Created user: ${user}`);
expect.fail('should not ger here unless the link button was pressed');
await unauthenticatedHue.users.createUser('node-hue-api', 'node-hue-api-tests');
expect.fail('should not get here unless the link button was pressed');
} catch (err) {
expect(err).to.be.instanceof(ApiError);
expect(err.getHueErrorType()).to.equal(101);
}
});
});

//TODO would need to mock this now as it is based of the modelid of the bridge
// describe('#createUser() no clientkey', () => {
//
// it('should create a user when the link button has been pressed', async () => {
// const user = await unauthenticatedHue.users.createUser('node-hue-api', 'delete-tests', true);
// expect(user).to.have.property('username').to.have.length.greaterThan(39);
// expect(user).to.not.have.property('clientkey');
// });
// });


describe.skip('#deleteUser()', () => {

Expand Down
25 changes: 25 additions & 0 deletions lib/api/http/endpoints/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,25 @@ module.exports = {
.get()
.acceptJson()
.uri('/<username>')
.pureJson(),
}

function getUnAuthenticatedConfig() {
return new ApiEndpoint()
.get()
.acceptJson()
.uri('/config')
.pureJson()
.postProcess(processUnauthenticatedData);
}

module.exports = {
createUser: createUser(),
getConfiguration: getConfiguration(),
updateConfiguration: updateConfiguration(),
deleteUser: deleteUser(),
getFullState: getFullState(),
getUnauthenticatedConfig: getUnAuthenticatedConfig(),
};


Expand All @@ -55,10 +73,17 @@ function processCreateUser(data) {
}
}


function createBridgeConfiguration(data) {
return model.createFromBridge('configuration', null, data);
}

function processUnauthenticatedData(data) {
return {
config: data
};
}

function buildUserPayload(data) {
//TODO utilize the type system
//TODO perform validation on the strings here
Expand Down
9 changes: 9 additions & 0 deletions lib/api/stateCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,14 @@ class Cache {

return light;
}

get modelid() {
// BSB001 is the first generation bridge, BSB002 is the current generation one that can support entertainment API
return this.data.config.modelid;
}

get apiversion() {
return this.data.config.apiversion;
}
}
module.exports = Cache;

0 comments on commit dbf5254

Please sign in to comment.