UNPKG

node-hue-api

Version:
728 lines (548 loc) 24.9 kB
'use strict'; const expect = require('chai').expect , v3 = require('../v3').v3 , discovery = require('../index').discovery , model = require('@peter-murray/hue-bridge-model').model , Light = model.Light , LightState = model.lightStates.LightState , RateLimitConfig = require('./HueApiRateLimits').HueApiRateLimits , testValues = require('../../test/support/testValues.js') ; const EXTENDED_COLOR_LIGHT_ID = testValues.testLightId; describe('Hue API #lights', function () { let hue; this.timeout(10000); before(async () => { const searchResults = await discovery.mdnsSearch(); if (!searchResults || searchResults.length === 0) { throw new Error('Failed to find a bridge in mDNS search'); } const localApi = v3.api.createLocal(searchResults[0].ipaddress); hue = await localApi.connect(testValues.username); }); describe('#getAll()', () => { it('should find some', async () => { const lights = await hue.lights.getAll(); expect(lights).to.have.length.greaterThan(20); let light = lights[0]; expect(light).to.have.property('id').to.be.greaterThan(0); expect(light instanceof Light).to.be.true; }); }); describe('#getLight()', () => { it('should get light with id === 2', async () => { const result = await hue.lights.getLight(2); expect(result).to.have.property('id').to.equal(2); }); it('should get light with id as a Light object', async () => { const light = await hue.lights.getLight(2); expect(light).to.have.property('id').to.equal(2); const result = await hue.lights.getLight(light); expect(result).to.have.property('id').to.equal(2); }); }); // describe('#getLightById()', () => { // // it('should get light with id === 2', async () => { // const result = await hue.lights.getLightById(2); // expect(result).to.have.property('id').to.equal(2); // }); // // it('should get light with id as a Light object', async () => { // const light = await hue.lights.getLightById(2); // expect(light).to.have.property('id').to.equal(2); // // const result = await hue.lights.getLightById(light); // expect(result).to.have.property('id').to.equal(2); // }); // }); describe('#getLightByName()', () => { it('should get light with a valid name', async () => { // TODO hardcoded flaky test when hue config changes const name = 'Monitor Left' , result = await hue.lights.getLightByName(name) ; expect(result[0]).to.have.property('name').to.equal(name); }); }); describe('#getNew()', () => { it('should find some', async () => { const result = await hue.lights.getNew(); expect(result).to.have.property('lastscan'); }); }); describe('#searchForNew()', () => { it('should perform a search', async () => { const result = await hue.lights.searchForNew(); expect(result).to.be.true; }); }); describe('rate limiting', function () { this.timeout(20000); async function multipleRequests(hueApi, count) { const promises = []; const start = Date.now(); for (let i = 0; i < 10; i++) { promises.push(hueApi.lights.setLightState(EXTENDED_COLOR_LIGHT_ID, {bri:100 + i})); } await Promise.all(promises); const stop = Date.now(); return stop - start; } before(async () => { await hue.lights.setLightState(EXTENDED_COLOR_LIGHT_ID, {on: true}); }); after(async () => { await hue.lights.setLightState(EXTENDED_COLOR_LIGHT_ID, {on: false}); }); it('should allow for a custom rate limit', async () => { const rateLimits = new RateLimitConfig({light: {maxConcurrent:1, minTime:30}}); const searchResults = await discovery.nupnpSearch(); const localApi = v3.api.createLocal(searchResults[0].ipaddress, undefined, rateLimits); const fastHue = await localApi.connect(testValues.username); const requestCount = 5; const normalHueSpeed = await multipleRequests(hue, requestCount); const fastHueSpeed = await multipleRequests(fastHue, requestCount); // The faster limits should confirm that the faster api is in fact that (this is an arbitary amount)... expect(fastHueSpeed).to.be.lessThan(normalHueSpeed - 100); }); }); describe('#getLightAttributesAndState()', () => { describe('using id', () => { it(`should return a light state for id=${EXTENDED_COLOR_LIGHT_ID}`, async () => { const result = await hue.lights.getLightAttributesAndState(EXTENDED_COLOR_LIGHT_ID); expect(result).to.have.property('id', EXTENDED_COLOR_LIGHT_ID); expect(result).to.have.property('state'); expect(result.state).to.have.property('on'); expect(result.state).to.have.property('bri'); expect(result.state).to.have.property('hue'); expect(result.state).to.have.property('sat'); expect(result.state).to.have.property('effect'); expect(result.state).to.have.property('xy'); expect(result.state).to.have.property('alert'); expect(result.state).to.have.property('colormode'); expect(result.state).to.have.property('mode'); expect(result.state).to.have.property('reachable'); expect(result).to.have.property('swupdate'); expect(result.swupdate).to.have.property('state'); expect(result.swupdate).to.have.property('lastinstall'); expect(result).to.have.property('type'); expect(result).to.have.property('name'); expect(result).to.have.property('modelid'); expect(result).to.have.property('manufacturername'); expect(result).to.have.property('productname'); expect(result).to.have.property('capabilities'); expect(result.capabilities).to.have.property('certified'); expect(result.capabilities).to.have.property('control'); expect(result.capabilities).to.have.property('streaming'); expect(result).to.have.property('config'); expect(result.config).to.have.property('archetype'); expect(result.config).to.have.property('function'); expect(result.config).to.have.property('direction'); expect(result).to.have.property('uniqueid'); expect(result).to.have.property('swversion'); }); }); describe('using Light instance', () => { it('should return a light state', async () => { const light = await hue.lights.getLight(EXTENDED_COLOR_LIGHT_ID) , result = await hue.lights.getLightAttributesAndState(light) ; expect(result).to.have.property('id').to.equal(EXTENDED_COLOR_LIGHT_ID); expect(result).to.have.property('state'); expect(result.state).to.have.property('on'); expect(result.state).to.have.property('bri'); expect(result.state).to.have.property('hue'); expect(result.state).to.have.property('sat'); expect(result.state).to.have.property('effect'); expect(result.state).to.have.property('xy'); expect(result.state).to.have.property('alert'); expect(result.state).to.have.property('colormode'); expect(result.state).to.have.property('mode'); expect(result.state).to.have.property('reachable'); expect(result).to.have.property('swupdate'); expect(result.swupdate).to.have.property('state'); expect(result.swupdate).to.have.property('lastinstall'); expect(result).to.have.property('type'); expect(result).to.have.property('name'); expect(result).to.have.property('modelid'); expect(result).to.have.property('manufacturername'); expect(result).to.have.property('productname'); expect(result).to.have.property('capabilities'); expect(result.capabilities).to.have.property('certified'); expect(result.capabilities).to.have.property('control'); expect(result.capabilities).to.have.property('streaming'); expect(result).to.have.property('config'); expect(result.config).to.have.property('archetype'); expect(result.config).to.have.property('function'); expect(result.config).to.have.property('direction'); expect(result).to.have.property('uniqueid'); expect(result).to.have.property('swversion'); }); }); }); describe('#getLightState()', () => { it(`should return a light state for id=${EXTENDED_COLOR_LIGHT_ID}`, async () => { const result = await hue.lights.getLightState(EXTENDED_COLOR_LIGHT_ID); expect(result).to.have.property('on'); expect(result).to.have.property('bri'); expect(result).to.have.property('hue'); expect(result).to.have.property('sat'); expect(result).to.have.property('effect'); expect(result).to.have.property('xy'); expect(result).to.have.property('alert'); expect(result).to.have.property('colormode'); expect(result).to.have.property('mode'); expect(result).to.have.property('reachable'); }); it('should return a light state for Light object', async () => { const light = await hue.lights.getLight(EXTENDED_COLOR_LIGHT_ID) , result = await hue.lights.getLightState(light) ; expect(result).to.have.property('on'); expect(result).to.have.property('bri'); expect(result).to.have.property('hue'); expect(result).to.have.property('sat'); expect(result).to.have.property('effect'); expect(result).to.have.property('xy'); expect(result).to.have.property('alert'); expect(result).to.have.property('colormode'); expect(result).to.have.property('mode'); expect(result).to.have.property('reachable'); }); }); describe('rename / renameLight', () => { let light , originalName ; beforeEach(async () => { light = await hue.lights.getLight(2); originalName = light.name; }); afterEach('reset light name in bridge', async () => { if (originalName) { light.name = originalName; await hue.lights.renameLight(light); } }); // describe('#rename (deprecated function)', () => { // it('should rename a light using id as integer', async () => { // const newName = 'Lounge Living Color' // , result = await hue.lights.rename(light.id, newName) // , updatedLight = await hue.lights.getLight(light); // // expect(result).to.be.true; // // expect(updatedLight).to.have.property('id').to.equal(light.id); // expect(updatedLight).to.have.property('name').to.equal(newName); // }); // // it('should rename a light using id as a Light instance', async () => { // const newName = 'Lounge Living Color' // , result = await hue.lights.rename(light, newName) // , updateLight = await hue.lights.getLight(light); // // expect(result).to.be.true; // // expect(updateLight).to.have.property('id').to.equal(light.id); // expect(updateLight).to.have.property('name').to.equal(newName); // }); // // it('should error is name is too long', async () => { // const newName = `Renamed Light ${Date.now()} ${Date.now()}`; // // try { // await hue.lights.rename(light.id, newName); // expect.fail('Should have failed to rename light'); // } catch (err) { // expect(err.message).to.contain('does not meet maximum length requirement'); // } // }); // }); describe('#renameLight()', () => { it('should rename a light using id as integer', async () => { const newName = 'Lounge Living Color'; light.name = newName; const result = await hue.lights.renameLight(light) , updatedLight = await hue.lights.getLight(light) ; expect(result).to.be.true; expect(updatedLight).to.have.property('id').to.equal(light.id); expect(updatedLight).to.have.property('name').to.equal(newName); }); // TODO this was copied from the older rename deprecated function, but the light should be validating this // it('should error is name is too long', async () => { // const newName = `Renamed Light ${Date.now()} ${Date.now()}`; // // try { // await hue.lights.rename(light.id, newName); // expect.fail('Should have failed to rename light'); // } catch (err) { // expect(err.message).to.contain('does not meet maximum length requirement'); // } // }); }); }); describe('#setLightState()', () => { describe('using Light instance', () => { it('should set an xy value', async () => { const id = EXTENDED_COLOR_LIGHT_ID , light = await hue.lights.getLight(id) , result = await hue.lights.setLightState(light, {on: true, xy: [0.1948, 0.5478]}) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('xy'); expect(finalLightState.xy[0]).to.be.within(0.194, 0.195); expect(finalLightState.xy[1]).to.be.closeTo(0.547, 0.548); }); }); describe('using light id', () => { describe('using raw objects', () => { it('should set an xy value', async () => { const id = testValues.testLightId , result = await hue.lights.setLightState(id, {on: true, xy: [0.1948, 0.5478]}) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('xy'); expect(finalLightState.xy[0]).to.be.within(0.194, 0.195); expect(finalLightState.xy[1]).to.be.closeTo(0.547, 0.548); }); it('should set an xy and bri value', async () => { const id = testValues.testLightId , state = { on: true, bri: 254, colormode: 'xy', xy: [0.153, 0.048] } , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('xy'); expect(finalLightState.xy[0]).to.be.within(0.15, 0.155); expect(finalLightState.xy[1]).to.be.closeTo(0.045, 0.05); }); }); describe('using lightState object', () => { it('should set alert to "lselect"', async () => { const id = testValues.testLightId , state = new LightState().alert('lselect') , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('alert').to.equal('lselect'); }); describe('#on', () => { function testOn(on) { return async () => { const id = testValues.testLightId , state = new LightState().on(on) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.equal(on); }; } it('should set on to true', testOn(true)); it('should set on to false', testOn(false)); }); describe('#bri', () => { function testBri(briVal) { return async () => { const id = testValues.testLightId , state = new LightState().on().bri(briVal) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('bri').to.equal(briVal); }; } it('should set bri to 1', testBri(1)); it('should set bri to 254', testBri(254)); it('should set bri to 100', testBri(100)); }); describe('#hue', () => { function testHue(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().hue(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('hue').to.equal(val); }; } it('should set hue to 1', testHue(1)); it('should set hue to 254', testHue(254)); it('should set hue to 100', testHue(100)); }); describe('#sat', () => { function testSat(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().sat(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('sat').to.equal(val); }; } it('should set sat to 1', testSat(1)); it('should set sat to 254', testSat(254)); it('should set sat to 100', testSat(100)); }); describe('#xy', () => { function testXY(xVal, yVal) { return async () => { const id = testValues.testLightId , state = new LightState().on().xy(xVal, yVal) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('xy').to.contain(xVal, yVal); }; } it('should set xy to 1,1', testXY(1, 1)); it('should set xy to 0,1', testXY(0, 1)); it('should set xy to 0,0', testXY(0, 0)); it('should set xy to 1,0', testXY(1, 0)); it('should set xy to 0.5,0.5', testXY(0.5, 0.5)); it('should set xy to 0.178,0.99', testXY(0.178, 0.99)); }); describe('#ct', () => { function testCt(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().ct(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('ct').to.equal(val); }; } it('should set ct to 153', testCt(153)); it('should set ct to 500', testCt(500)); it('should set ct to 200', testCt(200)); it('should set ct to 499', testCt(499)); //TODO do failure conditions }); describe('#alert', () => { function testAlert(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().alert(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('alert').to.equal(val); }; } it('should set alert to none', testAlert('none')); it('should set alert to select', testAlert('select')); it('should set alert to lselect', testAlert('lselect')); }); describe('#effect', () => { function testEffect(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().effect(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('effect').to.equal(val); }; } it('should set alert to none', testEffect('none')); it('should set alert to colorloop', testEffect('colorloop')); }); describe('#transitiontime', () => { function testTransitiontime(val) { return async () => { const id = testValues.testLightId , state = new LightState().on().transitiontime(val) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; // It is not possible to query the transition time value azs it is no longer returned in the state values // from the Hue API. }; } afterEach(async () => { // Turn off the light so that the next test call will do something. const id = testValues.testLightId; await hue.lights.setLightState(id, new LightState().off()); }); it('should set to 0', testTransitiontime(0)); it('should set to 4', testTransitiontime(4)); it('should set to 10', testTransitiontime(10)); it('should set to 1000', testTransitiontime(1000)); }); //TODO inc seem to be more difficult to test describe('#bri_inc', () => { function testBriInc(initialBri, incVal, expectedBri) { return async () => { const id = testValues.testLightId , initialState = new LightState().on().bri(initialBri) , incState = new LightState().transitiontime(0).bri_inc(incVal) ; const initialResult = await hue.lights.setLightState(id, initialState); expect(initialResult).to.be.true; const result = await hue.lights.setLightState(id, incState); expect(result).to.be.true; const finalLightState = await hue.lights.getLightState(id); expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('bri').to.equal(expectedBri); }; } it('should respond to +1', testBriInc(1, 1, 2)); it('should respond to +200', testBriInc(1, 200, 201)); }); }); describe('#rgb', () => { function testRGB(red, green, blue, xy) { return async () => { const id = testValues.testLightId , state = new LightState().on().rgb(red, green, blue) , result = await hue.lights.setLightState(id, state) , finalLightState = await hue.lights.getLightState(id) ; expect(result).to.be.true; expect(finalLightState).to.have.property('on').to.be.true; expect(finalLightState).to.have.property('colormode').to.equal('xy'); expect(finalLightState).to.have.property('xy'); expect(finalLightState.xy[0]).to.be.closeTo(xy[0], 0.001); expect(finalLightState.xy[1]).to.be.closeTo(xy[1], 0.001); }; } it('should set rgb to red', testRGB(255, 0, 0, [0.6484, 0.3309])); it('should set rgb to green', testRGB(0, 255, 0, [0.3157, 0.5906])); it('should set rgb to blue', testRGB(0, 0, 255, [0.153, 0.048])); }); //TODO complete all the property tests for a light state }); }); });