From fffb04378eb4fa77298fe409673ea32b5385eda2 Mon Sep 17 00:00:00 2001 From: tagyoureit Date: Tue, 16 Oct 2018 08:28:13 -0700 Subject: [PATCH 01/10] intellichlor changes, package updates --- README.md | 8 +- specs/assets/config/config.json | 22 +- .../chlorinator-controller.spec.js | 4 +- .../packetBufferToDecode-chlorinator.spec.js | 2 +- .../lib/comms/server-chlorinator-api.spec.js | 213 ++++++++++++++--- .../helpers/bootrstrap-config-editor.spec.js | 4 +- src/lib/comms/server.js | 38 +++ src/lib/equipment/chlorinator.js | 221 ++++++++++-------- 8 files changed, 362 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index 8eba71a3..d69a4232 100755 --- a/README.md +++ b/README.md @@ -198,10 +198,14 @@ for discussions, designs, and clarifications, we recommend you join our [Gitter #### Chlorinator and Intellichem - +(Note: As of 5.3 the Chlorinator API's will route the commands either through the Intellitouch/Intellicom or directly to the chlorinator depending upon your setup) | Direction | Socket | API | Description | | --- | --- | --- | --- | -| To app | setchlorinator(level) | /chlorinator/{level}|sets the level of output for chlorinator (pool only) +| To app | setchlorinator(poolLevel, spaLevel, superChlorinateHours) | /chlorinator/{level}/spa/{level}/superChlorinateHours/{hours}|sets the level of output for chlorinator (spa/superchlorinate can be omitted) +| To app | | /chlorinator/pool/{level}|sets the pool output % +| To app | | /chlorinator/spa/{level}|sets the spa output % +| To app | | /chlorinator/pool/{level}/spa/{level}|sets the pool & spa output % +| To app | | /chlorinator/superChlorinateHours/{hours}|sets the hours for super chlorination | To client | chlorinator | outputs an object with the chlorinator information | To app | | /chlorinator | outputs an object with the chlorinator information | To app | intellichem | /intellichem |outputs an object with the intellichem information diff --git a/specs/assets/config/config.json b/specs/assets/config/config.json index bbf0b517..b7957a15 100644 --- a/specs/assets/config/config.json +++ b/specs/assets/config/config.json @@ -1,4 +1,15 @@ { + "meta": { + "notifications": { + "version": { + "remote": { + "version": "4.1.200", + "tag_name": "v4.1.200", + "dismissUntilNextRemoteVersionBump": false + } + } + } + }, "version": "5.2.0", "equipment": { "controller": { @@ -141,16 +152,5 @@ } } }, - "meta": { - "notifications": { - "version": { - "remote": { - "version": "4.0.0", - "tag_name": "v4.0.0", - "dismissUntilNextRemoteVersionBump": false - } - } - } - }, "integrations": {} } \ No newline at end of file diff --git a/specs/lib/comms/controllers/chlorinator-controller.spec.js b/specs/lib/comms/controllers/chlorinator-controller.spec.js index 96784d37..32e9b01e 100755 --- a/specs/lib/comms/controllers/chlorinator-controller.spec.js +++ b/specs/lib/comms/controllers/chlorinator-controller.spec.js @@ -1,9 +1,9 @@ -describe('chlorinator controller', function () { +describe('chlorinator controller - Virtual', function () { describe('#startChlorinatorController starts the timer for 1 or 2 chlorinators', function () { before(function () { - return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor.json'}) + return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor_virtual.json'}) }); beforeEach(function () { diff --git a/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js b/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js index d4b4e4a4..48d65171 100644 --- a/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js +++ b/specs/lib/comms/inbound/packetBufferToDecode-chlorinator.spec.js @@ -10,7 +10,7 @@ describe('chlorinator packets: receives packets from buffer and follows them to }); beforeEach(function () { - return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor.json'}) + return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor_virtual.json'}) .then(function () { sinon = sinon.sinon.create() loggers = setupLoggerStubOrSpy('stub', 'spy') diff --git a/specs/lib/comms/server-chlorinator-api.spec.js b/specs/lib/comms/server-chlorinator-api.spec.js index ad47a316..20bc0394 100644 --- a/specs/lib/comms/server-chlorinator-api.spec.js +++ b/specs/lib/comms/server-chlorinator-api.spec.js @@ -32,45 +32,32 @@ describe('#set functions', function() { }) - it('should send a message if chlorinator is not installed', function (done) { - global.requestPoolDataWithURLAsync('chlorinator/0') + it('should send a message if chlorinator is not installed', function () { + return global.requestPoolDataWithURLAsync('chlorinator/0') .then(function (result) { result.text.should.contain('FAIL') queuePacketStub.callCount.should.eq(0) }) - .then(done) - .catch(function (err) { - bottle.container.logger.error('Error with chlor not installed.', err) - console.error(err) - }) + }) }) }) describe('#sends chlorinator commands', function() { - context('with a REST API', function() { + context('with the VIRTUAL chlorinator with a REST API', function() { before(function() { - - // return Promise.resolve() - // .then(function(){ - // bottle.container.settings.set('virtual.chlorinatorController', 1) - // bottle.container.settings.set('chlorinator.installed', 1) - // }) - // .then(global.initAllAsync) - return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor.json'}) + return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellichlor_virtual.json'}) }); beforeEach(function() { - loggers = setupLoggerStubOrSpy('stub','spy') + loggers = setupLoggerStubOrSpy('stub','stub') queuePacketStub = sinon.stub(bottle.container.queuePacket, 'queuePacket') settingsStub = sinon.stub(bottle.container.settings, 'updateChlorinatorDesiredOutputAsync') - //getVersionNotificationStub = sinon.stub(bottle.container.settings, 'get').withArgs('notifications.version.remote').returns({'dismissUntilNextRemoteVersionBump': true}) - }) afterEach(function() { - bottle.container.chlorinator.setChlorinatorLevelAsync(0) + bottle.container.chlorinator.setChlorinatorLevelAsync(0,0,0) .then(function(){ bottle.container.chlorinatorController.clearTimer() // Clear out the write queues @@ -100,62 +87,212 @@ describe('#set functions', function() { queuePacketStub.callCount.should.eq(1) queuePacketStub.args[0][0].should.deep.equal([16, 2, 80, 17, 50]) }) - .catch(function(err){ - bottle.container.logger.error(err.toString()) - }) }) - it('starts chlorinator at 100%', function(done) { + it('starts chlorinator at 100%', function() { - global.requestPoolDataWithURLAsync('chlorinator/100') + return global.requestPoolDataWithURLAsync('chlorinator/100') .then(function(result) { result.status.should.eq('on') result.value.should.eq(100) queuePacketStub.callCount.should.eq(1) queuePacketStub.args[0][0].should.deep.equal([16, 2, 80, 17, 100]) }) - .then(done,done) + }) - it('starts chlorinator at 101% (super chlorinate)', function(done) { + it('starts chlorinator at 101% (super chlorinate)', function() { - global.requestPoolDataWithURLAsync('chlorinator/101') + return global.requestPoolDataWithURLAsync('chlorinator/101') .then(function(result) { result.status.should.eq('on') result.value.should.eq(101) queuePacketStub.callCount.should.eq(1) queuePacketStub.args[0][0].should.deep.equal([16, 2, 80, 17, 101]) }) - .then(done,done) + }) - it('starts chlorinator at -1% (should fail)', function(done) { + it('starts chlorinator at -1% (should fail)', function() { - global.requestPoolDataWithURLAsync('chlorinator/-1') + return global.requestPoolDataWithURLAsync('chlorinator/-1') .then(function(result) { result.text.should.contain('FAIL') queuePacketStub.callCount.should.eq(0) }) - .then(done,done) + }) - it('starts chlorinator at 150% (should fail)', function(done) { + it('starts chlorinator at 150% (should fail)', function() { - global.requestPoolDataWithURLAsync('chlorinator/150') + return global.requestPoolDataWithURLAsync('chlorinator/150') .then(function(result){ result.text.should.contain('FAIL') queuePacketStub.callCount.should.eq(0) }) - .then(done,done) + }) - it('starts chlorinator at 0%', function(done) { + it('starts chlorinator at 0%', function() { //do this one last so - global.requestPoolDataWithURLAsync('chlorinator/0') + return global.requestPoolDataWithURLAsync('chlorinator/0') .then(function(result) { result.status.should.eq('off') result.value.should.eq(0) queuePacketStub.callCount.should.eq(1) queuePacketStub.args[0][0].should.deep.equal([16, 2, 80, 17, 0]) }) - .then(done,done) + + }) + }); + }); + + + describe('#sends chlorinator commands', function() { + + context('with a Intellitouch chlorinator with a REST API', function() { + + before(function() { + + return global.initAllAsync({'configLocation': '/specs/assets/config/templates/config_intellitouch_intellichlor.json'}) + }); + + beforeEach(function() { + loggers = setupLoggerStubOrSpy('stub','stub') + queuePacketStub = sinon.stub(bottle.container.queuePacket, 'queuePacket') + settingsStub = sinon.stub(bottle.container.settings, 'updateChlorinatorDesiredOutputAsync') + preambleStub = sinon.stub(bottle.container.intellitouch, 'getPreambleByte').returns(99) + }) + + afterEach(function() { + bottle.container.chlorinator.setChlorinatorLevelAsync(0,0,0) + .then(function(){ + bottle.container.chlorinatorController.clearTimer() + // Clear out the write queues + bottle.container.queuePacket.init() + bottle.container.writePacket.init() + sinon.restore() + }) + + }) + + after(function() { + return Promise.resolve() + .then(function(){ + bottle.container.settings.set('virtual.chlorinatorController', 0) + bottle.container.settings.set('chlorinator.installed', 0) + }) + .then(global.stopAllAsync) + }) + + it('starts chlorinator at 50%', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/50') + .delay(50) + .then(function(result) { + result.status.should.eq('on') + result.value.should.eq(50) + queuePacketStub.callCount.should.eq(1) + // NOTE: this spa setting should be 22 (11%) because that is what is saved in the config file + // all others should then be 0 because we reset the value after each test + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,22,50,0,0,0,0,0,0,0,0]) + }) + + }) + it('starts chlorinator at 100%', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/100') + .delay(50) + .then(function(result) { + result.status.should.eq('on') + result.value.should.eq(100) + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,0,100,0,0,0,0,0,0,0,0]) + }) + + }) + it('starts chlorinator at 101% (super chlorinate)', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/101') + .then(function(result) { + result.status.should.eq('on') + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,0,0,129,0,0,0,0,0,0,0]) + }) + + }) + it('starts chlorinator at -2% (should fail)', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/-2') + .then(function(result) { + result.text.should.contain('FAIL') + queuePacketStub.callCount.should.eq(0) + }) + + }) + it('starts chlorinator at 150% (should fail)', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/150') + .then(function(result){ + result.text.should.contain('FAIL') + queuePacketStub.callCount.should.eq(0) + }) + + }) + + it('tests /pool API at 75%', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/pool/75') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,0,75,0,0,0,0,0,0,0,0]) + }) + }) + + + it('tests /spa API at 75%', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/spa/75') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,150,0,0,0,0,0,0,0,0,0]) + }) + }) + + it('tests /pool/x/spa/y API at 85%/25%', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/pool/85/spa/25') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,50,85,0,0,0,0,0,0,0,0]) + }) + }) + + + it('tests /superChlorinateHours/x API at 2', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/superChlorinateHours/2') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,0,0,130,0,0,0,0,0,0,0]) + }) + }) + + it('tests /pool/x/spa/y/superChlorinateHours/z API at 2', function() { + + return global.requestPoolDataWithURLAsync('chlorinator/pool/1/spa/2/superChlorinateHours/3') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,4,1,131,0,0,0,0,0,0,0]) + }) + }) + + it('sets chlorinator at 0%', function() { + return global.requestPoolDataWithURLAsync('chlorinator/0') + .then(function(result) { + queuePacketStub.callCount.should.eq(1) + queuePacketStub.args[0][0].should.deep.equal([165,99,16,33,153,10,0,0,0,0,0,0,0,0,0,0]) + }) + }) }); }); + + }) diff --git a/specs/lib/helpers/bootrstrap-config-editor.spec.js b/specs/lib/helpers/bootrstrap-config-editor.spec.js index 68233ba6..2a501576 100644 --- a/specs/lib/helpers/bootrstrap-config-editor.spec.js +++ b/specs/lib/helpers/bootrstrap-config-editor.spec.js @@ -15,7 +15,7 @@ describe('updates/resets bootstrap configClient.json', function() { beforeEach(function () { // sinon = sinon.sinon.create() - loggers = setupLoggerStubOrSpy('spy', 'spy') + loggers = setupLoggerStubOrSpy('stub', 'spy') updateAvailStub = sinon.stub(bottle.container.updateAvailable, 'getResultsAsync').returns(Promise.resolve({})) var origFile = '/specs/assets/bootstrapConfig/configClient.json' @@ -65,7 +65,7 @@ describe('updates/resets bootstrap configClient.json', function() { return fs.readFileAsync(path.join(process.cwd(), '/specs/assets/bootstrapConfig/_configClient.json'), 'utf-8') .then(function(data){ data = JSON.parse(data) - console.log(data) + //console.log(data) for (var key in data.panelState) { if (data.panelState[key].state !== "visible"){ myReject(new Error('resetConfigClient did not reset all value.')) diff --git a/src/lib/comms/server.js b/src/lib/comms/server.js index fa1517ba..6e71216d 100755 --- a/src/lib/comms/server.js +++ b/src/lib/comms/server.js @@ -537,6 +537,7 @@ module.exports = function (container) { res.send(container.intellichem.getCurrentIntellichem()) }) + // This should be deprecated app.get('/chlorinator/:chlorinateLevel', function (req, res) { container.chlorinator.setChlorinatorLevelAsync(parseInt(req.params.chlorinateLevel)) .then(function (response) { @@ -544,6 +545,43 @@ module.exports = function (container) { }) }) + app.get('/chlorinator/pool/:poolChlorinateLevel', function (req, res) { + container.chlorinator.setChlorinatorLevelAsync(parseInt(req.params.poolChlorinateLevel)) + .then(function (response) { + res.send(response) + }) + }) + + app.get('/chlorinator/spa/:spaChlorinateLevel', function (req, res) { + container.chlorinator.setChlorinatorLevelAsync(-1, parseInt(req.params.spaChlorinateLevel)) + .then(function (response) { + res.send(response) + }) + }) + + app.get('/chlorinator/pool/:poolChlorinateLevel/spa/:spaChlorinateLevel', function (req, res) { + container.chlorinator.setChlorinatorLevelAsync(parseInt(req.params.poolChlorinateLevel), parseInt(req.params.spaChlorinateLevel)) + .then(function (response) { + res.send(response) + }) + }) + + + app.get('/chlorinator/superChlorinateHours/:hours', function (req, res) { + container.chlorinator.setChlorinatorLevelAsync(-1, -1, parseInt(req.params.hours)) + .then(function (response) { + res.send(response) + }) + }) + + app.get('/chlorinator/pool/:poolChlorinateLevel/spa/:spaChlorinateLevel/superChlorinateHours/:hours', function (req, res) { + container.chlorinator.setChlorinatorLevelAsync(parseInt(req.params.poolChlorinateLevel), parseInt(req.params.spaChlorinateLevel), parseInt(req.params.hours)) + .then(function (response) { + res.send(response) + }) + }) + + 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))) diff --git a/src/lib/equipment/chlorinator.js b/src/lib/equipment/chlorinator.js index 5518b88d..a006e1cd 100755 --- a/src/lib/equipment/chlorinator.js +++ b/src/lib/equipment/chlorinator.js @@ -225,10 +225,61 @@ module.exports = function (container) { container.chlorinatorController.startChlorinatorController() } + let response = {} + if (currentChlorinatorStatus.controlledBy === 'virtual') { + // check for valid settings to be sent to Chlorinator directly + if (chlorPoolLvl >= 0 && chlorPoolLvl <= 101) { + currentChlorinatorStatus.outputPoolPercent = chlorPoolLvl + } + else { + + response.text = 'FAIL: Request for invalid value for chlorinator (' + chlorPoolLvl + '). Chlorinator will continue to run at previous level (' + currentChlorinatorStatus.outputPoolPercent + ')' + response.status = this.status + response.value = currentChlorinatorStatus.outputPoolPercent + if (container.settings.get('logChlorinator')) { + container.logger.warn(response) + } + return Promise.resolve(response) + } + } + else { + // check for valid values with Intellicom/Intellitouch + if (chlorPoolLvl === 101) { + // assume we will set the superchlorinate for 1 hour + chlorSuperChlorinateHours = 1 + } + else if (chlorPoolLvl >= 0 && chlorPoolLvl <= 100) { + currentChlorinatorStatus.outputPoolPercent = chlorPoolLvl + } + else { + if (!chlorPoolLvl || chlorPoolLvl < -1 || chlorPoolLvl > 101) { + // -1 is valid if we don't want to change the setting. Anything else is invalid and should trigger a fail. + currentChlorinatorStatus.outputPoolPercent = 0; + response.text = 'FAIL: Request for invalid value for chlorinator (' + chlorPoolLvl + '). Chlorinator will continue to run at previous level (' + currentChlorinatorStatus.outputPoolPercent + ')' + response.status = this.status + response.value = currentChlorinatorStatus.outputPoolPercent + response.chlorinator = currentChlorinatorStatus + if (container.settings.get('logChlorinator')) { + container.logger.warn(response) + } + return Promise.resolve(response) + } + + } + } + + if (chlorSpaLvl >= 0 & chlorSpaLvl <= 100) { currentChlorinatorStatus.outputSpaPercent = chlorSpaLvl } - if (chlorSuperChlorinateHours > 0 & chlorSuperChlorinateHours <= 96) { + else { + if (!currentChlorinatorStatus.outputSpaPercent || currentChlorinatorStatus.outputSpaPercent < 0) { + // just in case it isn't set. otherwise we don't want to touch it + currentChlorinatorStatus.outputSpaPercent = 0; + } + } + + if ((chlorSuperChlorinateHours > 0 & chlorSuperChlorinateHours <= 96) || currentChlorinatorStatus.superChlorinateHours>0) { currentChlorinatorStatus.superChlorinate = 1 currentChlorinatorStatus.superChlorinateHours = chlorSuperChlorinateHours } @@ -236,112 +287,94 @@ module.exports = function (container) { currentChlorinatorStatus.superChlorinate = 0 currentChlorinatorStatus.superChlorinateHours = 0 } - currentChlorinatorStatus.outputPoolPercent = chlorPoolLvl - - let response = {} - return Promise.resolve() - .then(function () { - if (container.settings.get('chlorinator.installed')) { - if (currentChlorinatorStatus.controlledBy === 'virtual') { - // chlorinator only has pool setting; send commands directly to chlorinator - if (chlorPoolLvl >= 0 && chlorPoolLvl <= 101) { - return Promise.resolve() - .then(function () { - - if (currentChlorinatorStatus.outputPoolPercent === 0) { - response.text = 'Chlorinator set to off. Chlorinator will be queried every 30 mins for PPM' - response.status = 'off' - response.value = 0 - } else if (currentChlorinatorStatus.outputPoolPercent >= 1 && currentChlorinatorStatus.outputPoolPercent <= 100) { - response.text = 'Chlorinator set to ' + currentChlorinatorStatus.outputPoolPercent + '%.' - response.status = 'on' - response.value = currentChlorinatorStatus.outputPoolPercent - } else if (currentChlorinatorStatus.outputPoolPercent === 101) { - response.text = 'Chlorinator set to super chlorinate' - response.status = 'on' - response.value = currentChlorinatorStatus.outputPoolPercent - } - }) - .then(container.settings.updateChlorinatorDesiredOutputAsync(chlorPoolLvl, currentChlorinatorStatus.outputSpaPercent)) - .then(function () { - container.io.emitToClients('chlorinator') - if (container.chlorinatorController.isChlorinatorTimerRunning()) - container.chlorinatorController.chlorinatorStatusCheck() //This is causing problems if the chlorinator is offline (repeated calls to send status packet.) - else - container.queuePacket.queuePacket([16, 2, 80, 17, chlorPoolLvl]) - if (container.settings.get('logChlorinator')) { - container.logger.info(response) - } - return response - }) - } - else { - response.text = 'FAIL: Request for invalid value for chlorinator (' + chlorPoolLvl + '). Chlorinator will continue to run at previous level (' + currentChlorinatorStatus.outputPoolPercent + ')' - response.status = this.status + if (container.settings.get('chlorinator.installed')) { + if (currentChlorinatorStatus.controlledBy === 'virtual') { + // chlorinator only has one setting; it doesn't know the difference between pool/spa + return Promise.resolve() + .then(function () { + response.chlorinator = currentChlorinatorStatus + if (currentChlorinatorStatus.outputPoolPercent === 0) { + response.text = 'Chlorinator set to off. Chlorinator will be queried every 30 mins for PPM' + response.status = 'off' + response.value = 0 + } else if (currentChlorinatorStatus.outputPoolPercent >= 1 && currentChlorinatorStatus.outputPoolPercent <= 100) { + response.text = 'Chlorinator set to ' + currentChlorinatorStatus.outputPoolPercent + '%.' + response.status = 'on' + response.value = currentChlorinatorStatus.outputPoolPercent + } else if (currentChlorinatorStatus.outputPoolPercent === 101) { + response.text = 'Chlorinator set to super chlorinate' + response.status = 'on' response.value = currentChlorinatorStatus.outputPoolPercent - if (container.settings.get('logChlorinator')) { - container.logger.warn(response) - } - return Promise.resolve(response) } - } - else if (currentChlorinatorStatus.controlledBy === 'intellicom') { - // chlorinator controlled by intellicom; it only has the pool setting + }) + .then(container.settings.updateChlorinatorDesiredOutputAsync(currentChlorinatorStatus.outputPoolPercent, currentChlorinatorStatus.outputSpaPercent)) + .then(function () { + container.io.emitToClients('chlorinator') + if (container.chlorinatorController.isChlorinatorTimerRunning()) + container.chlorinatorController.chlorinatorStatusCheck() //This is causing problems if the chlorinator is offline (repeated calls to send status packet.) + else + container.queuePacket.queuePacket([16, 2, 80, 17, currentChlorinatorStatus.outputPoolPercent]) + if (container.settings.get('logChlorinator')) { + container.logger.info(response) + } + return response + }) + } + + else if (currentChlorinatorStatus.controlledBy === 'intellicom') { + // chlorinator controlled by intellicom; it only has the pool setting - return Promise.resolve() - .then(function () { + return Promise.resolve() + .then(function () { + response.chlorinator = currentChlorinatorStatus + response.text = `Chlorinator set to ${currentChlorinatorStatus.outputPoolPercent} and SuperChlorinate is ${currentChlorinatorStatus.superChlorinate} for ${currentChlorinatorStatus.superChlorinateHours} hours.` + response.status = 'on' + response.value = currentChlorinatorStatus.outputPoolPercent - if (chlorPoolLvl >= 0 && chlorPoolLvl <= 100) { - currentChlorinatorStatus.outputPoolPercent = chlorPoolLvl - response.text = `Chlorinator set to ${currentChlorinatorStatus.outputPoolPercent} and SuperChlorinate is ${currentChlorinatorStatus.superChlorinate} for ${currentChlorinatorStatus.superChlorinateHours} hours.` - response.status = 'on' - response.value = currentChlorinatorStatus.outputPoolPercent - } - }) - .then(container.settings.updateChlorinatorDesiredOutputAsync(currentChlorinatorStatus.outputPoolPercent, currentChlorinatorStatus.outputSpaPercent)) - .then(function () { - container.queuePacket.queuePacket([165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 153, 10, outputSpaByte(), currentChlorinatorStatus.outputPoolPercent, 0, superChlorinateByte(), 0, 0, 0, 0, 0, 0, 0]) + }) + .then(container.settings.updateChlorinatorDesiredOutputAsync(currentChlorinatorStatus.outputPoolPercent, currentChlorinatorStatus.outputSpaPercent)) + .then(function () { + // TODO: Check if the packet is the same on Intellicom (sans Spa setting)... currently it is the same as Intellichlor but the response is formatted differently. + container.queuePacket.queuePacket([165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 153, 10, outputSpaByte(), currentChlorinatorStatus.outputPoolPercent, 0, superChlorinateByte(), 0, 0, 0, 0, 0, 0, 0]) - }) + }) + + if (container.settings.get('logChlorinator')) { + container.logger.info(response) + } + } + else { + // controlled by Intellitouch. We should set both pool and spa levels at the controller + + return Promise.resolve() + .then(function () { + response.chlorinator = currentChlorinatorStatus + response.text = `Chlorinator pool set to ${currentChlorinatorStatus.outputPoolPercent}, spa set to ${currentChlorinatorStatus.outputSpaPercent} and SuperChlorinate is ${currentChlorinatorStatus.superChlorinate} for ${currentChlorinatorStatus.superChlorinateHours} hours.` + response.status = 'on' + response.value = currentChlorinatorStatus.outputPoolPercent + + }) + .then(container.settings.updateChlorinatorDesiredOutputAsync(currentChlorinatorStatus.outputPoolPercent, currentChlorinatorStatus.outputSpaPercent)) + .then(function () { + container.queuePacket.queuePacket([165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 153, 10, outputSpaByte(), currentChlorinatorStatus.outputPoolPercent, superChlorinateByte(), 0, 0, 0, 0, 0, 0, 0]) if (container.settings.get('logChlorinator')) { container.logger.info(response) } - } + return response + }) - else { - // controlled by Intellitouch. We should set both pool and spa levels at the controller - - return Promise.resolve() - .then(function () { - - if (chlorPoolLvl >= 0 && chlorPoolLvl <= 100) { - currentChlorinatorStatus.outputPoolPercent = chlorPoolLvl - response.text = `Chlorinator pool set to ${currentChlorinatorStatus.outputPoolPercent}, spa set to ${currentChlorinatorStatus.outputSpaPercent} and SuperChlorinate is ${currentChlorinatorStatus.superChlorinate} for ${currentChlorinatorStatus.superChlorinateHours} hours.` - response.status = 'on' - response.value = currentChlorinatorStatus.outputPoolPercent - } - }) - .then(container.settings.updateChlorinatorDesiredOutputAsync(currentChlorinatorStatus.outputPoolPercent, currentChlorinatorStatus.outputSpaPercent)) - .then(function () { - container.queuePacket.queuePacket([165, container.intellitouch.getPreambleByte(), 16, container.settings.get('appAddress'), 153, 10, outputSpaByte(), currentChlorinatorStatus.outputPoolPercent, superChlorinateByte(), 0, 0, 0, 0, 0, 0, 0]) - if (container.settings.get('logChlorinator')) { - container.logger.info(response) - } - return Promise.resolve(response) - }) + } + container.io.emitToClients('chlorinator') + } - } - container.io.emitToClients('chlorinator') - } - else { - // chlor NOT installed - response.text = 'FAIL: Chlorinator not enabled. Set Chlorinator=1 in config.json' - return Promise.resolve(response) - } - }) + else { + // chlor NOT installed + response.text = 'FAIL: Chlorinator not enabled. Set Chlorinator=1 in config.json' + return Promise.resolve(response) + } } From f9a2b42f2429aef178f608df92be10d78e9e054d Mon Sep 17 00:00:00 2001 From: tagyoureit Date: Sun, 21 Oct 2018 06:25:11 -0700 Subject: [PATCH 02/10] api updates --- src/scss/Custom.scss | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/scss/Custom.scss diff --git a/src/scss/Custom.scss b/src/scss/Custom.scss new file mode 100644 index 00000000..e69de29b From 1c5ef5b015d62459c464163348362dc06e38de3f Mon Sep 17 00:00:00 2001 From: arrmo Date: Tue, 1 Jan 2019 10:23:02 -0600 Subject: [PATCH 03/10] Update express.static, to address missing cache-control. - Without these changes, max-age=0 (express default), no caching at all! - add socket.io-client, to enable max-age control also --- src/lib/comms/server.js | 11 ++++++----- src/www/bootstrap/index.html | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/comms/server.js b/src/lib/comms/server.js index 6e71216d..69c4dab4 100755 --- a/src/lib/comms/server.js +++ b/src/lib/comms/server.js @@ -309,11 +309,12 @@ module.exports = function (container) { }) // Routing - app.use(express.static(path.join(process.cwd(), 'src/www'))); - app.use('/bootstrap', express.static(path.join(process.cwd(), '/node_modules/bootstrap/dist/'))); - app.use('/jquery', express.static(path.join(process.cwd(), '/node_modules/jquery/'))); - app.use('/jquery-ui', express.static(path.join(process.cwd(), '/node_modules/jquery-ui-dist/'))); - app.use('/jquery-clockpicker', express.static(path.join(process.cwd(), '/node_modules/jquery-clockpicker/dist/'))); + app.use(express.static(path.join(process.cwd(), 'src/www'), { maxAge: '14d' })); + app.use('/bootstrap', express.static(path.join(process.cwd(), '/node_modules/bootstrap/dist/'), { maxAge: '60d' })); + app.use('/jquery', express.static(path.join(process.cwd(), '/node_modules/jquery/'), { maxAge: '60d' })); + app.use('/jquery-ui', express.static(path.join(process.cwd(), '/node_modules/jquery-ui-dist/'), { maxAge: '60d' })); + app.use('/jquery-clockpicker', express.static(path.join(process.cwd(), '/node_modules/jquery-clockpicker/dist/'), { maxAge: '60d' })); + app.use('/socket.io-client', express.static(path.join(process.cwd(), '/node_modules/socket.io-client/dist/'), { maxAge: '60d' })); // disable for security app.disable('x-powered-by') diff --git a/src/www/bootstrap/index.html b/src/www/bootstrap/index.html index eb069a39..de301682 100755 --- a/src/www/bootstrap/index.html +++ b/src/www/bootstrap/index.html @@ -14,7 +14,7 @@ - + From 7391c8098986571e5b9cca5ea296595fa85c0434 Mon Sep 17 00:00:00 2001 From: arrmo Date: Tue, 1 Jan 2019 10:23:44 -0600 Subject: [PATCH 04/10] Update config path, to address issues with bootstrap (in URL) --- src/www/bootstrap/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/www/bootstrap/main.js b/src/www/bootstrap/main.js index ee3c080c..c59a3ba8 100755 --- a/src/www/bootstrap/main.js +++ b/src/www/bootstrap/main.js @@ -2182,7 +2182,7 @@ function lastUpdate(reset) { } var loadAppSettings = function () { - $.getJSON('/config', function (appConfig) { + $.getJSON('../config', function (appConfig) { appParams = appConfig.config console.log(appParams) if (appParams.systemReady) { From 6938c0fd380d9a04ade140dee719bf9836077aa9 Mon Sep 17 00:00:00 2001 From: tagyoureit Date: Thu, 3 Jan 2019 13:23:19 -0800 Subject: [PATCH 05/10] session changes --- .snyk | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.snyk b/.snyk index 6a931b1e..461e1cd0 100644 --- a/.snyk +++ b/.snyk @@ -125,7 +125,7 @@ ignore: expires: '2018-05-27T15:05:54.491Z' request-promise > request-promise-core > lodash: reason: None given - expires: '2018-11-13T23:00:17.087Z' + expires: '2019-01-28T22:22:52.646Z' - node-ssdp > async > lodash: reason: None given expires: '2018-04-01T03:18:10.568Z' @@ -150,6 +150,9 @@ ignore: - request-promise > request-promise-core > lodash: reason: None given expires: '2018-08-16T06:24:46.574Z' + - request-promise > request-promise-core > lodash: + reason: None given + expires: '2018-11-13T23:00:17.087Z' 'npm:hoek:20180212': - request > hawk > hoek: reason: None given @@ -173,7 +176,7 @@ ignore: 'npm:extend:20180424': - node-ssdp > extend: reason: None given - expires: '2018-11-13T23:00:17.087Z' + expires: '2019-01-28T22:22:52.645Z' - request > extend: reason: None given expires: '2018-08-23T22:44:40.819Z' @@ -199,7 +202,7 @@ ignore: 'npm:chownr:20180731': - serialport > prebuild-install > tar-fs > chownr: reason: None given - expires: '2018-10-05T03:45:29.603Z' + expires: '2019-01-28T22:18:22.043Z' - chokidar > fsevents > node-pre-gyp > tar > chownr: reason: None given expires: '2018-09-01T14:44:44.769Z' From 7c42fd08029445d0ac26254342368fc0cf28fa93 Mon Sep 17 00:00:00 2001 From: tagyoureit Date: Thu, 3 Jan 2019 13:41:30 -0800 Subject: [PATCH 06/10] session changes --- README.md | 4 +- package.json | 13 +- specs/assets/config/config.json | 24 +-- specs/assets/replays/1/config.json | 2 +- specs/assets/replays/1/packetCapture.log | 18 +- specs/index.spec.js | 15 +- src/etc/settings.js | 8 +- src/lib/comms/server.js | 87 +++++----- src/lib/comms/socketio-helper.js | 4 +- src/lib/equipment/circuit.js | 3 + src/lib/logger/winston-3.js | 16 +- src/lib/logger/winston-helper.js | 200 ++++++++++++----------- src/www/bootstrap/index.html | 4 +- src/www/bootstrap/main.js | 2 +- src/www/public/replay.html | 2 +- sysDefault.json | 2 +- 16 files changed, 227 insertions(+), 177 deletions(-) diff --git a/README.md b/README.md index 8eba71a3..9ce5532b 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# nodejs-poolController - Version 5.2.0 +# nodejs-poolController - Version 5.3.01 [![Join the chat at https://gitter.im/nodejs-poolController/Lobby](https://badges.gitter.im/nodejs-poolController/Lobby.svg)](https://gitter.im/nodejs-poolController/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/tagyoureit/nodejs-poolController.svg?branch=master)](https://travis-ci.org/tagyoureit/nodejs-poolController) [![Coverage Status](https://coveralls.io/repos/github/tagyoureit/nodejs-poolController/badge.svg?branch=master)](https://coveralls.io/github/tagyoureit/nodejs-poolController?branch=master) [![Known Vulnerabilities](https://snyk.io/test/github/tagyoureit/nodejs-poolcontroller/badge.svg)](https://snyk.io/test/github/tagyoureit/nodejs-poolcontroller) [Full Changelog](#module_nodejs-poolController--changelog) -### 5.2.0 +### 5.3.01 1. Node 6+ is supported. This app no longer supports Node 4. 1. Update of modules. Make sure to run `npm i` or `npm upgrade` to get the latest. 1. Much better support of multiple Intellibrite controllers. We can read both controllers now. There are still some issues with sending changes and help is needed to debug these. diff --git a/package.json b/package.json index c888ef38..6e6dd81e 100644 --- a/package.json +++ b/package.json @@ -41,16 +41,17 @@ }, "homepage": "https://github.com/tagyoureit/nodejs-poolController#readme", "dependencies": { - "bluebird": "^3.5.0", - "bootstrap": "^3.3.7", + "bluebird": "^3.5.3", + "bootstrap": "^3.4.0", "bottlejs": "^1.7.1", "dateformat": "^3.0.3", "deep-diff": "^1.0.1", "dequeue": "^1.0.5", "events": "^3.0.0", - "express": "^4.16.3", + "express": "^4.16.4", "getmac": "^1.4.3", "http-auth": "^3.1.3", + "http-shutdown": "^1.2.0", "influx": "^5.0.7", "ip": "^1.1.5", "jquery": "^3.2.1", @@ -73,12 +74,12 @@ "yargs-parser": "^10.1.0" }, "devDependencies": { - "chai": "^4.0.1", - "coveralls": "^3.0.1", + "chai": "^4.2.0", + "coveralls": "^3.0.2", "grunt": "^1.0.3", "grunt-banner": "^0.6.0", "istanbul": "^0.4.5", - "jshint": "^2.9.4", + "jshint": "^2.9.7", "mocha": "^5.2.0", "nock": "^9.3.3", "sinon": "^6.1.4", diff --git a/specs/assets/config/config.json b/specs/assets/config/config.json index bbf0b517..3570701f 100644 --- a/specs/assets/config/config.json +++ b/specs/assets/config/config.json @@ -1,5 +1,16 @@ { - "version": "5.2.0", + "meta": { + "notifications": { + "version": { + "remote": { + "version": "4.1.200", + "tag_name": "v4.1.200", + "dismissUntilNextRemoteVersionBump": false + } + } + } + }, + "version": "5.3.01", "equipment": { "controller": { "intellicom": { @@ -141,16 +152,5 @@ } } }, - "meta": { - "notifications": { - "version": { - "remote": { - "version": "4.0.0", - "tag_name": "v4.0.0", - "dismissUntilNextRemoteVersionBump": false - } - } - } - }, "integrations": {} } \ No newline at end of file diff --git a/specs/assets/replays/1/config.json b/specs/assets/replays/1/config.json index 6fb0978f..de500b36 100644 --- a/specs/assets/replays/1/config.json +++ b/specs/assets/replays/1/config.json @@ -1,5 +1,5 @@ { - "version": "5.2.0", + "version": "5.3.01", "equipment": { "controller": { "intellicom": { diff --git a/specs/assets/replays/1/packetCapture.log b/specs/assets/replays/1/packetCapture.log index efa6177c..59a97a35 100644 --- a/specs/assets/replays/1/packetCapture.log +++ b/specs/assets/replays/1/packetCapture.log @@ -9,11 +9,11 @@ 15:09:47.871 INFO Settings: ******************************* - Version: 5.2.0 + Version: 5.3.01 Configuration file name: config.json *******************************{ - "version": "5.2.0", + "version": "5.3.01", "equipment": { "controller": { "intellicom": { @@ -326,7 +326,7 @@ Configuration file name: config.json 15:09:47.974 SILLY New SOCKET.IO Client connected, rN_W8cd9yhLJ_2KjAAAA 15:09:47.975 SILLY Outputting socket all 15:09:47.975 SILLY Socket.IO checking if we need to output updateAvail: false (will send if false) -15:09:47.979 SILLY Socket.IO NOT outputting updateAvail because it is missing the result string: {"local":{"version":"5.2.0"}} +15:09:47.979 SILLY Socket.IO NOT outputting updateAvail because it is missing the result string: {"local":{"version":"5.3.01"}} 15:09:47.987 SILLY New https server connection ::ffff:127.0.0.1 15:09:47.988 SILLY New https server connection ::ffff:127.0.0.1 15:09:48.000 VERBOSE Sending NR POST @@ -343,12 +343,12 @@ Configuration file name: config.json 15:09:48.134 SILLY updateAvail.compareLocaltoSavedLocal: (current) published release (5.1.1) to cached/last published config.json version (5.1.1) 15:09:48.134 SILLY updateAvail: no change in current remote version compared to local cached config.json version of app 15:09:48.136 SILLY updateAvail: versions discovered: - {"local":{"version":"5.2.0"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"}} -15:09:48.137 SILLY updateAvail: local ver: 5.2.0 latest published release ver: 5.1.1 -15:09:48.139 INFO You are running a newer release (5.2.0) than the published release (5.1.1) + {"local":{"version":"5.3.01"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"}} +15:09:48.137 SILLY updateAvail: local ver: 5.3.01 latest published release ver: 5.1.1 +15:09:48.139 INFO You are running a newer release (5.3.01) than the published release (5.1.1) 15:09:48.140 SILLY Outputting socket updateAvailable 15:09:48.141 SILLY Socket.IO checking if we need to output updateAvail: false (will send if false) -15:09:48.141 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.2.0"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.2.0) than the published release (5.1.1)"} +15:09:48.141 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.3.01"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.3.01) than the published release (5.1.1)"} 15:09:48.142 SILLY updateAvail: finished successfully 15:09:48.163 VERBOSE ISY: Response from ISY: [object Object] @@ -1860,7 +1860,7 @@ Full queue: [[255,0,255,165,1,16,33,232,1,0,1,192], [255,0,255,165,1,16,33,219,1,1,1,180]] 15:09:49.849 SILLY Outputting socket all 15:09:49.849 SILLY Socket.IO checking if we need to output updateAvail: false (will send if false) -15:09:49.850 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.2.0"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.2.0) than the published release (5.1.1)"} +15:09:49.850 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.3.01"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.3.01) than the published release (5.1.1)"} 15:09:49.851 DEBUG postWritePacketHelper: First time writing packet. {"counter":1,"packetWrittenAt":5,"msgWrote":[255,0,255,165,1,16,33,232,1,0,1,192]} 15:09:49.851 DEBUG writePacketHelper: Setting timeout to write next packet (will call preWritePacketHelper()) @@ -25698,7 +25698,7 @@ container.packetBuffer.length()(0) === 0 && bufferToProcess.length(0) > 0: false 15:10:49.881 SILLY New http server connection ::ffff:192.168.1.12 15:10:49.885 SILLY Outputting socket all 15:10:49.885 SILLY Socket.IO checking if we need to output updateAvail: false (will send if false) -15:10:49.897 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.2.0"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.2.0) than the published release (5.1.1)"} +15:10:49.897 SILLY Socket.IO outputting updateAvail: {"local":{"version":"5.3.01"},"remote":{"tag_name":"v5.1.1","version":"5.1.1"},"result":"newer","resultStr":"You are running a newer release (5.3.01) than the published release (5.1.1)"} 15:10:49.902 DEBUG NR Socket: Will not send circuit2status because the value has not changed (1) 15:10:49.902 DEBUG NR Socket: Will not send circuit3status because the value has not changed (0) 15:10:49.902 DEBUG NR Socket: Will not send circuit4status because the value has not changed (0) diff --git a/specs/index.spec.js b/specs/index.spec.js index 708a5e22..98faf2c1 100755 --- a/specs/index.spec.js +++ b/specs/index.spec.js @@ -10,12 +10,23 @@ describe('nodejs-poolController', function () { describe('Loads/checks for a valid configuration file', function () { before(function () { - + // initialize winston once with defaults + return Promise.resolve() + .then(function(){ + bottle.container.logger.init() + }) + .delay(50) + .then(function(){ + console.log("done") + bottle.container.logger.info("test logger") + bottle.container.logger.warn("test warn") + bottle.container.logger.error("test error") + }) }) beforeEach(function () { - + updateAvailStub = sinon.stub(bottle.container.updateAvailable, 'getResultsAsync').returns(Promise.resolve({})) if (global.logInitAndStop) { loggerInfoStub = sinon.spy(bottle.container.logger, 'info') diff --git a/src/etc/settings.js b/src/etc/settings.js index 257b1146..c7660ada 100755 --- a/src/etc/settings.js +++ b/src/etc/settings.js @@ -24,7 +24,8 @@ module.exports = function (container) { pfs = Promise.promisifyAll(container.fs), diff = container.deepdiff.diff, observableDiff = container.deepdiff.observableDiff, - applyChange = container.deepdiff.applyChange; + applyChange = container.deepdiff.applyChange, + ready = false var argv = require('yargs-parser')(process.argv.slice(2), opts = {boolean: ['capturePackets', 'suppressWrite']}) @@ -605,6 +606,7 @@ module.exports = function (container) { }) .finally(function () { container.logger.debug('Finished settings.loadAsync()') + ready = true; }) } @@ -870,12 +872,16 @@ module.exports = function (container) { }) } + var isReady = function(){ + return ready; + } /* istanbul ignore next */ if (container.logModuleLoading) console.log('Loaded: settings.js') return { + isReady: isReady, loadAsync: loadAsync, has: has, get: get, diff --git a/src/lib/comms/server.js b/src/lib/comms/server.js index fa1517ba..8c893684 100755 --- a/src/lib/comms/server.js +++ b/src/lib/comms/server.js @@ -22,21 +22,22 @@ module.exports = function (container) { if (container.logModuleLoading) container.logger.info('Loading: server.js'); - var express = container.express, servers = {http: {}, https: {}, ssdp: {}, mdns: {}} + var express = container.express, servers = { http: {}, https: {}, ssdp: {}, mdns: {} } var path = require('path').posix; - var defaultPort = {http: 3000, https: 3001} + var defaultPort = { http: 3000, https: 3001 } var mdnsEmitter = new container.events.EventEmitter(); - var mdns = {query: [], answers: []} + var mdns = { query: [], answers: [] } var emitter = new container.events.EventEmitter(); + var httpShutdown = require('http-shutdown') function startServerAsync(type) { return new Promise(function (resolve, reject) { if (container.settings.get(type + 'Enabled')) { - // srvDesired += container.settings.get(type + 'Enabled'); servers[type].app = express(); + servers[type].port = container.settings.get(type + 'ExpressPort') || defaultPort[type]; servers[type].server = undefined; @@ -79,25 +80,19 @@ module.exports = function (container) { else configExpressServer(servers[type].app, express); + // And Start Listening - servers[type].server.listen(servers[type].port, function () { + servers[type].server = httpShutdown(servers[type].server.listen(servers[type].port, function () { container.logger.verbose('Express Server ' + type + ' listening at port %d', servers[type].port); container.io.init(servers[type].server, type) resolve('Server ' + type + ' started.'); - }); + })); servers[type].server.on('error', function (e) { container.logger.error('error from ' + type + ':', e) console.error(e) reject(e) }); - - // based on https://stackoverflow.com/questions/43003870/how-do-i-shut-down-my-express-server-gracefully-when-its-process-is-killed - servers[type].connections = [] - servers[type].server.on('connection', function (connection) { - container.logger.silly('New %s server connection', type, connection.remoteAddress) - servers[type].connections.push(connection) - }) } else { var res = 'Not starting ' + type + ' server.' @@ -179,11 +174,11 @@ module.exports = function (container) { servers['mdns'].isRunning = 1 servers['mdns'].query({ - questions: [{ - name: 'myserver.local', - type: 'A' - }] - }, + questions: [{ + name: 'myserver.local', + type: 'A' + }] + }, resolve('Server MDNS started.')) @@ -244,17 +239,14 @@ module.exports = function (container) { // advertise shutting down and stop listening } else if (servers[type].server !== undefined) { - container.logger.silly('%s has %s connections in it: ', type, servers[type].connections.length) container.io.stop(type) servers[type].server.close(function () { container.logger.verbose('Express Server ' + type + ' closed'); resolve(); }); - servers[type].connections.forEach(function (conn) { - container.logger.silly('Destroying %s connection from %s', type, conn.remoteAddress) - conn.end() - conn.destroy() - }) + + // graceful shutdown thanks to http-shutdown + servers[type].server.shutdown() } else { container.logger.debug('Trying to close ' + type + ', but it is not running.'); resolve(); //it's ok if it isn't running, so resolve the promise. @@ -289,9 +281,9 @@ module.exports = function (container) { var customRoutes = require(path.join(process.cwd(), 'src/integrations/customExpressRoutes')); customRoutes.init(app); - // Middleware to capture requests to log + // Middleware app.use(function (req, res, next) { - + // Middleware to capture requests to log var reqType = req.originalUrl.split('/') if (!['bootstrap', 'assets', 'poolController', 'public'].includes(reqType[1])) { @@ -305,15 +297,34 @@ module.exports = function (container) { }) } } + + /* console.log('looking at session: ', req.sessionID) + //store session in memory store + if (req.session.views) { + req.session.views++ + res.setHeader('Content-Type', 'text/html') + res.write('

views: ' + req.session.views + '

') + res.write('

expires in: ' + (req.session.cookie.maxAge / 1000) + 's

') + res.write('

json session:
' + JSON.stringify(req.session) ) + res.end() + } else { + req.session.views = 1 + res.end('welcome to the session demo. refresh!') + } + + //output request variables + console.log(`Request session: ${req}`) + */ next() }) // Routing - app.use(express.static(path.join(process.cwd(), 'src/www'))); - app.use('/bootstrap', express.static(path.join(process.cwd(), '/node_modules/bootstrap/dist/'))); - app.use('/jquery', express.static(path.join(process.cwd(), '/node_modules/jquery/'))); - app.use('/jquery-ui', express.static(path.join(process.cwd(), '/node_modules/jquery-ui-dist/'))); - app.use('/jquery-clockpicker', express.static(path.join(process.cwd(), '/node_modules/jquery-clockpicker/dist/'))); + app.use(express.static(path.join(process.cwd(), 'src/www'), { maxAge: '14d' })); + app.use('/bootstrap', express.static(path.join(process.cwd(), '/node_modules/bootstrap/dist/'), { maxAge: '60d' })); + app.use('/jquery', express.static(path.join(process.cwd(), '/node_modules/jquery/'), { maxAge: '60d' })); + app.use('/jquery-ui', express.static(path.join(process.cwd(), '/node_modules/jquery-ui-dist/'), { maxAge: '60d' })); + app.use('/jquery-clockpicker', express.static(path.join(process.cwd(), '/node_modules/jquery-clockpicker/dist/'), { maxAge: '60d' })); + app.use('/socket.io-client', express.static(path.join(process.cwd(), '/node_modules/socket.io-client/dist/'), { maxAge: '60d' })); // disable for security app.disable('x-powered-by') @@ -322,6 +333,8 @@ module.exports = function (container) { res.send(container.status.getCurrentStatus()) })*/ + + app.get('/all', function (req, res) { res.send(container.helpers.allEquipmentInOneJSON()); container.io.emitToClients('all'); @@ -471,7 +484,7 @@ module.exports = function (container) { }) -//TODO: do we need DOW in these??? + //TODO: do we need DOW in these??? app.get('/datetime/set/time/:hh/:mm/date/:dow/:dd/:mon/:yy/:dst', function (req, res) { var hour = parseInt(req.params.hh) var min = parseInt(req.params.mm) @@ -862,7 +875,7 @@ module.exports = function (container) { res.send(response) }) -//#11 Run pump at GPM for an indefinite duration + //#11 Run pump at GPM for an indefinite duration app.get('/pumpCommand/run/pump/:pump/gpm/:gpm', function (req, res) { var pump = parseInt(req.params.pump) var gpm = parseInt(req.params.gpm) @@ -876,7 +889,7 @@ module.exports = function (container) { res.send(response) }) -//#12 Run pump at GPM for specified duration + //#12 Run pump at GPM for specified duration app.get('/pumpCommand/run/pump/:pump/gpm/:gpm/duration/:duration', function (req, res) { var pump = parseInt(req.params.pump) var gpm = parseInt(req.params.gpm) @@ -890,7 +903,7 @@ module.exports = function (container) { res.send(response) }) -//#13 Save program to pump + //#13 Save program to pump app.get('/pumpCommand/save/pump/:pump/program/:program/gpm/:speed', function (req, res) { var pump = parseInt(req.params.pump) var program = parseInt(req.params.program) @@ -911,7 +924,7 @@ module.exports = function (container) { } }) -//#14 Save and run program for indefinite duration + //#14 Save and run program for indefinite duration app.get('/pumpCommand/saverun/pump/:pump/program/:program/gpm/:speed', function (req, res) { var pump = parseInt(req.params.pump) var program = parseInt(req.params.program) @@ -930,7 +943,7 @@ module.exports = function (container) { } }) -//#15 Save and run program for specified duration + //#15 Save and run program for specified duration app.get('/pumpCommand/saverun/pump/:pump/program/:program/gpm/:speed/duration/:duration', function (req, res) { var pump = parseInt(req.params.pump) diff --git a/src/lib/comms/socketio-helper.js b/src/lib/comms/socketio-helper.js index 70278cb9..40bcadcb 100755 --- a/src/lib/comms/socketio-helper.js +++ b/src/lib/comms/socketio-helper.js @@ -197,7 +197,7 @@ module.exports = function (container) { } else { try { - container.logger.debug('Stopping Socket IO Server') + container.logger.debug(`Stopping Socket IO ${type} Server`) while (socketList[type].length !== 0) { container.logger.silly('total sockets in list: ', socketList[type].length) @@ -212,7 +212,7 @@ module.exports = function (container) { if (typeof io[type].close === 'function') { io[type].close(); io[type + 'Enabled'] = 0 - container.logger.debug('Socket IO Server closed') + container.logger.debug(`Socket IO ${type} Server closed`) } else { container.logger.silly('Trying to close IO server, but already closed.') diff --git a/src/lib/equipment/circuit.js b/src/lib/equipment/circuit.js index a763c191..67b450dd 100755 --- a/src/lib/equipment/circuit.js +++ b/src/lib/equipment/circuit.js @@ -949,6 +949,9 @@ module.exports = function (container) { container.logger.info(retStr) } + // assign color to circuit object + assignControllerLightColor(mode,0,'API') + return retStr } diff --git a/src/lib/logger/winston-3.js b/src/lib/logger/winston-3.js index a27559ed..863ab368 100644 --- a/src/lib/logger/winston-3.js +++ b/src/lib/logger/winston-3.js @@ -174,13 +174,13 @@ module.exports = function (container) { packetLogger.info(msg) } - function error(msg) { - if (logger === undefined) { - console.log('Error ', arguments) - } - else - logger.error.apply(this, arguments) - } + // function error(msg) { + // if (logger === undefined) { + // console.log('Error ', arguments) + // } + // else + // logger.error.apply(this, arguments) + // } function warn(msg) { if (logger === undefined) { @@ -251,7 +251,7 @@ module.exports = function (container) { return { init: init, - error: error, + // error: error, warn: warn, silly: silly, debug: debug, diff --git a/src/lib/logger/winston-helper.js b/src/lib/logger/winston-helper.js index bf4a27cc..56dc3947 100755 --- a/src/lib/logger/winston-helper.js +++ b/src/lib/logger/winston-helper.js @@ -19,7 +19,7 @@ // dateFormat = require('dateformat'), // util = require('util') -module.exports = function(container) { +module.exports = function (container) { /*istanbul ignore next */ if (container.logModuleLoading) console.log('Loading: winston-helper.js') @@ -31,129 +31,145 @@ module.exports = function(container) { var logger, packetLogger - var init = function(){ + var init = function () { - logger = new (winston.Logger)({ - transports: [ - new (winston.transports.Console)({ + if (container.settings.isReady()) { + console.log('Winstion initializing with customized settings.') + + logger = new (winston.Logger)({ + transports: [ + new (winston.transports.Console)({ + timestamp: function () { + return dateFormat(Date.now(), "HH:MM:ss.l"); + }, + formatter: function (options) { + // Return string will be passed to logger. + return options.timestamp() + ' ' + winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' + (undefined !== options.message ? options.message : '') + + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : ''); + }, + colorize: true, + level: container.settings.get('logLevel') || 'info', + stderrLevels: [], + handleExceptions: true, + humanReadableUnhandledException: true + }) + ] + }); + + var fileLogEnable = 0 + if (container.settings.has('fileLog.enable')) { + fileLogEnable = container.settings.get('fileLog.enable') + } + if (fileLogEnable) { + var _level = container.settings.get('fileLog.fileLogLevel') + var file = path.join(process.cwd(), container.settings.get('fileLog.fileName')) + + var options = { timestamp: function () { return dateFormat(Date.now(), "HH:MM:ss.l"); }, + level: _level, formatter: function (options) { // Return string will be passed to logger. - return options.timestamp() + ' ' + winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' + (undefined !== options.message ? options.message : '') + + return options.timestamp() + ' ' + options.level.toUpperCase() + ' ' + (undefined !== options.message ? options.message : '') + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : ''); }, - colorize: true, - level: container.settings.get('logLevel') || 'info', - stderrLevels: [], - handleExceptions: true, - humanReadableUnhandledException: true - }) - ] - }); - var fileLogEnable = 0 - if (container.settings.has('fileLog.enable')){ - fileLogEnable = container.settings.get('fileLog.enable') - } - if (fileLogEnable) { - var _level = container.settings.get('fileLog.fileLogLevel') - var file = path.join(process.cwd(), container.settings.get('fileLog.fileName')) - - var options = { - timestamp: function () { - return dateFormat(Date.now(), "HH:MM:ss.l"); - }, - level: _level, - formatter: function (options) { - // Return string will be passed to logger. - return options.timestamp() + ' ' + options.level.toUpperCase() + ' ' + (undefined !== options.message ? options.message : '') + - (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : ''); - }, - filename: file, - colorize: false, - json: false + filename: file, + colorize: false, + json: false + } + logger.add(winston.transports.File, options) + } - logger.add(winston.transports.File, options) - } + + if (container.settings.get('capturePackets')) { + packetLogger = new (winston.Logger)({ + transports: [ + new (winston.transports.File)({ + formatter: function (options) { + // Return string will be passed to logger. + return JSON.stringify(options.message); + }, + colorize: false, + level: 'info', + handleExceptions: true, + humanReadableUnhandledException: false, + json: true, + filename: path.join(process.cwd(), 'replay/packetCapture.json') + }) + ] + }); + } - if (container.settings.get('capturePackets')) - { - packetLogger = new (winston.Logger)({ + } + else { + console.log('Winstion initializing with default settings.') + // initialize winston with defaults + logger = new (winston.Logger)({ transports: [ - new (winston.transports.File)({ + new (winston.transports.Console)({ + timestamp: function () { + return dateFormat(Date.now(), "HH:MM:ss.l"); + }, formatter: function (options) { // Return string will be passed to logger. - return JSON.stringify(options.message); + return options.timestamp() + ' ' + winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' + (undefined !== options.message ? options.message : '') + + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : ''); }, - colorize: false, + colorize: true, level: 'info', + stderrLevels: [], handleExceptions: true, - humanReadableUnhandledException: false, - json: true, - filename: path.join(process.cwd(), 'replay/packetCapture.json') + humanReadableUnhandledException: true }) ] }); } - - } - function error(msg, ...args){ - // console.log('error msg: ', msg) - // console.log('error args... ', args) + function error(msg){ + if (logger===undefined){ + init() + } + logger.error.apply(this, arguments) + } + + function warn(msg){ if (logger===undefined){ - console.log('Error ', args) - } - else - // is this right? - logger.error.apply(this, args) - } - - function warn(msg){ - if (logger===undefined){ - console.log('Warn ',arguments) + init() } - else - logger.warn.apply(this, arguments) - } - function silly(msg){ + logger.warn.apply(this, arguments) + } + function silly(msg){ if (logger===undefined){ init() - - logger.silly.apply(this, arguments) } - else - logger.silly.apply(this, arguments) - } - function debug(msg){ + logger.silly.apply(this, arguments) + } + function debug(msg){ if (logger===undefined){ - console.log('Debug ',arguments) + init() } - else - logger.debug.apply(this, arguments) - } - function verbose(msg){ + logger.debug.apply(this, arguments) + } + function verbose(msg){ if (logger===undefined){ - console.log('Verbose ',arguments) + init() } - else - logger.verbose.apply(this, arguments) - } - function info(msg){ + logger.verbose.apply(this, arguments) + } + function info(msg){ if (logger===undefined){ - console.log('Info ',arguments) + init() } - else - logger.info.apply(this, arguments) - } + logger.info.apply(this, arguments) + } - function changeLevel(transport, lvl){ + function changeLevel(transport, lvl) { //when testing, we may call this first - if (logger===undefined) { + if (logger === undefined) { //init() //calling init here may lead to retrieving settings which we don't have yet... so print a message and move on console.log('Error trying to call changeLevel when winston is not yet initialized') @@ -162,12 +178,12 @@ module.exports = function(container) { logger.transports[transport].level = lvl; } } - function add(transport, options){ + function add(transport, options) { logger.add(transport, options) } - function packet(msg){ - packetLogger.info.apply(this, arguments) + function packet(msg) { + packetLogger.info.apply(this, arguments) } /*istanbul ignore next */ @@ -175,13 +191,13 @@ module.exports = function(container) { logger.info('Loaded: winston-helper.js') return { - init:init, + init: init, error: error, warn: warn, silly: silly, debug: debug, verbose: verbose, - info: info, + info: info, changeLevel: changeLevel, add: add, packet: packet diff --git a/src/www/bootstrap/index.html b/src/www/bootstrap/index.html index eb069a39..a48652ec 100755 --- a/src/www/bootstrap/index.html +++ b/src/www/bootstrap/index.html @@ -14,7 +14,7 @@ - + @@ -73,7 +73,7 @@ - + diff --git a/src/www/bootstrap/main.js b/src/www/bootstrap/main.js index ee3c080c..c59a3ba8 100755 --- a/src/www/bootstrap/main.js +++ b/src/www/bootstrap/main.js @@ -2182,7 +2182,7 @@ function lastUpdate(reset) { } var loadAppSettings = function () { - $.getJSON('/config', function (appConfig) { + $.getJSON('../config', function (appConfig) { appParams = appConfig.config console.log(appParams) if (appParams.systemReady) { diff --git a/src/www/public/replay.html b/src/www/public/replay.html index fee6ef6d..2b2dfa57 100755 --- a/src/www/public/replay.html +++ b/src/www/public/replay.html @@ -17,7 +17,7 @@ - +
5.2.0 release notes5.3.01 release notes