From 9e870c2c7731a81870213361685af04d3b669555 Mon Sep 17 00:00:00 2001 From: Russell Goldin Date: Sat, 2 Jun 2018 01:28:24 -0700 Subject: [PATCH] 5.1.0 --- .snyk | 8 +- README.md | 12 + package.json | 2 +- specs/assets/config/config.json | 5 +- .../config/templates/config_intellibrite.json | 13 + .../inbound/controller_packets/2.spec.js | 51 +- .../packetBufferToDecode-chlorinator.spec.js | 2 + specs/lib/comms/server-api-misc.spec.js | 2 +- specs/lib/equipment/circuit.spec.js | 44 +- specs/lib/equipment/intellibright.spec.js | 545 ++++++ src/etc/constants.js | 35 +- src/etc/settings.js | 1 + src/integrations/outputToSmartThings.js | 41 +- src/lib/comms/inbound/controller/2.js | 2 +- src/lib/comms/inbound/controller/39.js | 2 +- src/lib/comms/inbound/controller/96.js | 5 +- src/lib/comms/inbound/packet-buffer.js | 59 +- src/lib/comms/influx-connector.js | 4 +- src/lib/comms/outbound/queue-packet.js | 4 +- src/lib/comms/server.js | 40 +- src/lib/comms/socketio-helper.js | 42 + src/lib/comms/sp-helper.js | 8 +- src/lib/equipment/circuit.js | 574 +++++- src/lib/equipment/time.js | 2 +- src/www/bootstrap/configClient.json | 5 +- src/www/bootstrap/index.html | 1739 +++++++++-------- src/www/bootstrap/main.js | 357 ++++ sysDefault.json | 3 +- 28 files changed, 2646 insertions(+), 961 deletions(-) create mode 100644 specs/assets/config/templates/config_intellibrite.json create mode 100644 specs/lib/equipment/intellibright.spec.js diff --git a/.snyk b/.snyk index 0df5090b..371800ba 100644 --- a/.snyk +++ b/.snyk @@ -118,14 +118,11 @@ ignore: 'npm:bootstrap:20160627': - bootstrap: reason: None given - expires: '2018-05-27T15:05:54.490Z' + expires: '2018-06-27T21:13:28.800Z' 'npm:lodash:20180130': - snyk-go-plugin > graphlib > lodash: reason: None given expires: '2018-05-27T15:05:54.491Z' - node-ip > node-localip > wmic > async > lodash: - reason: None given - expires: '2018-06-03T00:32:11.116Z' - node-ssdp > async > lodash: reason: None given expires: '2018-04-01T03:18:10.568Z' @@ -141,6 +138,9 @@ ignore: - request-promise > request-promise-core > lodash: reason: None given expires: '2018-06-03T00:31:50.876Z' + - node-ip > node-localip > wmic > async > lodash: + reason: None given + expires: '2018-06-03T00:32:11.116Z' 'npm:hoek:20180212': - request > hawk > hoek: reason: None given diff --git a/README.md b/README.md index e5c824cf..5b5097c2 100755 --- a/README.md +++ b/README.md @@ -9,6 +9,18 @@ 1. Fixed bad characters in custom names +#### 5.1.0 Highlights +1. Intellibrite support - API's, Sockets and a WebUI +Will document more later... but... +/light/mode/:mode +/light/circuit/:circuit/setColor/:color +/light/circuit/:circuit/setSwimDelay/:delay +/light/circuit/:circuit/setPosition/:position + +See the constants.js file and the sections: + strIntellibriteModes (for modes) + lightColors (for setColor) + #### 5.0.0 Highlights Make sure to run `npm upgrade`. There are many package updates and changes. diff --git a/package.json b/package.json index b4613b6b..dea96363 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodejs-poolcontroller", - "version": "5.0.1", + "version": "5.1.0", "description": "NodeJS program to read and write to the the pool equipment serial bus.", "main": "src/index.js", "scripts": { diff --git a/specs/assets/config/config.json b/specs/assets/config/config.json index 7db77a02..c27683d6 100644 --- a/specs/assets/config/config.json +++ b/specs/assets/config/config.json @@ -10,7 +10,7 @@ } } }, - "version": "5.0.0", + "version": "5.1.0", "equipment": { "controller": { "intellicom": { @@ -132,6 +132,7 @@ "logPumpTimers": 0, "logReload": 0, "logApi": 0, + "logIntellibrite": 0, "fileLog": { "enable": 0, "fileLogLevel": "silly", @@ -148,4 +149,4 @@ } }, "integrations": {} -} \ No newline at end of file +} diff --git a/specs/assets/config/templates/config_intellibrite.json b/specs/assets/config/templates/config_intellibrite.json new file mode 100644 index 00000000..f8112403 --- /dev/null +++ b/specs/assets/config/templates/config_intellibrite.json @@ -0,0 +1,13 @@ +{ + "poolController": { + + "log": { + "logLevel": "silly", + "logIntellibrite": 1, + "logMessageDecoding": 1, + "logPacketWrites": 1, + "logAPI": 1 + }, + "appAddress": 34 + } +} diff --git a/specs/lib/comms/inbound/controller_packets/2.spec.js b/specs/lib/comms/inbound/controller_packets/2.spec.js index 6f5dda64..92b8ae50 100644 --- a/specs/lib/comms/inbound/controller_packets/2.spec.js +++ b/specs/lib/comms/inbound/controller_packets/2.spec.js @@ -1,65 +1,72 @@ -describe('processes 2 (Status) packets', function() { +describe('processes 2 (Status) packets', function () { var data = [ - Buffer.from([255, 0, 255, 165,33,15,16,2,29,12,41,32,0,0,0,0,0,0,0,3,0,64,4,60,60,0,0,62,71,0,0,0,0,0,74,142,0,13,3,130]) + Buffer.from([255, 0, 255, 165, 33, 15, 16, 2, 29, 12, 41, 32, 0, 0, 0, 0, 0, 0, 0, 3, 0, 64, 4, 60, 60, 0, 0, 62, 71, 0, 0, 0, 0, 0, 74, 142, 0, 13, 3, 130]) ] var equip = 'controller' - describe('#When packets arrive', function() { - context('via serialport or Socat', function() { + describe('#When packets arrive', function () { + context('via serialport or Socat', function () { - before(function() { + before(function () { return global.initAllAsync() }); - beforeEach(function() { - // sandbox = sinon.sandbox.create() - //clock = sandbox.useFakeTimers() - // queuePacketStub = sandbox.stub(bottle.container.queuePacket, 'queuePacket') + beforeEach(function () { loggers = setupLoggerStubOrSpy('stub', 'spy') - // writeNetPacketStub = sandbox.stub(bottle.container.sp, 'writeNET') - // writeSPPacketStub = sandbox.stub(bottle.container.sp, 'writeSP') - bottle.container.circuit.init() }) - afterEach(function() { + afterEach(function () { sandbox.restore() }) - after(function() { + after(function () { return global.stopAllAsync() }) - it('#Processes a controller status packet', function(done) { + it('#Processes a controller status packet', function (done) { Promise.resolve() - .then(function(){ + .then(function () { return bottle.container.packetBuffer.push(data[0]) }) .delay(50) .then( - function(){ + function () { bottle.container.temperatures.getTemperatures().temperature.airTemp.should.equal(62) bottle.container.time.getTime().time.controllerTime.should.equal('12:41 PM') }) .then(done, done) }) - it('#Processes a Duplicate Broadcast controller status packet', function(done) { + it('#Processes a Duplicate Broadcast controller status packet', function (done) { Promise.resolve() - .then(function(){ + .then(function () { return bottle.container.packetBuffer.push(data[0]) }) - .then(function(){ + .then(function () { return bottle.container.packetBuffer.push(data[0]) }) .delay(50) .then( - function(){ + function () { bottle.container.temperatures.getTemperatures().temperature.airTemp.should.equal(62) bottle.container.time.getTime().time.controllerTime.should.equal('12:41 PM') - loggers.loggerVerboseStub.args[1][0].should.contain('Duplicate broadcast.') + var text = 'not found' + // iterate through debug statements to see if we find 'duplicate broadcast + loggers.loggerDebugStub.args.forEach(function (i) { + i.forEach(function (j) { + if (typeof j === 'string') { + if (j.includes('Duplicate broadcast')) { + text = 'found' + } + } + }) + }) + text.should.eq('found') + // loggers.loggerDebugStub.args[4][0].should.contain('Duplicate broadcast.') + }) .then(done, done) }) diff --git a/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js b/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js index a2dbd00f..167b73c2 100644 --- a/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js +++ b/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js @@ -1,3 +1,4 @@ +/* describe('chlorinator packets: receives packets from buffer and follows them to decoding', function () { @@ -260,3 +261,4 @@ describe('chlorinator packets: receives packets from buffer and follows them to }) }) +*/ diff --git a/specs/lib/comms/server-api-misc.spec.js b/specs/lib/comms/server-api-misc.spec.js index c89199b4..02b1800d 100755 --- a/specs/lib/comms/server-api-misc.spec.js +++ b/specs/lib/comms/server-api-misc.spec.js @@ -15,7 +15,7 @@ describe('server', function() { //clock = sandbox.useFakeTimers() writeSPPacketStub = sandbox.stub(bottle.container.sp, 'writeSP')//.callsFake(function(){bottle.container.writePacket.postWritePacketHelper()}) sandbox.stub(bottle.container.intellitouch, 'getPreambleByte').returns(33) - queuePacketStub = sandbox.stub(bottle.container.queuePacket, 'queuePacket') + //queuePacketStub = sandbox.stub(bottle.container.queuePacket, 'queuePacket') }) diff --git a/specs/lib/equipment/circuit.spec.js b/specs/lib/equipment/circuit.spec.js index 30e4e963..3d4d930d 100644 --- a/specs/lib/equipment/circuit.spec.js +++ b/specs/lib/equipment/circuit.spec.js @@ -1,35 +1,47 @@ -// var myModule = rewire(path.join(process.cwd(), '/src/lib/equipment/circuit.js')) -// +// /* +// +// 09:55:04.008 DEBUG Msg# 12 Incoming controller packet: 165,33,15,16,10,12,2,87,116,114,70,97,108,108,32,50,0,251,5,6 +// 09:55:04.192 DEBUG Msg# 13 Incoming controller packet: 165,33,15,16,10,12,3,87,116,114,70,97,108,108,32,51,0,251,5,8 +// 09:55:05.055 DEBUG Msg# 16 Incoming controller packet: 165,33,15,16,10,12,5,85,83,69,82,78,65,77,69,45,48,54,3,243 +// 09:55:05.201 DEBUG Msg# 17 Incoming controller packet: 165,33,15,16,10,12,6,85,83,69,82,78,65,77,69,45, +// 09:55:05.374 DEBUG Msg# 18 Incoming controller packet: 165,33,15,16,10,12,7,85,83,69,82,78,65,77,69,45 +// 09:55:05.550 DEBUG Msg# 19 Incoming controller packet: 165,33,15,16,10,12,8,85,83,69,82,78,65,77,69,45,48,57,3,249 +// 09:55:05.728 DEBUG Msg# 20 Incoming controller packet: 165,33,15,16,10,12,9,85,83,69,82,78,65,77,69,45 +// 09:55:05.749 INFO +// Custom Circuit Names retrieved from configuration: +// ["WtrFall 1","WtrFall 1.5","WtrFall 2","WtrFall 3","Pool Low2","USERNAME-06","USERNAME-07","USERNAME-08","USERNAME-09","USERNAME-10"] +// */ // // describe('circuit controller', function() { // // describe('#sets the friendlyNames', function() { // -// before(function() { -// global.initAllAsync() -// }); +// var equip = 'controller' +// before(function() { +// return global.initAllAsync() // -// beforeEach(function() { -// sandbox = sinon.sandbox.create() -// clock = sandbox.useFakeTimers() -// loggerInfoStub = sandbox.stub(bottle.container.logger, 'info') -// loggerWarnStub = sandbox.spy(bottle.container.logger, 'warn') -// loggerVerboseStub = sandbox.stub(bottle.container.logger, 'verbose') -// loggerDebugStub = sandbox.stub(bottle.container.logger, 'debug') -// loggerSillyStub = sandbox.stub(bottle.container.logger, 'silly') +// }); +// +// beforeEach(function() { +// loggers = setupLoggerStubOrSpy('stub', 'stub') +// clock = sandbox.useFakeTimers() +// +// updateAvailStub = sandbox.stub(bottle.container.updateAvailable, 'getResultsAsync').returns(Promise.resolve({})) // }) // // afterEach(function() { +// //restore the sandbox after each function // sandbox.restore() +// +// // }) // // after(function() { -// global.stopAllAsync() +// return global.stopAllAsync() // }) // // it('sets the names for circuits other than pool and spa', function() { -// var queuePacketStub = sinon.stub() -// var loggerInfoStub = sinon.stub() +// // var fnArr = JSON.parse(fs.readFileSync(path.join(process.cwd(), '/specs/assets/config', 'configFriendlyNames.json'), 'utf8')) // //var _response = {} // myModule.__with__({ diff --git a/specs/lib/equipment/intellibright.spec.js b/specs/lib/equipment/intellibright.spec.js new file mode 100644 index 00000000..f3a44c7f --- /dev/null +++ b/specs/lib/equipment/intellibright.spec.js @@ -0,0 +1,545 @@ +describe('processes Intellibrite packets', function () { + + var circuitPackets = [ + Buffer.from([255, 0, 255, 165, 33, 15, 16, 11, 5, 1, 1, 72, 0, 0, 1, 63]), + Buffer.from([255, 0, 255, 165, 33, 15, 16, 11, 5, 2, 0, 46, 0, 0, 1, 37]), + Buffer.from([255, 0, 255, 165, 33, 15, 16, 11, 5, 7, 16, 74, 0, 0, 1, 86]), + Buffer.from([255, 0, 255, 165, 33, 15, 16, 11, 5, 8, 16, 63, 0, 0, 1, 76]), + Buffer.from([255, 0, 255, 165, 33, 15, 16, 11, 5, 16, 16, 41, 0, 0, 1, 62]) + ] + var intellibriteAssignment = [255, 0, 255, 165, 33, 15, 16, 39, 32, 7, 8, 4, 0, 8, 20, 10, 0, 16, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 159] //light group and colorSet assignments + var intellibriteCyan = [255, 0, 255, 165, 33, 16, 34, 167, 32, 7, 12, 4, 0, 8, 22, 10, 0, 16, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 56] // change pool light to cyan and spa light to magenta + + + var intellibritePosition1 = [255, 0, 255, 165, 33, 16, 34, 167, 32, 7, 12, 4, 0, 8, 22, 10, 0, 16, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 24] // change pool light to cyan and spa light to magenta + + + var intellibriteSwimDelay = [255, 0, 255, 165, 33, 16, 34, 167, 32, 7, 12, 4, 0, 8, 22, 14, 0, 16, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 60] // change pool light swim delay to 7 + var intellibriteSwimDelay10 = [255, 0, 255, 165, 33, 16, 34, 167, 32, 7, 12, 4, 0, 8, 22, 20, 0, 16, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 66] // change pool light swim delay to 10 + + var intellibriteOff = [255, 0, 255, 165, 33, 16, 34, 96, 2, 0, 0, 1, 90] // intellibrite lights to off + var intellibriteOn = [255, 0, 255, 165, 33, 16, 34, 96, 2, 1, 0, 1, 91] // intellibrite lights to on + var intellibriteColorSet = [255, 0, 255, 165, 33, 16, 34, 96, 2, 160, 0, 1, 250] // intellibrite lights to color set + var intellibriteCaribbean = [255, 0, 255, 165, 33, 16, 34, 96, 2, 179, 0, 2, 13] // intellibrite to caribbean + var intellibriteSave = [255, 0, 255, 165, 33, 16, 34, 96, 2, 190, 0, 2, 24] // intellibrite save + var intellibriteRecall = [255, 0, 255, 165, 33, 16, 34, 96, 2, 191, 0, 2, 25] // intellibrite recall + + + var equip = 'controller' + + describe('#When packets arrive', function () { + context('via serialport or Socat', function () { + + before(function () { + return global.initAllAsync('/specs/assets/config/templates/config_intellibrite.json') + }); + + beforeEach(function () { + // sandbox = sinon.sandbox.create() + loggers = setupLoggerStubOrSpy('stub', 'spy') + checkIfNeedControllerConfigurationStub = sandbox.stub(bottle.container.intellitouch, 'checkIfNeedControllerConfiguration') + + }) + + afterEach(function () { + sandbox.restore() + + }) + + after(function () { + // return global.stopAllAsync() + }) + + it('#Loads circuit information', function () { + return Promise.resolve() + .then(function () { + for (var i in circuitPackets) { + bottle.container.packetBuffer.push(circuitPackets[i]) + } + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCurrentCircuits().circuit + // console.log('current Circuits:', JSON.stringify(json)) + json[1].number.should.equal(1) + json[1].name.should.equal("SPA") + }) + }) + + it('#Light positions and colors should be discovered', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteAssignment)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getLightGroup() + // console.log('all lights:', JSON.stringify(json)) + json[7].position.should.eq(1) + json[7].colorSet.should.eq(8) + json[8].colorSwimDelay.should.eq(5) + }) + .catch(function (err) { + return Promise.reject(new Error('Light positions: ' + err)) + }) + + }) + + it('#Changes pool light colorSet to Cyan', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteCyan)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getLightGroup() + // console.log('all lights:', JSON.stringify(json)) + json[8].colorSet.should.eq(6) + json[8].colorSetStr.should.eq('Cyan') + }) + .catch(function (err) { + return Promise.reject(new Error('Cyan: ' + err)) + }) + + }) + + it('#Changes garden lights position to 1', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibritePosition1)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getLightGroup() + // console.log('all lights:', JSON.stringify(json)) + json[16].position.should.eq(1) + }) + .then(function () { + //reset it to position 3 + bottle.container.packetBuffer.push(new Buffer(intellibriteCyan)) + + }) + // .catch(function (err) { + // return Promise.reject(new Error('Position: ' + err)) + // }) + + }) + + it('#Changes pool swim delay to 7', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteSwimDelay)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getLightGroup() + // console.log('all lights:', JSON.stringify(json)) + json[8].colorSwimDelay.should.eq(7) + }) + .catch(function (err) { + return Promise.reject(new Error('Swim Delay: ' + err)) + }) + + }) + + + it('#Changes Intellibrite to Color Set', function () { + + /* var _circuit = { + "number": 8, + "numberStr": "circuit8", + "name": "POOL LIGHT", + "circuitFunction": "Intellibrite", + "freeze": 0, + "macro": 0, + "light": { + "position": 2, + "colorStr": "Custom", + "color": -1, + "colorSet": 6, + "colorSetStr": "Cyan", + "prevColor": "Cyan", + "colorSwimDelay": 5, + "mode": -1, + "modeStr": "Custom" + } + }*/ + + + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteColorSet)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(7) + // console.log('circuit 7:', JSON.stringify(json, null, 2)) + json.light.mode.should.eq(160) + json.light.modeStr.should.eq('Color Set') + json.light.colorStr.should.eq('Magenta') + json = bottle.container.circuit.getCircuit(8) + // console.log('circuit 8:', JSON.stringify(json, null, 2)) + json.light.mode.should.eq(160) + json.light.modeStr.should.eq('Color Set') + json.light.colorStr.should.eq('Cyan') + }) + .catch(function (err) { + return Promise.reject(new Error('Color Set: ' + err)) + }) + + }) + + + it('#Changes Intellibrite to Off', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteOff)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(8) + // console.log('circuit:', JSON.stringify(json)) + json.light.mode.should.eq(0) + json.light.modeStr.should.eq('Off') + json.light.color.should.eq(0) + json.light.prevColor.should.eq(6) + json.light.prevColorStr.should.eq('Cyan') + json.light.prevMode.should.eq(160) + }) + .catch(function (err) { + return Promise.reject(new Error('Off: ' + err)) + }) + + }) + + + it('#Changes Intellibrite to On', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteOn)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(8) + // console.log('circuit:', JSON.stringify(json)) + json.light.mode.should.eq(160) + json.light.modeStr.should.eq('Color Set') + json.light.colorStr.should.eq('Cyan') + json.light.prevMode.should.eq(0) + }) + .catch(function (err) { + return Promise.reject(new Error('On: ' + err)) + }) + + }) + + it('#Changes Intellibrite to Caribbean', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteCaribbean)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(8) + // console.log('circuit:', JSON.stringify(json)) + json.light.modeStr.should.eq('Caribbean') + json.light.mode.should.eq(179) + json.light.colorStr.should.eq('Caribbean') + json.light.color.should.eq(179) + }) + .catch(function (err) { + return Promise.reject(new Error('Caribbean: ' + err)) + }) + + }) + + it('#Changes Intellibrite to Save', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteSave)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(8) + // console.log('circuit:', JSON.stringify(json)) + json.light.mode.should.eq(190) + json.light.modeStr.should.eq('Save') + json.light.color.should.eq(190) + json.light.colorStr.should.eq('Save') + }) + .catch(function (err) { + return Promise.reject(new Error('Save: ' + err)) + }) + + }) + + it('#Changes Intellibrite to Recall', function () { + return Promise.resolve() + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteCyan)) + + }) + .delay(50) + .then(function () { + bottle.container.packetBuffer.push(new Buffer(intellibriteRecall)) + + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCircuit(8) + // console.log('circuit:', JSON.stringify(json)) + json.light.mode.should.eq(191) + json.light.modeStr.should.eq('Recall') + json.light.color.should.eq(191) + json.light.colorStr.should.eq('Recall') + + }) + .catch(function (err) { + return Promise.reject(new Error('Recall: ' + err)) + }) + + }) + + }) + }) + + describe('#circuit api calls', function () { + + context('with a URL', function () { + + // before(function() { + // return global.initAllAsync() + // .catch(function(err){ + // console.log('what is the error?', err) + // }) + // }) + + beforeEach(function () { + loggers = setupLoggerStubOrSpy('stub', 'spy') + //clock = sandbox.useFakeTimers() + writeSPPacketStub = sandbox.stub(bottle.container.sp, 'writeSP')//.callsFake(function(){bottle.container.writePacket.postWritePacketHelper()}) + sandbox.stub(bottle.container.intellitouch, 'getPreambleByte').returns(33) + //queuePacketStub = sandbox.stub(bottle.container.queuePacket, 'queuePacket') + checkIfNeedControllerConfigurationStub = sandbox.stub(bottle.container.intellitouch, 'checkIfNeedControllerConfiguration') + + }) + + afterEach(function () { + bottle.container.writePacket.init() + bottle.container.queuePacket.init() + sandbox.restore() + + }) + + after(function () { + + return global.stopAllAsync() + }) + + it('sets the color of pool circuit color via the api to cyan', function (done) { + global.requestPoolDataWithURLAsync('light/circuit/8/setColor/6') + + .delay(50) + .then(function (obj) { + writeSPPacketStub.args[0][0].should.deep.equal(intellibriteCyan) + }) + .then(done, done) + }); + + it('sets the Circuit 16 light position to 1 (from 3)', function (done) { + global.requestPoolDataWithURLAsync('light/circuit/16/setPosition/1') + .delay(50) + + .then(function (obj) { + writeSPPacketStub.args[0][0].should.deep.equal(intellibritePosition1) + return global.requestPoolDataWithURLAsync('light/circuit/16/setPosition/3') + }) + .delay(500) + .then(function (obj) { + queue = bottle.container.queuePacket.entireQueue() + queue[2].should.deep.equal(intellibriteCyan) + }) + .then(done, done) + }); + + it('sets the color of pool circuit delay via the api to 10 seconds', function (done) { + global.requestPoolDataWithURLAsync('light/circuit/8/setSwimDelay/10') + .delay(50) + + .then(function (obj) { + writeSPPacketStub.args[0][0].should.deep.equal(intellibriteSwimDelay10) + }) + .then(done, done) + }); + + it('sets the Intellibrite light mode to Off', function (done) { + global.requestPoolDataWithURLAsync('light/mode/0') + .delay(50) + + .then(function (obj) { + writeSPPacketStub.args[0][0].should.deep.equal(intellibriteOff) + }) + .then(done, done) + }); + + it('sets the Intellibrite light mode to Caribbean', function (done) { + global.requestPoolDataWithURLAsync('light/mode/179') + .delay(50) + + .then(function (obj) { + writeSPPacketStub.args[0][0].should.deep.equal(intellibriteCaribbean) + }) + .then(done, done) + }); + + + + + + }) + }) + describe('Socket.io tests', function () { + context('for Intellibrite', function () { + + before(function () { + + return global.initAllAsync('/specs/assets/config/templates/config_intellibrite.json') + .then(function(){ + bottle.container.writePacket.init() + bottle.container.queuePacket.init() + }) + + + }); + + beforeEach(function () { + // sandbox = sinon.sandbox.create() + loggers = setupLoggerStubOrSpy('stub', 'spy') + checkIfNeedControllerConfigurationStub = sandbox.stub(bottle.container.intellitouch, 'checkIfNeedControllerConfiguration') + writeSPPacketStub = sandbox.stub(bottle.container.sp, 'writeSP') + + }) + + afterEach(function () { + bottle.container.writePacket.init() + bottle.container.queuePacket.init() + sandbox.restore() + + }) + + after(function () { + return global.stopAllAsync() + }) + + it('#resets packet info', function () { + return Promise.resolve() + .then(function () { + for (var i in circuitPackets) { + bottle.container.packetBuffer.push(circuitPackets[i]) + } + }) + .delay(50) + .then(function () { + var json = bottle.container.circuit.getCurrentCircuits().circuit + // console.log('current Circuits:', JSON.stringify(json, null, 2)) + json[1].number.should.equal(1) + json[1].name.should.equal("SPA") + }) + .then(function () { + return bottle.container.packetBuffer.push(new Buffer(intellibriteAssignment)) + + }) + .delay(50) + .then(function () { + console.log('current Circuits:', JSON.stringify(json, null, 2)) + var json = bottle.container.circuit.getLightGroup() + // console.log('all lights:', JSON.stringify(json)) + json[7].position.should.eq(1) + json[7].colorSet.should.eq(8) + json[8].colorSwimDelay.should.eq(5) + }) + .catch(function (err) { + return Promise.reject(new Error('Light positions: ' + err)) + }) + }) + + it('#sets the color of the pool circuit via the socket to Cyan', function () { + var client = global.ioclient.connect(global.socketURL, global.socketOptions) + client.on('connect', function (data) { + + client.emit('setLightColor', 8, 6) + client.emit('setLightColor', 7, 12) + client.disconnect() + + + }) + return Promise.resolve() + .delay(100) + .then(function () { + queue = bottle.container.queuePacket.entireQueue() + queue[2].should.deep.equal(intellibriteCyan) + + }) + + + }); + + + it('#sets the position of the circuit 16 to 1 (and back to 3)', function () { + var client = global.ioclient.connect(global.socketURL, global.socketOptions) + client.on('connect', function (data) { + + client.emit('setLightPosition', 16, 1) + client.emit('setLightPosition', 16, 3) + + client.disconnect() + + + }) + return Promise.resolve() + .delay(100) + .then(function () { + queue = bottle.container.queuePacket.entireQueue() + queue[0].should.deep.equal(intellibritePosition1) + queue[2].should.deep.equal(intellibriteCyan) + }) + + + }); + + + it('#sets the swim delay of the pool circuit via the socket to 10', function () { + var client = global.ioclient.connect(global.socketURL, global.socketOptions) + client.on('connect', function (data) { + + client.emit('setLightSwimDelay', 8, 10) + client.disconnect() + + + }) + + return Promise.resolve() + .delay(100) + .then(function () { + // console.log('writeSP: ', writeSPPacketStub.args) + writeSPPacketStub.args[0][0].should.deep.equal(intellibriteSwimDelay10) + }) + + }) + + + }) + + + }) + +}) diff --git a/src/etc/constants.js b/src/etc/constants.js index e26b3d0f..48a69ccb 100755 --- a/src/etc/constants.js +++ b/src/etc/constants.js @@ -336,7 +336,7 @@ module.exports = function(container) { 9: 'SAM Light', 10: 'SAL Light', 11: 'Photon Gen', - 12: 'color wheel', + 12: 'Color Wheel', 13: 'Valves', 14: 'Spillway', 15: 'Floor Cleaner', @@ -400,7 +400,7 @@ module.exports = function(container) { // Set commands - 96: 'Set Color', //Intellibrite, maybe more? + 96: 'Set Color', 131: 'Set Delay Cancel', 133: 'Set Date/Time', 134: 'Set Circuit', @@ -451,18 +451,32 @@ module.exports = function(container) { 253: 'Get SW Version' } + var lightColors = { + 0: "White", + 1: "Custom", // custom addition for when save/recall are used + 2: "Light Green", + 4: "Green", + 6: "Cyan", + 8: "Blue", + 10: "Lavender", + 12: "Magenta", + 14: "Light Magenta" + } + var strIntellibriteModes = { - 0: 'Off', //All off in UI - 1: 'On', //All on in UI + 0: 'Off', + 1: 'On', 128: 'Color Sync', 144: 'Color Swim', - 160: 'Color Set', //??? + 160: 'Color Set', 177: 'Party', 178: 'Romance', 179: 'Caribbean', 180: 'American', 181: 'Sunset', 182: 'Royal', + 190: 'Save', + 191: 'Recall', 193: 'Blue', 194: 'Green', 195: 'Red', @@ -470,17 +484,19 @@ module.exports = function(container) { 197: 'Magenta' } var intellibriteModes = { - 'Off': 0, //All off in UI - 'On': 1, //All on in UI + 'Off': 0, + 'On': 1, 'Color Sync': 128, 'Color Swim': 144, - 'Color Set': 160, //??? + 'Color Set': 160, 'Party': 177, 'Romance': 178, 'Caribbean': 179, 'American': 180, 'Sunset': 181, 'Royal': 182, + 'Save': 190, + 'Recall': 191, 'Blue': 193, 'Green': 194, 'Red': 195, @@ -653,7 +669,8 @@ module.exports = function(container) { schedulePacketBytes: schedulePacketBytes, intellichemPacketFields: intellichemPacketFields, strIntellibriteModes: strIntellibriteModes, - intellibriteModes: intellibriteModes + intellibriteModes: intellibriteModes, + lightColors: lightColors } } diff --git a/src/etc/settings.js b/src/etc/settings.js index 1f2723de..2bf7ee36 100755 --- a/src/etc/settings.js +++ b/src/etc/settings.js @@ -507,6 +507,7 @@ module.exports = function (container) { _settings.logPacketWrites = configurationFileContent.poolController.log.logPacketWrites; _settings.logPumpTimers = configurationFileContent.poolController.log.logPumpTimers; _settings.logApi = configurationFileContent.poolController.log.logApi; + _settings.logIntellibrite = configurationFileContent.poolController.log.logIntellibrite; // Database _settings.influxEnabled = configurationFileContent.poolController.database.influx.enabled; diff --git a/src/integrations/outputToSmartThings.js b/src/integrations/outputToSmartThings.js index 2481956d..bd22f7a0 100644 --- a/src/integrations/outputToSmartThings.js +++ b/src/integrations/outputToSmartThings.js @@ -1,4 +1,4 @@ -module.exports = function(container) { +module.exports = function (container) { var http = container.http @@ -6,11 +6,15 @@ module.exports = function(container) { var address = configFile.outputToSmartThings.address var port = configFile.outputToSmartThings.port - var secureTransport = configFile.poolController.https.enabled===1?true:false + var secureTransport = configFile.poolController.https.enabled === 1 ? true : false + var logEnabled = 0 var serverURL; + if (configFile.outputToSmartThings.hasOwnProperty("logEnabled")) { + logEnabled = configFile.outputToSmartThings.log.enabled + } if (secureTransport) { @@ -27,7 +31,7 @@ module.exports = function(container) { }); function notify(event, data) { - if (address!=='*') { + if (address !== '*') { var json = JSON.stringify(data) var opts = { @@ -48,41 +52,44 @@ module.exports = function(container) { }); req.write(json); req.end(); - container.logger.debug('outputToSmartThings sent event %s', event) - container.logger.silly('outputToSmartThings (' + address + ':' + port + ') Sent ' + event + "'" + json + "'") - + if (logEnabled) { + container.logger.debug('outputToSmartThings sent event %s', event) + container.logger.silly('outputToSmartThings (' + address + ':' + port + ') Sent ' + event + "'" + json + "'") + } } } - socket.on('all', function(data) { + socket.on('all', function (data) { notify('all', data) }) - socket.on('circuit', function(data) { + socket.on('circuit', function (data) { notify('circuit', data) }) - - - socket.on('chlorinator', function(data) { - notify('chlorinator',data) + socket.on('chlorinator', function (data) { + notify('chlorinator', data) }) function init() { - container.logger.info('outputToSmartThings Loaded. \n\taddress: %s\n\tport: %s\n\tsecure: %s', address, port, secureTransport) + if (logEnabled) { + container.logger.info('outputToSmartThings Loaded. \n\taddress: %s\n\tport: %s\n\tsecure: %s', address, port, secureTransport) + } } var mdns = container.server.getMdnsEmitter(); - mdns.on('response', function(response){ - if (address!==response.additionals[2].data) { + mdns.on('response', function (response) { + if (address !== response.additionals[2].data) { //console.log('in ST: TXT data:', response.additionals[0].data.toString()) //console.log('in ST: SRV data:', JSON.stringify(response.additionals[1].data)) //console.log('in ST: IP Address:', response.additionals[2].data) address = response.additionals[2].data - container.logger.info('outputToSmartThings: Hub address updated to:', address) - // close mdns server if we don't need it... or keep it open if you think the IP address of ST will change + if (logEnabled) { + container.logger.info('outputToSmartThings: Hub address updated to:', address) + // close mdns server if we don't need it... or keep it open if you think the IP address of ST will change + } container.server.closeAsync('mdns') } diff --git a/src/lib/comms/inbound/controller/2.js b/src/lib/comms/inbound/controller/2.js index 262ae6e9..ea140878 100755 --- a/src/lib/comms/inbound/controller/2.js +++ b/src/lib/comms/inbound/controller/2.js @@ -70,7 +70,7 @@ module.exports = function(container) { } else { if (container.settings.get('logDuplicateMessages')) - logger.verbose('Msg# %s Duplicate broadcast.', counter) + logger.debug('Msg# %s Duplicate broadcast.', counter) } var decoded = true; diff --git a/src/lib/comms/inbound/controller/39.js b/src/lib/comms/inbound/controller/39.js index 9206c471..92fb9fa5 100644 --- a/src/lib/comms/inbound/controller/39.js +++ b/src/lib/comms/inbound/controller/39.js @@ -31,7 +31,7 @@ module.exports = function(container) { var _temp = data.slice(6, data.length) // create new array with all packets after the preamble, dest, src, action, and length _temp.splice(_temp.length-2, _temp.length) // remove checksum high/low bytes - container.circuit.setControllerLightGroup(_temp, counter) + container.circuit.assignControllerLightGroup(_temp, counter) return true } diff --git a/src/lib/comms/inbound/controller/96.js b/src/lib/comms/inbound/controller/96.js index 31227b3a..ea678d48 100644 --- a/src/lib/comms/inbound/controller/96.js +++ b/src/lib/comms/inbound/controller/96.js @@ -28,7 +28,10 @@ module.exports = function(container) { //eg RED: 165,16,16,34,96,2,195,0,2,12 // data[6] = color // data[7] = light group - container.circuit.setControllerLightColor(data[6], data[7], counter) + if (container.settings.get('logIntellibret')) { + container.logger.silly('Msg# %s: Set Light Group packet: %s\ncolor: %s \t light group: %s', counter, JSON.stringify(data), data[6], data[7]) + } + container.circuit.assignControllerLightColor(data[6], data[7], counter) return true } diff --git a/src/lib/comms/inbound/packet-buffer.js b/src/lib/comms/inbound/packet-buffer.js index 4e8f8ee2..045c5c5f 100755 --- a/src/lib/comms/inbound/packet-buffer.js +++ b/src/lib/comms/inbound/packet-buffer.js @@ -16,45 +16,62 @@ */ - - -module.exports = function(container) { +module.exports = function (container) { var logger = container.logger - var bufferArrayOfArrays = container.dequeue; + //var bufferArrayOfArrays = new Dequeue() - function push(packet){ - var packetArr = packet.toJSON().data - bufferArrayOfArrays.push(packetArr); + function push(packet) { - if (!container.receiveBuffer.getProcessingBuffer()) { - //console.log('Arrays being passed for processing: \n[[%s]]\n\n', testbufferArrayOfArrays.join('],\n[')) - container.receiveBuffer.iterateOverArrayOfArrays() - //testbufferArrayOfArrays=[] - } - container.sp.resetConnectionTimer() + + // try { + var packetArr = packet.toJSON().data + bufferArrayOfArrays.push(packetArr); + + if (!container.receiveBuffer.getProcessingBuffer()) { + //console.log('Arrays being passed for processing: \n[[%s]]\n\n', testbufferArrayOfArrays.join('],\n[')) + container.receiveBuffer.iterateOverArrayOfArrays() + //testbufferArrayOfArrays=[] + + container.sp.resetConnectionTimer() + } + // } + // catch (err) + // { + // logger.error('Error: ', err) + // logger.warn('Could not push packet to buffer. \n\tBuffer: %s\nResetting Serial Port.', JSON.stringify(packet)) + // logger.warn('Is SP Open?', container.sp.isOpen()) + // container.sp.drainSP(function () { + // console.log('SP Drained') + // }) + // container.sp.close() + // container.sp.init() + // container.queuepacket.init() + // container.writepacket.init() + // } } - function pop(){ - return bufferArrayOfArrays.shift() + function pop() { + return bufferArrayOfArrays.shift() } function length() { - return bufferArrayOfArrays.length + return bufferArrayOfArrays.length } + function clear() { container.logger.silly('Emptying the packet buffer queue') bufferArrayOfArrays.empty() container.receiveBuffer.clear() } -return{ - push : push, - pop : pop, - length : length, - clear: clear + return { + push: push, + pop: pop, + length: length, + clear: clear } } diff --git a/src/lib/comms/influx-connector.js b/src/lib/comms/influx-connector.js index 13859d1c..00b46cb0 100644 --- a/src/lib/comms/influx-connector.js +++ b/src/lib/comms/influx-connector.js @@ -75,9 +75,9 @@ module.exports = function (container) { 'numberStr': data[key].numberStr, 'name': data[key].name, 'circuitFunction': data[key].circuitFunction, - 'lightgroup': data[key].light.group, + //'lightgroup': data[key].light.group, 'friendlyName': data[key].friendlyName, - 'colorStr': data[key].light.colorStr, + //'colorStr': data[key].light.colorStr, 'freeze': data[key].freeze }, fields: { diff --git a/src/lib/comms/outbound/queue-packet.js b/src/lib/comms/outbound/queue-packet.js index 0c284fc5..212e74ed 100755 --- a/src/lib/comms/outbound/queue-packet.js +++ b/src/lib/comms/outbound/queue-packet.js @@ -62,8 +62,8 @@ module.exports = function(container) { packet = [255, 0, 255]; Array.prototype.push.apply(packet, message); - //if we request to "SET" a variable on the HEAT STATUS & TIME - if ((packet[7] === 136 || packet[7] === 133) && container.settings.get('intellitouch.installed')) { + //if we request to "SET" a variable on the HEAT STATUS & TIME, or Intellibrite + if ((packet[7] === 136 || packet[7] === 133 || packet[7]=== 167) && container.settings.get('intellitouch.installed')) { requestGet = 1; } } diff --git a/src/lib/comms/server.js b/src/lib/comms/server.js index 994f467d..890c8b47 100755 --- a/src/lib/comms/server.js +++ b/src/lib/comms/server.js @@ -523,8 +523,46 @@ module.exports = function (container) { }) }) + app.get('/light/mode/:mode', function (req, res) { + if (parseInt(req.params.mode) >= 0 && parseInt(req.params.mode) <= 256) { + res.send(container.circuit.setLightMode(parseInt(req.params.mode))) + } else { + res.send('Not a valid light power command.') + } + }) + + app.get('/light/circuit/:circuit/setColor/:color', function (req, res) { + if (parseInt(req.params.circuit) > 0 && parseInt(req.params.circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(req.params.color) >= 0 && parseInt(req.params.color) <= 256) { + res.send(container.circuit.setLightColor(parseInt(req.params.circuit), parseInt(req.params.color))) + } else { + res.send('Not a valid light set color.') + } + } + }) + + app.get('/light/circuit/:circuit/setSwimDelay/:delay', function (req, res) { + if (parseInt(req.params.circuit) > 0 && parseInt(req.params.circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(req.params.delay) >= 0 && parseInt(req.params.delay) <= 256) { + res.send(container.circuit.setLightSwimDelay(parseInt(req.params.circuit), parseInt(req.params.delay))) + } else { + res.send('Not a valid light swim delay.') + } + } + }) + + app.get('/light/circuit/:circuit/setPosition/:position', function (req, res) { + if (parseInt(req.params.circuit) > 0 && parseInt(req.params.circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(req.params.position) >= 0 && parseInt(req.params.position) <= container.circuit.getNumberOfCircuits()) { + res.send(container.circuit.setLightPosition(parseInt(req.params.circuit), parseInt(req.params.position))) + } else { + res.send('Not a valid light position.') + } + } + }) + app.get('/circuit/:circuit', function (req, res) { - if (parseInt(req.params.circuit) > 0 && parseInt(req.params.circuit) <= 20) { + if (parseInt(req.params.circuit) > 0 && parseInt(req.params.circuit) <= container.circuit.getNumberOfCircuits()) { res.send(container.circuit.getCircuit(parseInt(req.params.circuit))) } else { res.send('Not a valid circuit') diff --git a/src/lib/comms/socketio-helper.js b/src/lib/comms/socketio-helper.js index e5d22129..51e3f3b9 100755 --- a/src/lib/comms/socketio-helper.js +++ b/src/lib/comms/socketio-helper.js @@ -50,6 +50,9 @@ module.exports = function(container) { container.logger.silly('Socket.IO NOT outputting updateAvail because it is missing the result string: %s ', JSON.stringify(updateAvail)) } }) + .catch(function(err){ + container.logger.error("Error getting update available results: ", err) + }) } } @@ -405,6 +408,45 @@ module.exports = function(container) { emitToClients('pump') }) + socket.on('setLightMode', function (mode) { + if (parseInt(mode) >= 0 && parseInt(mode) <= 256) { + container.circuit.setLightMode(parseInt(mode)) + } else { + container.logger.warn('Socket lightMode: Not a valid light power command.') + } + }) + + socket.on('setLightColor', function (circuit, color) { + if (parseInt(circuit) > 0 && parseInt(circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(color) >= 0 && parseInt(color) <= 256) { + (container.circuit.setLightColor(parseInt(circuit), parseInt(color))) + } else { + container.logger.warn('Socket lightSetColor: Not a valid light set color.') + } + } + }) + + socket.on('setLightSwimDelay', function (circuit, delay) { + if (parseInt(circuit) > 0 && parseInt(circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(delay) >= 0 && parseInt(delay) <= 256) { + (container.circuit.setLightSwimDelay(parseInt(circuit), parseInt(delay))) + } else { + container.logger.warn('Socket lightSetSwimDelay: Not a valid light swim delay.') + } + } + }) + + + socket.on('setLightPosition', function (circuit, position) { + if (parseInt(circuit) > 0 && parseInt(circuit) <= container.circuit.getNumberOfCircuits()) { + if (parseInt(position) >= 0 && parseInt(position) <= container.circuit.getNumberOfCircuits()) { + (container.circuit.setLightPosition(parseInt(circuit), parseInt(position))) + } else { + container.logger.warn('Socket lightSetPosition: Not a valid light swim position.') + } + } + }) + /* New pumpCommand API's */ //#1 Turn pump off socket.on('pumpCommandOff', function(pump) { diff --git a/src/lib/comms/sp-helper.js b/src/lib/comms/sp-helper.js index 8e167804..a925b55a 100755 --- a/src/lib/comms/sp-helper.js +++ b/src/lib/comms/sp-helper.js @@ -71,11 +71,15 @@ module.exports = function(container) { }) sp.on('readable', function () { - container.packetBuffer.push(sp.read()) + + var buf = sp.read() + //console.log('Data as JSON:', JSON.stringify(buf.toJSON())) + + container.packetBuffer.push(buf) // data = sp.read() // console.log('Data in Buffer as Hex:', data); - // console.log('Data as JSON:', JSON.stringify(data.toJSON())) + }); } else { diff --git a/src/lib/equipment/circuit.js b/src/lib/equipment/circuit.js index 990c1b2f..e3c60426 100755 --- a/src/lib/equipment/circuit.js +++ b/src/lib/equipment/circuit.js @@ -15,13 +15,20 @@ * along with this program. If not, see . */ -function Light(group, colorStr, color) { - this.group = group; +function Light(position, colorStr, color) { + this.position = position; this.colorStr = colorStr; this.color = color; + this.colorSet = 0; + this.colorSetStr = 'White' + this.prevColor = 0; + this.prevColorStr = 'White'; + this.colorSwimDelay = 0; + this.mode = 0; + this.modeStr = 'Off'; } -function Circuit(number, numberStr, name, circuitFunction, status, freeze, macro, delay, group, colorStr, color) { +function Circuit(number, numberStr, name, circuitFunction, status, freeze, macro, delay, position, colorStr, color) { this.number = number; //1 this.numberStr = numberStr; //circuit1 this.name = name; //Pool @@ -30,16 +37,19 @@ function Circuit(number, numberStr, name, circuitFunction, status, freeze, macro this.freeze = freeze; //0, 1 this.macro = macro; //is the circuit a macro? this.delay = delay; //0 no delay, 1 in delay - this.light = { - 'group': group, - 'colorStr': colorStr, - 'color': color - }; + // this.light = { + // 'position': position, + // 'colorStr': colorStr, + // 'color': color, + // 'prevColor': 0, + // 'prevColorStr': 'White' + // }; } var currentCircuitArrObj = {}, lightGroup = {}, + lightGroupPacket = [], numberOfCircuits = 20 @@ -51,9 +61,10 @@ module.exports = function (container) { container.logger.info('Loading: circuit.js') var init = function () { - + lightGroup = {}, + lightGroupPacket = [] for (var i = 1; i <= numberOfCircuits; i++) { - lightGroup[i] = new Light(-1, 'off', -1) // assign empty light object + //lightGroup[i] = new Light(-1, 'off', -1) // assign empty light object currentCircuitArrObj[i] = new Circuit() } @@ -227,8 +238,9 @@ module.exports = function (container) { currentCircuitArrObj[circuit].freeze = circuitArrObj.freeze currentCircuitArrObj[circuit].circuitFunction = circuitArrObj.circuitFunction currentCircuitArrObj[circuit].macro = circuitArrObj.macro - - currentCircuitArrObj[circuit].light = JSON.parse(JSON.stringify(lightGroup[circuit])) //copy light group object + // if (isLight(circuitArrObj.circuitFunction)) { + // currentCircuitArrObj[circuit].light = JSON.parse(JSON.stringify(lightGroup[circuit])) //copy light group object + // } } @@ -275,16 +287,26 @@ module.exports = function (container) { } - //external methode to return the friendlyName - //TODO: Should set the friendlyName in the currentCircuitArrObj. + //external method to return the friendlyName function getFriendlyName(circuit) { try { if (circuit >= 1 && circuit <= numberOfCircuits) { - return currentCircuitArrObj[circuit].friendlyName - } else { + if (currentCircuitArrObj[circuit].friendlyName === undefined) { + return currentCircuitArrObj[circuit].name + + } + else { + return currentCircuitArrObj[circuit].friendlyName + + } + } + else { return container.constants.strCircuitFunction[circuit] } - } catch (err) { + } + + catch + (err) { logger.warn('Tried to retrieve circuit %s which is not a valid circuit.', circuit) return 'No valid circuit (' + circuit + ')' } @@ -294,8 +316,8 @@ module.exports = function (container) { function getAllNonLightCircuits() { var tempObj = {} for (var key in currentCircuitArrObj) { - if (currentCircuitArrObj[key].circuitFunction!==undefined){ - if (currentCircuitArrObj[key].name!=="NOT USED") { + if (currentCircuitArrObj[key].circuitFunction !== undefined) { + if (currentCircuitArrObj[key].name !== "NOT USED") { if (['intellibrite', 'light', 'sam light', 'sal light', 'color wheel'].indexOf(currentCircuitArrObj[key].circuitFunction.toLowerCase()) === -1) { tempObj[key] = { // "circuitFunction": currentCircuitArrObj[key].circuitFunction, @@ -311,8 +333,8 @@ module.exports = function (container) { function getAllLightCircuits() { var tempObj = {} for (var key in currentCircuitArrObj) { - if (currentCircuitArrObj[key].circuitFunction!==undefined || currentCircuitArrObj[key].name==="NOT USED") { - if (['intellibrite', 'light', 'sam light', 'sal light', 'color wheel'].indexOf(currentCircuitArrObj[key].circuitFunction.toLowerCase()) >= 0) { + if (currentCircuitArrObj[key].circuitFunction !== undefined || currentCircuitArrObj[key].name === "NOT USED") { + if (isLight(key)) { tempObj[key] = { //"circuitFunction": currentCircuitArrObj[key].circuitFunction, "circuitName": getFriendlyName(key) @@ -323,9 +345,23 @@ module.exports = function (container) { return tempObj } + function isLight(circuitNum) { + + // return true if circuitFunction is one of Light, SAM Light, SAL Light, Photon Gen, Color Wheel, Intellibrite + var circuitFunction = currentCircuitArrObj[circuitNum].circuitFunction + return [container.constants.strCircuitFunction[7], + container.constants.strCircuitFunction[9], + container.constants.strCircuitFunction[10], + container.constants.strCircuitFunction[11], + container.constants.strCircuitFunction[12], + container.constants.strCircuitFunction[16]].includes(circuitFunction) + + // return ['intellibrite', 'light', 'sam light', 'sal light', 'color wheel'].indexOf(circuitFunction) >= 0 + } + function setCircuitFromController(circuit, nameByte, functionByte, counter) { - if (circuit<=numberOfCircuits) { + if (circuit <= numberOfCircuits) { var circuitArrObj = {} //if the ID of the circuit name is 1-101 then it is a standard name. If it is 200-209 it is a custom name. The mapping between the string value in the getCircuitNames and getCustomNames is 200. So subtract 200 from the circuit name to get the id in the custom name array. @@ -353,7 +389,6 @@ module.exports = function (container) { setCircuitFriendlyNames() doWeHaveAllInformation() } else if (sendInitialBroadcast.initialCircuitsBroadcast === 1) { - //not sure we can do this ... have to check to see if they will come out the same if (JSON.stringify(currentCircuitArrObj[circuit]) === JSON.stringify(circuit)) { circuitChanged(circuit, circuitArrObj, counter) assignCircuitVars(circuit, circuitArrObj) @@ -402,11 +437,7 @@ module.exports = function (container) { //this function takes the status packet (controller:2) and parses through the equipment fields function assignCircuitStatusFromControllerStatus(data, counter) { - //temp object so we can compare - //Is there a faster way to do this? var circuitArrObj = [] - //TODO: clean this section up. probably don't need to broadcast it at all because we broadcast the full circuits later - //assign circuit status to circuitArrObj //This is an attempt to support >20 circuits when there is an expansion port(s) in use. Intellitouch can support up to 50. @@ -433,8 +464,6 @@ module.exports = function (container) { } doWeHaveAllInformation() - //logger.verbose('Msg# %s Circuit %s state discovered: %s', counter, j + (i * 8) + 1, newStatus) - //currentCircuitArrObj[j + (i * 8) + 1].status = newStatus } else for (i = 1; i <= numberOfCircuits; i++) { if (currentCircuitArrObj[i].status === circuitArrObj[i].status) { @@ -520,75 +549,308 @@ module.exports = function (container) { return response } - var setControllerLightColor = function (color, lightGroup, counter) { - console.log('clr %s, lgrp %s, counter %s', color, lightGroup, counter) + var assignControllerLightColor = function (color, param, counter) { + + var strIntellibriteModes = container.constants.strIntellibriteModes; - // if (container.settings.logConfigMessages) - container.logger.verbose('Msg# %s Detected a light change. Color -> %s (%s) for light group %s ', counter, color, strIntellibriteModes[color], lightGroup) + if (container.settings.get('logIntellibrite')) { + container.logger.verbose('Msg# %s Intellibrite light change. Color -> %s (%s) for param %s ', counter, color, strIntellibriteModes[color], param) + } + + var str = ''; + for (var key in lightGroup) { + // if circuit has the light attribute + if (currentCircuitArrObj[key].circuitFunction === 'Intellibrite') { + + // TODO: not exactly sure what the param does here. Doesn't seem to apply to the light groups. Is the lightGroup ever another number besides 0? + + if (color === 0) { + // Set to off; save previous colors + + + // save prev colors + currentCircuitArrObj[key].light.prevColor = currentCircuitArrObj[key].light.color + currentCircuitArrObj[key].light.prevColorStr = currentCircuitArrObj[key].light.colorStr + // save prev mode + currentCircuitArrObj[key].light.prevMode = currentCircuitArrObj[key].light.mode + currentCircuitArrObj[key].light.prevModeStr = currentCircuitArrObj[key].light.modeStr + + + // set current mode + currentCircuitArrObj[key].light.mode = color + currentCircuitArrObj[key].light.modeStr = strIntellibriteModes[color] + + + // set current color + currentCircuitArrObj[key].light.color = color + currentCircuitArrObj[key].light.colorStr = strIntellibriteModes[color] + + + } + else if (color === 1) { + // Set to on; restore previous colors and mode + // currentCircuitArrObj[key].light.mode = color + // currentCircuitArrObj[key].light.modeStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.color = currentCircuitArrObj[key].light.prevColor + currentCircuitArrObj[key].light.colorStr = currentCircuitArrObj[key].light.prevColorStr + // restore prev mode + currentCircuitArrObj[key].light.mode = currentCircuitArrObj[key].light.prevMode + currentCircuitArrObj[key].light.modeStr = currentCircuitArrObj[key].light.prevModeStr + + currentCircuitArrObj[key].light.prevColor = 0 + currentCircuitArrObj[key].light.prevColorStr = 'n/a' + currentCircuitArrObj[key].light.prevMode = 0 + currentCircuitArrObj[key].light.prevModeStr = 'n/a' + } + else if (color === 160) { + // Color Set + currentCircuitArrObj[key].light.mode = color + currentCircuitArrObj[key].light.modeStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.color = lightGroup[key].colorSet + currentCircuitArrObj[key].light.colorStr = lightGroup[key].colorSetStr + + currentCircuitArrObj[key].light.prevColor = 0 + currentCircuitArrObj[key].light.prevColorStr = 'n/a' + currentCircuitArrObj[key].light.prevMode = 0 + currentCircuitArrObj[key].light.prevModeStr = 'n/a' + + } + else if (color === 190 || color === 191) { + // save and recall + currentCircuitArrObj[key].light.mode = color + currentCircuitArrObj[key].light.modeStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.color = color + currentCircuitArrObj[key].light.colorStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.prevColor = 0 + currentCircuitArrObj[key].light.prevColorStr = 'n/a' + currentCircuitArrObj[key].light.prevMode = 0 + currentCircuitArrObj[key].light.prevModeStr = 'n/a' + } + else { + // all other direct color and built-in cycle modes + currentCircuitArrObj[key].light.mode = color + currentCircuitArrObj[key].light.modeStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.color = color + currentCircuitArrObj[key].light.colorStr = strIntellibriteModes[color] + + currentCircuitArrObj[key].light.prevColor = 0 + currentCircuitArrObj[key].light.prevColorStr = 'n/a' + currentCircuitArrObj[key].light.prevMode = 0 + currentCircuitArrObj[key].light.prevModeStr = 'n/a' + } + + + str += getFriendlyName(key) + '\n' - for (var key in currentCircuitArrObj) { - if (currentCircuitArrObj[key].light.group === lightGroup) { - currentCircuitArrObj[key].light.color = color - currentCircuitArrObj[key].light.colorStr = strIntellibriteModes[color] - lightGroup[key].light.color = color - lightGroup[key].light.colorStr = strIntellibriteModes[color] - // if (container.settings.logConfigMessages) - container.logger.info('Msg# %s Detected a light change. Color -> %s (%s) for circuit %s (%s)', counter, color, strIntellibriteModes[color], JSON.stringify(currentCircuitArrObj[key].light, null, 2), key) } } + container.io.emitToClients('circuit') + if (container.settings.get("logIntellibrite")) { + container.logger.info('Msg# %s Intellibrite light change. Color -> %s for circuit(s): \n%s', counter, strIntellibriteModes[color], str) + } } - var setControllerLightGroup = function (_lightGroupPacketArr, counter) { - if (container.settings.get('logConfigMessages')) - container.logger.info('Msg# %s Light group/positions are: %s', counter, _lightGroupPacketArr) + var assignControllerLightGroup = function (_lightGroupPacketArr, counter) { - var _temp = {} //temporary object to hold light group/position assignments - for (var i = 0; i <= 7; i++) { - // split off groups of 4 packets and assign them to a copy of the lightGroup - var _this = _lightGroupPacketArr.splice(0, 4) // remove this light groupt + if (1 === 0) {//(lightGroupPacket === _lightGroupPacketArr) { + //no change + if (container.settings.get('logIntellibrite')) + container.logger.silly('Msg# %s Duplicate Light all on/off and position packet is: %s', counter, _lightGroupPacketArr) + } + else { + lightGroupPacket = _lightGroupPacketArr.slice() + if (container.settings.get('logIntellibrite')) + container.logger.debug('Msg# %s Light all on/off and position packet is: %s', counter, _lightGroupPacketArr) + + var tempLightGroup = {} //temporary object to hold light group/position assignments + var discovered = 0; + if (Object.keys(lightGroup).length === 0) { + discovered = 1 + } + for (var i = 0; i <= 7; i++) { + // split off groups of 4 packets and assign them to a copy of the lightGroup + var _temp = _lightGroupPacketArr.splice(0, 4) // remove this light group + + if (_temp[0] !== 0) { + tempLightGroup[i] = { + 'circuit': _temp[0], + 'position': (_temp[1] >> 4) + 1, // group/position 0000=1; 0001=2; 0010=3, etc. + 'colorSet': (_temp[1] & 15), + 'colorSetStr': container.constants.lightColors[_temp[1] & 15], + 'colorSwimDelay': _temp[2] >> 1 + } + } + + + /* + + Use IndexBy to Pivot the array. + We pivot the array because the positions can change and we don't want to lose any details. + For example, if a light group changes from the 4th position to the 3rd, we don't want to delete it from the 4th position when we look for changes. + + Example output: + indexBy: { + "7": { + "position": 1, + "circuit": 7 + }, + "8": { + "position": 2, + "circuit": 8 + }, + "9": { + "position": 4, + "circuit": 9 + }, + "16": { + "position": 3, + "circuit": 16 + } + } + + */ + tempLightGroup = container._.indexBy(tempLightGroup, 'circuit') - if (_this[0] !== 0) { - _temp[_this[0]] = { - 'group': (_this[1] / 16) + 1 - } // group/position reported as 0, 16, 32, 48 } - } + var changed = 0 - var changed = 0 - for (var key in lightGroup) { - if (lightGroup[key].group !== -1 && !_temp.hasOwnProperty(key)) { - // compare existing light group to passed light group assignments. - // if the group is not 0 in the existing lightGroup, and does - // not exist in the passed light assignments than it was deleted - // light group deleted - changed = 1 - lightGroup[key].group = new Light(0, 'off', 0) - currentCircuitArrObj[key].light = new Light(0, 'off', 0) - if (container.settings.get('logConfigMessages')) - container.logger.verbose('Msg# %s Light group deleted on circuit %s (%s):', counter, currentCircuitArrObj[key].friendlyName, key, JSON.stringify(lightGroup[key], null, 2)) - } else if (_temp.hasOwnProperty(key)) { - // if the group does exist in the passed light group - if (lightGroup[key].group !== _temp[key].group) { - // check to see if it is the same as what we already have - // if it's different, we can determine that there is an - // add/change in group assignment - changed = 1 - lightGroup[key].group = _temp[key].group - currentCircuitArrObj[key].light.group = _temp[key].group - if (container.settings.get('logConfigMessages')) - container.logger.verbose('Msg# %s Light group added or changed for circuit %s (%s):', counter, currentCircuitArrObj[key].friendlyName, key, JSON.stringify(lightGroup[key], null, 2)) - } else if (lightGroup[key].group !== _temp[key].group) { - // if it is the same, then no change - if (container.settings.get('logConfigMessages')) - container.logger.silly('Msg# %s No change in light group for circuit %s (%s):', counter, currentCircuitArrObj[key].friendlyName, key, lightGroup[key]) + var diff1 = container.deepdiff.diff(lightGroup, tempLightGroup) + + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Intellibrite All on/off groups indexBy: ', JSON.stringify(tempLightGroup, null, 2)) + if (diff1 === undefined) { + container.logger.debug('Intellibrite all on/off packet retrieved, but there were no changes. (Hint: you need to add the circuit to the "Special Lights" group in order for Light Position changes to take effect.') + + } + else { + container.logger.debug('Intellibrite all on/off differences: %s\n\tFull packet: %s', JSON.stringify(diff1, null, 2), _lightGroupPacketArr) + + for (var key in diff1) { + var cir = diff1[key].path //circuit we want to change + + if (diff1[key].kind === 'D') { + + + changed = 1 + + // use the prior value, and set it to 0 + currentCircuitArrObj[cir].light = {} + + // use the new circuit + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Intellibrite all on/off group DELETED key:', JSON.stringify(diff1[key], null, 2)) + container.logger.verbose('Msg# %s Light group deleted for circuit %s (%s):', counter, getFriendlyName(cir), cir, JSON.stringify(lightGroup[cir], null, 2)) + } + } + + else if (diff1[key].kind === 'N') { + + changed = 1 + + /* + diff1[key].path] is the key for the tempLightGroup + when N(ew), we want to add it. + + { + "kind": "N", + "path": [ + "7" + ], + "rhs": { + "position": 1, + "circuit": 7 + } + } + */ + + + currentCircuitArrObj[cir].light = new Light(diff1[key].rhs.position, 'off', 0) + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('NEW key:', JSON.stringify(diff1[key], null, 2)) + container.logger.verbose('Msg# %s Light details added for circuit %s (%s):', counter, getFriendlyName(cir), cir, diff1[key].rhs.position) + } + } + else if (diff1[key].kind === 'E') { + cir = diff1[key].path[0] //circuit we want to change; different for edited because of the path + + changed = 1 + + /* + diff1[key].path] is the key for the tempLightGroup + when E(dited), we want to change it. + + [ + { + "kind": "E", + "path": [ + "7", + "group" + ], + "lhs": 3, + "rhs": 2 + } + ] + */ + + var el = diff1[key].path[1] + var val = diff1[key].rhs + currentCircuitArrObj[cir].light[el] = val + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('NEW key:', JSON.stringify(diff1[key], null, 2)) + container.logger.verbose('Msg# %s Light attribute `%s` changed for circuit %s (%s) to', counter, el, getFriendlyName(cir), cir, JSON.stringify(diff1[key].rhs, null, 2)) + } + } + else { + container.logger.warn('Msg# %s Intellibrite all on/off change -- unknown for circuit %s (%s):', counter, getFriendlyName(cir), cir, JSON.stringify(diff1, null, 2)) + } + } + + lightGroup = JSON.parse(JSON.stringify(tempLightGroup)) + + for (var key in lightGroup) { + currentCircuitArrObj[key].light.position = lightGroup[key].position + currentCircuitArrObj[key].light.colorSet = lightGroup[key].colorSet + currentCircuitArrObj[key].light.colorSetStr = lightGroup[key].colorSetStr + currentCircuitArrObj[key].light.colorSwimDelay = lightGroup[key].colorSwimDelay + + } + + if (discovered === 1) { + if (container.settings.get('logIntellibrite')) + container.logger.silly('Msg# %s: Intellibrite All On/Off Light positions discovered \n%s:', counter, JSON.stringify(lightGroup, null, 2)) + var str = '' + + + for (var key in lightGroup) { + str += getFriendlyName(key) + ': Position ' + lightGroup[key].position + '\n' + } + container.logger.info('Msg# %s: Intellibrite All On/Off and Light positions discovered: \n%s', counter, str) + } + + if (changed) { + + container.io.emitToClients('circuit') + } + if (sendInitialBroadcast.initialCircuitsBroadcast === 1) container.influx.writeCircuit(currentCircuitArrObj) } + } } - if (changed) - container.io.emitToClients('circuit'); - if (sendInitialBroadcast.initialCircuitsBroadcast === 1) container.influx.writeCircuit(currentCircuitArrObj) + + } + + function getLightGroup() { + return lightGroup } function setDelayCancel(callback) { @@ -606,6 +868,140 @@ module.exports = function (container) { } + function getNumberOfCircuits() { + return numberOfCircuits + } + + + function setLightMode(mode) { + // 255, 0, 255, 165, 33, 16, 34, 96, 2, 1, 0, 1, 91 + + var packet = [165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 96, 2, mode, 0] + container.queuePacket.queuePacket(packet); + + // this causes LOTS of problems... it goes into an endless loop. :( + // better way to do this??? + // assignControllerLightColor(mode, 0, 0) + + var retStr = 'API: Intellibrite Light Mode ' + container.constants.strIntellibriteModes[mode] + ' (' + mode + ') requested' + if (container.settings.get('logAPI') || container.settings.get('logIntellibrite')) { + container.logger.info(retStr) + + } + return retStr + } + + function setLightPosition(circuit, position) { + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightPosition original packet:', lightGroupPacket) + + } + for (var i = 0; i <= 7; i++) { + + // packets are in groups of 4. + // lightGroupPacket[0] is circuit + // lightGroupPacket[1] xxxxyyyy where xxxx is position and yyyy is the color + // 'position': (_temp[1] >> 4) + 1, // group/position 0000=1; 0001=2; 0010=3, etc. + + positionBinary = (position - 1) << 4 + if (lightGroupPacket[i * 4] === circuit) { + lightGroupPacket[(i * 4) + 1] = (positionBinary) + (lightGroupPacket[(i * 4) + 1] & 15) + } + + + } + + var packet = [165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 167, 32].concat(lightGroupPacket) + + container.queuePacket.queuePacket(packet); + + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightPosition NEW packet:', lightGroupPacket) + + } + var retStr = 'API: Light group circuit ' + circuit + ' setPosition is now : ' + position + if (container.settings.get('logAPI')) { + container.logger.info(retStr) + + } + + return retStr + + } + + function setLightColor(circuit, color) { + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightColor original packet:', lightGroupPacket) + + } + for (var i = 0; i <= 7; i++) { + + // packets are in groups of 4. + // lightGroupPacket[0] is circuit + // lightGroupPacket[1] xxxxyyyy where xxxx is position and yyyy is the color + + if (lightGroupPacket[i * 4] === circuit) { + lightGroupPacket[(i * 4) + 1] = (color) + (lightGroupPacket[(i * 4) + 1] & 240) + } + + + } + + var packet = [165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 167, 32].concat(lightGroupPacket) + + container.queuePacket.queuePacket(packet); + + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightColor NEW packet:', lightGroupPacket) + + } + var retStr = 'API: Light group circuit ' + circuit + ' setColor is now : ' + container.constants.lightColors[color] + ' (' + color + ')' + if (container.settings.get('logAPI')) { + container.logger.info(retStr) + + } + + return retStr + + } + + function setLightSwimDelay(circuit, delay) { + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightDelay original packet:', lightGroupPacket) + + } + + for (var i = 0; i <= 7; i++) { + + // packets are in groups of 4. + // lightGroupPacket[0] is circuit + // lightGroupPacket[2] is the delay + if (lightGroupPacket[i * 4] === circuit) { + lightGroupPacket[(i * 4) + 2] = (delay << 1) + (lightGroupPacket[(i * 4) + 2] & 1) + } + } + + // 165,33,16,34,167,32,7,10,4,0,8,22,14,0,16,32,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,58 + var packet = [165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 167, 32].concat(lightGroupPacket) + container.queuePacket.queuePacket(packet); + + if (container.settings.get('logIntellibrite')) { + container.logger.silly('Light setLightDelay NEW packet:', lightGroupPacket) + + } + var retStr = 'API: Light group circuit ' + circuit + ' Swim Delay is now : ' + delay + ' seconds' + if (container.settings.get('logAPI')) { + container.logger.info(retStr) + + } + + return retStr + + } + + /*istanbul ignore next */ if (container.logModuleLoading) container.logger.info('Loaded: circuit.js') @@ -628,11 +1024,17 @@ module.exports = function (container) { setCurrentStatusBytes: setCurrentStatusBytes, toggleCircuit: toggleCircuit, setCircuit: setCircuit, - setControllerLightColor: setControllerLightColor, - setControllerLightGroup: setControllerLightGroup, + assignControllerLightColor: assignControllerLightColor, + assignControllerLightGroup: assignControllerLightGroup, setDelayCancel: setDelayCancel, //TESTING setCircuitFriendlyNames: setCircuitFriendlyNames, - numberOfCircuits: numberOfCircuits + numberOfCircuits: numberOfCircuits, + getNumberOfCircuits: getNumberOfCircuits, + getLightGroup: getLightGroup, + setLightMode: setLightMode, + setLightColor: setLightColor, + setLightSwimDelay: setLightSwimDelay, + setLightPosition: setLightPosition } } diff --git a/src/lib/equipment/time.js b/src/lib/equipment/time.js index aa2d38ba..667a995a 100755 --- a/src/lib/equipment/time.js +++ b/src/lib/equipment/time.js @@ -105,7 +105,7 @@ module.exports = function(container) { container.io.emitToClients('time') time.controllerTime = timeStr; } else { - if (container.settings.get('logConfigMessages')) container.logger.debug('No change in time.') + if (container.settings.get('logConfigMessages')) container.logger.silly('No change in time.') } } diff --git a/src/www/bootstrap/configClient.json b/src/www/bootstrap/configClient.json index e49ff8b1..cb9eb10e 100755 --- a/src/www/bootstrap/configClient.json +++ b/src/www/bootstrap/configClient.json @@ -32,6 +32,9 @@ }, "release": { "state": "visible" + }, + "lights": { + "state": "visible" } }, "equipConfig": { @@ -87,7 +90,7 @@ } }, "pumpParams": { - "name": { + "friendlyName": { "title": "Parameter", "type": "th" }, diff --git a/src/www/bootstrap/index.html b/src/www/bootstrap/index.html index 32d899ce..44962cd7 100755 --- a/src/www/bootstrap/index.html +++ b/src/www/bootstrap/index.html @@ -2,798 +2,999 @@ - nodejs-poolController Control Center - - - - - - - - - - - - - - - - - - - - + nodejs-poolController Control Center + + + + + + + + + + + + + + + + + + + +
- +
- -
-
Release Details - - -
-
- - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
5.0.0 release notes
Dual interface (http/https) support - Now supports both http/https web servers at the same time, if desired. Either/both can have Auth. Redirect from http to https, if desired. Thanks @arrmo. -
Serial Port & Packages + +
+
Release Details + + +
+
+ + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - -
5.0.0 release notes
Dual interface (http/https) support + Now supports both http/https web servers at the same time, if desired. Either/both can have Auth. Redirect from http to https, if desired. Thanks @arrmo. +
Serial Port & Packages Serialport has been upgraded from v4 to v6. Please make sure you run npm update to get the latest versions. You can run npm outdated to see if any dependencies are out of date.

Note: Serialport now only support major, LTS versions of node. You need to be running Node v4, v6 or v8.

-
Intellichem - Intellichem packets and UI added -
Socket/APIs - Sockets/API will now all be SINGULAR. If you have custom apps that call circuits, schedules, etc please change them to circuit, schedule, etc. - Per Issue #57, all sockets will transition to include a parent tag that describes the information. This should make any code/logic easier in the future. -
- Inactivity Timer - - Implemented serial port/net inactivity timer. See Issue #9 - -
SSDP/UPnP - Thanks @bsileo for implementing a ZeroConf protocol for SmartThings and other services to discover poolController. -
-
-
+
Intellichem + Intellichem packets and UI added +
Socket/APIs + Sockets/API will now all be SINGULAR. If you have custom apps that call circuits, schedules, etc please change them to circuit, schedule, etc. + Per Issue #57, all sockets will transition to include a parent tag that describes the information. This should make any code/logic easier in the future. +
+ Inactivity Timer + + Implemented serial port/net inactivity timer. See Issue #9 + +
SSDP/UPnP + Thanks @bsileo for implementing a ZeroConf protocol for SmartThings and other services to discover poolController. +
+
+
-
-
-
System Information - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterValue
Date - -
Time - -
Air Temp---
Solar Temp---
Freeze Prot---
-
-
-
-
- -
-
Pool Details - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterStatus
Pool State - -
Temperature---
SetPoint - - --- - -
Heater Mode - - - -
-
-
-
-
- -
-
Spa Details - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterStatus
Spa State - -
Temperature---
SetPoint - - --- - -
Heater Mode - - - -
-
-
-
-
- -
-
Chlorinator Details - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterStatus
Chlorinator Installed? - Not installed -
Chlorinator State - -
Name---
Salt---
Current Output---
Pool Setpoint---
SuperChlorinate---
Status---
-
-
-
-
- -
-
Lighting / Features - - -
-
- - - - - - - - - -
FeatureStatus
-
-
-
-
- -
-
Pumps - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Parameter
Watts
RPM
GPM
Error
Drive State
Run Mode
-
-
-
-
- -
-
Schedules - - - -
-
- - - - - - - - - - - -
#CircuitStart TimeEnd Time
-
-
-
-
- -
-
EggTimers - - - -
-
- - - - - - - - - - - - - - -
#CircuitDuration
-
-
-
-
- -
-
Intellichem - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SI
ParameterpHORP
Reading
Setpoint
Tank Level
Mode
Water Flow Alarm
Calcium Hardness
Total Alkalinity
CYA
-
-
-
-
- -
-
Debug Log - - -
-
-
- DEBUG LOG ...
-
-
-
-
+ + Freeze Prot + --- + + + +
+ + +
+ +
+
Pool Details + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterStatus
Pool State + +
Temperature---
SetPoint + + --- + +
Heater Mode + + + +
+
+
+
+
+ +
+
Spa Details + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterStatus
Spa State + +
Temperature---
SetPoint + + --- + +
Heater Mode + + + +
+
+
+
+
+ +
+
Chlorinator Details + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterStatus
Chlorinator Installed? + Not installed +
Chlorinator State + +
Name---
Salt---
Current Output---
Pool Setpoint---
SuperChlorinate---
Status---
+
+
+
+
+ +
+
Lighting / Features + + +
+
+ + + + + + + + + +
FeatureStatus
+
+
+
+
+ +
+
Pumps + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameter
Watts
RPM
GPM
Error
Drive State
Run Mode
+
+
+
+
+ +
+
Schedules + + + +
+
+ + + + + + + + + + + +
#CircuitStart TimeEnd Time
+
+
+
+
+ +
+
EggTimers + + + +
+
+ + + + + + + + + + + + + + +
#CircuitDuration
+
+
+
+
+ +
+
Intellichem + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SI
ParameterpHORP
Reading
Setpoint
Tank Level
Mode
Water Flow Alarm
Calcium Hardness
Total Alkalinity
CYA
+
+
+
+
+ +
+
Lights + + + +
+
+ + + + + + + + + + + + + + + +
Mode
+ +
+
+
+ + + + + + + + + + + + +
+
+
+
+
+ +
+
Debug Log + + +
+
+
+ DEBUG LOG ...
+
+
+
+
diff --git a/src/www/bootstrap/main.js b/src/www/bootstrap/main.js index 6edcf9c1..e2fbe482 100755 --- a/src/www/bootstrap/main.js +++ b/src/www/bootstrap/main.js @@ -184,6 +184,7 @@ function bindClockPicker(el, _align) { }) } + // Add last row to schedule/egg timer if there is an available slot function bindSelectPickerScheduleCircuit(el, _default) { // To style only ', { + class: 'bootstrap-select', + id: currCircuit.numberStr + 'Color', + "data-width": "auto", + "data-circuitnum": indx + }))) + ) + // position + .append( + ($('')) + .append( + ($('
', { + //class: 'input-group', + //style: "width:55px" + })) + .append( + $('', { + class: 'bootstrap-select', + id: currCircuit.numberStr + 'SwimDelay', + "data-width": "auto", + "data-circuitnum": indx + }))) + ) + + + // append colors to select-picker + $('#' + currCircuit.numberStr + 'Color') + .append($('