node-hue-api
Version:
Philips Hue API Library for Node.js
368 lines (287 loc) • 10.6 kB
JavaScript
;
const expect = require('chai').expect
, v3Api = require('../v3').v3.api
, discovery = require('../index').discovery
, model = require('@peter-murray/hue-bridge-model').model
, LightState = model.lightStates.LightState
, GroupState = model.lightStates.GroupLightState
, conditionOperators = model.ruleConditionOperators
, CLIPOpenClose = model.CLIPOpenClose
, Rule = model.Rule
, testValues = require('../../test/support/testValues.js')
;
describe('Hue API #rules', function() {
let hue
, testSensor
, targetLight
, targetGroup
;
this.timeout(10000);
before(() => {
return discovery.nupnpSearch()
.then(searchResults => {
const localApi = v3Api.createLocal(searchResults[0].ipaddress);
return localApi.connect(testValues.username)
.then(api => {
hue = api;
})
.then(() => {
const sensor = new CLIPOpenClose();
sensor.name = 'Test Open/Close Sensor';
sensor.modelid = 'software';
sensor.swversion = '1.0';
sensor.uniqueid = '00:00:00:01';
sensor.manufacturername = 'software';
sensor.config = {
url: 'http://developers.meethue.com'
};
return hue.sensors.createSensor(sensor);
})
.then(sensor => {
testSensor = sensor;
return Promise.all([
hue.lights.getAll(),
hue.groups.getAll()
]);
})
.then(results => {
targetLight = results[0][1];
targetGroup = results[1][1];
});
});
});
after(async () => {
if (hue && testSensor) {
try {
await hue.sensors.deleteSensor(testSensor.id);
} catch (err) {
console.error(`Failed to remove CLIP Sensor, ${testSensor.toString()}, error: ${err}`);
}
}
});
describe('#getAll()', () => {
it('should find some', async () => {
const results = await hue.rules.getAll();
expect(results).to.be.instanceOf(Array);
expect(results).to.have.length.greaterThan(0);
const rule = results[0];
expect(rule instanceof Rule).to.be.true;
expect(rule).to.have.property('name');
expect(rule).to.have.property('owner');
//TODO more fields
});
});
describe('#getRule()', () => {
describe('using id value', () => {
it('should get an existing rule', async () => {
const allRules = await hue.rules.getAll()
, target = allRules[0]
;
const rule = await hue.rules.getRule(target.id);
expect(rule).to.have.property('id').to.equal(target.id);
});
});
describe('using Rule object', () => {
it('should get an existing rule', async () => {
const allRules = await hue.rules.getAll()
, target = allRules[1]
;
const rule = await hue.rules.getRule(target);
expect(rule).to.have.property('id').to.equal(target.id);
});
});
});
describe('#createRule()', () => {
let ruleId = null;
afterEach(async () => {
if (ruleId) {
try {
await hue.rules.deleteRule(ruleId);
} catch (err) {
console.error(`Failed to delete rule: ${ruleId}`);
}
}
});
it('should create a new rule', async () => {
const rule = new Rule();
rule.name = 'Simple Test Rule';
rule.recycle = true;
rule.addCondition(model.ruleConditions.sensor(testSensor).when('open').changed());
rule.addAction(model.actions.light(targetLight).withState(new LightState().on()));
const result = await hue.rules.createRule(rule);
expect(result).to.have.property('id');
ruleId = result.id;
});
});
describe('#deleteRule()', () => {
let rule = null;
beforeEach(async () => {
const newRule = new Rule();
newRule.name = 'Test Rule to be deleted';
newRule.recycle = true;
newRule.addCondition(model.ruleConditions.sensor(testSensor).when('open').equals(true));
newRule.addAction(model.actions.light(targetLight).withState(new LightState().on()));
rule = await hue.rules.createRule(newRule);
});
afterEach(async () => {
if (rule) {
let existing = null;
try {
existing = await hue.rules.getRule(rule);
} catch (err) {
// Not found is fine
expect(err.getHueErrorType()).to.equal(3);
}
if (existing) {
try {
await hue.rules.deleteRule(rule);
} catch (err) {
console.error(`Failed to delete rule: ${rule.id}`);
}
}
}
});
it('should delete a rule using id', async () => {
const result = await hue.rules.deleteRule(rule.id);
expect(result).to.have.be.true;
});
it('should delete a rule using Rule object', async () => {
const result = await hue.rules.deleteRule(rule);
expect(result).to.have.be.true;
});
it('should error if a rule is not found', async () => {
const allRules = await hue.rules.getAll();
const invalidRuleId = getNextRuleId(allRules);
try {
await hue.rules.deleteRule(invalidRuleId);
expect.fail('should not get here')
} catch (err) {
expect(err.getHueErrorType()).to.equal(3);
expect(err.message).to.contain('not available');
}
})
});
// describe('#getOrphanedRules()', () => {
//
// it('should get orphaned rules', async () => {
// const results = await hue.rules.getOrphanedRules();
// expect(results).to.be.instanceOf(Array);
// // console.log(JSON.stringify(results, null, 2));
// });
// });
//
//
// describe('#getDisabledRules()', () => {
//
// it('should get disabled rules', async () => {
// const results = await hue.rules.getDisabledRules();
// expect(results).to.be.instanceOf(Array);
// // console.log(JSON.stringify(results, null, 2));
// });
// });
describe('#updateRule()', () => {
let rule;
beforeEach(async () => {
const newRule = new Rule();
newRule.name = 'Test Rule to be deleted';
newRule.recycle = true;
newRule.addCondition(model.ruleConditions.sensor(testSensor).when('open').equals(true));
newRule.addAction(model.actions.light(targetLight).withState(new LightState().on()));
rule = await hue.rules.createRule(newRule);
});
afterEach(async () => {
if (rule) {
const existing = await hue.rules.getRule(rule);
if (existing) {
try {
await hue.rules.deleteRule(rule);
} catch (err) {
console.error(`Failed to delete rule: ${rule.id}`);
}
}
}
});
it('should update the name', async () => {
rule.name = `Updated - ${Date.now()}`;
const result = await hue.rules.updateRule(rule);
expect(result).to.have.property('name').to.be.true;
expect(result).to.have.property('actions').to.be.true;
expect(result).to.have.property('conditions').to.be.true;
});
it('should update conditions', async () => {
rule.resetConditions();
rule.addCondition(model.ruleConditions.sensor(testSensor).when('open').equals(false));
const result = await hue.rules.updateRule(rule);
expect(result).to.have.property('name').to.be.true;
expect(result).to.have.property('actions').to.be.true;
expect(result).to.have.property('conditions').to.be.true;
const updatedRule = await hue.rules.getRule(rule);
expect(updatedRule.conditions).to.have.length(1);
const condition = updatedRule.conditions[0];
expect(condition).to.have.property('operator').to.equal(conditionOperators.equals);
expect(condition).to.have.property('value').to.equal('false');
});
describe('adding actions', () => {
it('should add a LightStateAction', async () => {
rule.resetActions();
rule.addAction(model.actions.light(targetLight).withState(new LightState().on(false)));
const result = await hue.rules.updateRule(rule);
expect(result).to.have.property('name').to.be.true;
expect(result).to.have.property('actions').to.be.true;
expect(result).to.have.property('conditions').to.be.true;
const updatedRule = await hue.rules.getRule(rule);
expect(updatedRule.actions).to.have.length(1);
const action = updatedRule.actions[0];
expect(action).to.have.property('address').to.equal(`/lights/${targetLight.id}/state`);
expect(action).to.have.property('method').to.equal('PUT');
expect(action).to.have.property('body').to.deep.equals({on: false});
});
it('should add a GroupStateAction', async () => {
rule.resetActions();
rule.addAction(model.actions.group(targetGroup).withState(new GroupState().on(false)));
const result = await hue.rules.updateRule(rule);
expect(result).to.have.property('name').to.be.true;
expect(result).to.have.property('actions').to.be.true;
expect(result).to.have.property('conditions').to.be.true;
const updatedRule = await hue.rules.getRule(rule);
expect(updatedRule.actions).to.have.length(1);
const action = updatedRule.actions[0];
expect(action).to.have.property('address').to.equal(`/groups/${targetGroup.id}/action`);
expect(action).to.have.property('method').to.equal('PUT');
expect(action).to.have.property('body').to.deep.equals({on: false});
});
//TODO expand to cover all other actions
});
it('should produce error if no conditions set', async () => {
try {
rule.resetConditions();
await hue.rules.updateRule(rule);
expect.fail('Should have produced an error');
} catch (err) {
expect(err).to.have.property('message').to.contain('at least one condition');
}
});
it('should produce error if no actions set', async () => {
try {
rule.resetActions();
await hue.rules.updateRule(rule);
expect.fail('Should have produced an error');
} catch (err) {
expect(err).to.have.property('message').to.contain('at least one action');
}
});
});
});
function getNextRuleId(allRules) {
const ids = allRules.map(rule => rule.id);
let id = 1
, nextId = null
;
while (!nextId) {
id++;
if (ids.indexOf(`${id}`) === -1) {
nextId = id;
}
}
return nextId;
}