Important note-downgrade from the latest version of node to use dependencies in this project. A new version of this app is currently under development.
- #134
- #132
- Fix for #106
- Fix for "Error 60" messages
- Improved caching of files on browsers. Thanks @arrmo! Now files will be loaded once in the browser and kept in cache instead of reloaded each time.
- Improved handling of sessions and graceful closing of the HTTP(s) servers.
nodejs-poolController. An application to control pool equipment. Copyright (C) 2016, 2017. Russell Goldin, tagyoureit. [email protected]
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.
nodejs-poolController is an application to communicate and control your Pentair compatible pool equipment.
Want to include a low cost controller for your pool? Want a web interface for your system? Want to turn your pumps on remotely? Want to have your home automation system talk to your pool? Want to control your pumps or chlorinator without a pool controller?
Controllers: Intellitouch, EasyTouch, Intermatic, SunTouch, IntellicomII Pumps: Intelliflow, older models Chlorinator: Intellichlor, Aqua-Rite and OEM brands Home Automation: ISY. (Soon to include Siri, Echo, more?)
Extend nodejs-Poolcontroller with these additional integration points
- outputSocketToConsoleExample A sample included with the code to demonstrate correct usage.
- Homebridge/Siri by @leftyfl1p
- SmartThings by @bsileo, @johnny2678, @donkarnag, @arrmo
- Another SmartThings Controller by @dhop90
- ISY included with this app. Original credit to @blueman2, enhancements by @mayermd
- ISY Polyglot NodeServer created by @brianmtreese
- MQTT created by @crsherman.
- Hubitat by @bsileo
This code requires a physical RS485 adapter to work.
If you don't know anything about NodeJS, these directions might be helpful.
- Install Nodejs. (https://nodejs.org/en/download/)
- Update NPM (https://docs.npmjs.com/getting-started/installing-node).
- Download the latest code release
OR
clone with
git clone [email protected]:tagyoureit/nodejs-poolController.git
- Unzip into nodejs-poolController.
- Run 'npm install' in the new folder (where package.json exists). This will automatically install all the dependencies (serial-port, express, sockets.io, etc).
- Run the app by calling 'npm start'* (again, in the root directory). It should now run properly.
- to run with a specific configuration, run
node index.js arg
where arg is the name of your current config file. egnpm start configCustomized.json
. By default, the app will loadconfig.json
.
- to run with a specific configuration, run
Universal notes
- For precaution, make a backup copy of your
config.json
or customized configuration file. New in the 5.0.0 release is that the app will automatically upgrade this file.
Git clone method - Harder way, but you can create PR's and help with development
git clone [email protected]:tagyoureit/nodejs-poolController.git
(clone the repo if you are starting fresh)1.Will update when there is a new Dev branch.git checkout 5.0.0
(switch to 5.0.0 branch - Development branch only)git pull
(anytime you want to grab the latest code)npm update
(update dependencies)
Download method - Easier way
- Download the latest release from the release page or branch page big
Clone or download v
button - Unzip and overwrite your existing directory*. See note above about
config.json
file.
For support you can open a github issue, for discussions, designs, and clarifications, we recommend you join our Gitter Chat room.
- A slick Bootstrap interface by @arrmo. Set variable:
"expressDir": "/bootstrap"
- A boring, basic, functional interface. Set variable:
"expressDir": "/public"
To choose, set theexpressDir
variable in the 'config.json'. Load both interfaces fromhttp://localhost:3000/index.html
Control standalone pumps:See #150.http://_your_machine_name_:3000/pump.html
- Listen for specific messages:
http://_your_machine_name_:3000/debug.html
- Send a message on the serial bus:
http://<server>:3000/public/send_packet.html
The web UI will dynamically load as the information is received from the app. Yes, Socket.io, we love you! Full loading may take 20-30 seconds depending on your equipment setup.
You can also call REST URI's like:
- Get circuit status: /circuit/# to get the status of circuit '#'
- Toggle circuit status: /circuit/#/toggle to get the toggle circuit '#'
Get system status: /statusDepricated.- Get schedules: /schedule
- Get pump status: /pump
- Get all equipment as one JSON: /all
- Set spa heat setpoint: /spaheat/setpoint/#
- Set spa heat mode: /spaheat/mode/# (0=off, 1=heater, 2=solar pref, 3=solar only)
- Set pool heat setpoint: /poolheat/setpoint/#
- Set pool heat mode: /poolheat/mode/# (0=off, 1=heater, 2=solar pref, 3=solar only)
- Run pumps in stand-alone mode
- Cancel delay: /cancelDelay
You can use Sockets.IO (see the "basic UI" example). Valid sockets:
Direction | Socket | API | Description |
---|---|---|---|
To app | echo(equipment) |
no api | test socket |
To client | echo |
outputs the incoming echo (for testing) | |
To app | search(mode, src, dest, action) |
Searches for specific packets that match all four bytes and outputs to the socket searchResults |
|
To client | searchResults |
outputs packets that match the search socket |
|
To app | sendPacket(packet) |
Send a packet as an array of values [xx,yy,zz...] to the bus. Pump and Controller packets should start with [DEST, SRC,...]. Chlorinator packets should start with [16,2...] |
|
To client | all |
outputs an object with all equipment in one JSON | |
To app | all |
/all |
sends all information in one socket |
To client | time |
/time |
|
To app | setDateTime(hour, min, dow*, day, mon, yy, dst) |
/datetime/set/time/{hour}/{min}/{dow}/{day}/{mon}/{year}/{dst} |
|
To app | updateVersionNotification(bool) |
true = do not send the updateAvailable socket until the next version is available. false = send updateAvailable everytime. | |
To client | updateAvailable |
outputs an object with current running version vs latest published release on GitHub (local is the running app, remote is the GitHub version) | |
To client | valve |
outputs an object with the valve information | |
To client | UOM |
outputs the unit of measure (C or F) |
Direction | Socket | API | Description |
---|---|---|---|
To client | circuit |
outputs an object of circuits and their status | |
To app | /circuit |
outputs an object of circuits and their status | |
To app | circuit/{#} |
/circuit |
outputs an object of a single circuit and its status |
To app | toggleCircuit(equipment) |
/circuit/{#}/toggle |
toggles the circuit (as a circuit number) |
To app | /circuit/{#}/set/{0/1} |
set the circuit (as a circuit number) to 1 (on) or 0 (off) | |
To app | cancelDelay |
/cancelDelay |
Cancel and current circuit (valves/heater cool down?) delay. |
Direction | Socket | API | Description |
---|---|---|---|
To client | temperature |
outputs an object with the temperatures, heat and set point information | |
To app | /temperature |
outputs an object with the temperatures, heat and set point information | |
To app | setSpaSetPoint(spasetpoint) |
/spaheat/setpoint/{#} |
Change the spa to setpoint (degrees) |
To app | incrementSpaSetPoint(degrees) |
/spaheat/increment/{degrees} |
Increment the spa by [optional] degrees; default=1 |
To app | decrementSpaSetPoint(degrees) |
/spaheat/decrement/{degrees} |
Decrement the spa by [optional] degrees; default=1 |
To app | spaheatmode(spaheatmode) |
/spaheat/mode/{mode} |
Change the spa heat mode (integer 0=off, 1=heater, 2=solar pref, 3=solar only) |
To app | setPoolSetPoint(poolsetpoint) |
/poolheat/setpoint/{degrees} |
Change the pool to setpoint (degrees) |
To app | incrementPoolSetPoint(degrees) |
/poolheat/increment/{degrees} |
Increment the pool by [optional] degrees; default=1 |
To app | decrementPoolSetPoint(degrees) |
/poolheat/decrement/{degrees} |
Decrement the pool by [optional] degrees; default=1 |
To app | poolheatmode(poolheatmode) |
/poolheat/mode/{mode} |
Change the pool heat mode (integer 0=off, 1=heater, 2=solar pref, 3=solar only) |
(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(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 |
Direction | Socket | API | Description |
---|---|---|---|
To client | pump |
outputs an object with the pump information | |
To app | /pump |
requests an object with the pump information | |
To app | setPumpCommand(action, pump, program, rpm, duration) |
action=off,run, save, saverun; pump=1 or 2, program = 1 to 4, rpm = 450-3450, duration in minutes (or null for indefinite); leave parameters as null for any values that are not relevant. For example, to run program 4 on pump 1, call setPumpCommand('run',1,4,null,null) | |
To app | setPumpType(pump, type) |
/pumpCommand/pump/{pump}/type/{type} |
Set [pump] to [type] (one of VS ,VF ,VSF ,None ) |
Direction | Socket | API | Description |
---|---|---|---|
To app | /pumpCommand/off/pump/{pump} |
Turns {pump} off | |
To app | /pumpCommand/run/pump/{pump} |
Runs {pump} indefinitely | |
To app | /pumpCommand/run/pump/{pump}/duration/{duration} |
Runs {pump} for a duration | |
To app | /pumpCommand/run/pump/{pump}/program/{program} |
Runs {pump} {program} indefinitely | |
To app | /pumpCommand/run/pump/{pump}/program/{program}/duration/{duration} |
Runs {pump} {program} for a {duration} | |
To app | /pumpCommand/run/pump/{pump}/rpm/{rpm} |
Runs {pump} at {rpm} indefinitely | |
To app | /pumpCommand/run/pump/{pump}/rpm/{rpm}/duration/{duration} |
Runs {pump} at {rpm} for a {duration} | |
To app | /pumpCommand/save/pump/{pump}/program/{program}/rpm/{rpm} |
Saves {pump} {external program} as {rpm} | |
To app | /pumpCommand/saverun/pump/{pump}/program/{program}/rpm/{rpm} |
Saves {external program} as {rpm}, then runs {pump} {program} indefinitely | |
To app | /pumpCommand/saverun/pump/{pump}/program/{program}/rpm/{rpm}/duration/{duration} |
Saves {external program} as {rpm}, then runs {pump} {program} for {duration} | |
To app | /pumpCommand/run/pump/{pump}/gpm/{gpm} |
Runs {pump} at {gpm} indefinitely | |
To app | /pumpCommand/run/pump/{pump}/gpm/{gpm}/duration/{duration} |
Runs {pump} at {gpm} for a {duration} | |
To app | /pumpCommand/save/pump/{pump}/program/{program}/gpm/{gpm} |
Saves {pump} {external program} as {gpm} | |
To app | /pumpCommand/saverun/pump/{pump}/program/{program}/gpm/{gpm} |
Saves {external program} as {gpm}, then runs {pump} {program} indefinitely | |
To app | /pumpCommand/saverun/pump/{pump}/program/{program}/gpm/{gpm}/duration/{duration} |
Saves {external program} as {gpm}, then runs {pump} {program} for {duration} |
Direction | Socket | API | Description |
---|---|---|---|
To client | schedule |
/schedule |
outputs an object with the schedule information |
To app | setSchedule(id, circuit, starthh, startmm, endhh, endmm, dow*) |
set the schedule on the controller for the particular schedule ID. dow= day of week as expressed as [0=Sunday, 1=Monday, 2=Tuesday, 4=Wednesday, 8=Thursday, 16=Friday, 32=Saturday] or a combination thereof [3=Monday+Tuesday]. To set a schedule set a valid start and end time (hh:mm). To set an egg timer, set the start time to 25:00 and the endtime to the duration (hh:mm) you want the egg timer to run. | |
To app | toggleScheduleDay(id,dow) |
/schedule/toggle/id/{id}/day/{day} |
Toggle the day of schedule [id]. [dow] can be expressed as a single day (three letters, eg Sun; full name, eg Sunday; or dow as described in setSchedule. |
To app | deleteScheduleOrEggTimer(id) |
/schedule/delete/id/{id}/day/{day} |
Delete the [id] with the corresponding schedule |
To app | setScheduleStartOrEndTime(id,sOE,hour,min) |
/schedule/set/id/{id}/startOrEnd/{sOE}/hour/{hour}/min/{min} |
Edit schedule with [id]. sOE=start or end . hour in 24h notation 0-23. min 0-59. |
To app | setScheduleCircuit(id,circuit) |
/schedule/set/id/{id}/circuit/{circuit} |
Assign [circuit] (as id of circuit) to schedule [id] |
To app | setEggTimer(id,circuit,hour,min) |
/eggtimer/set/id/{id}/circuit/{circuit}/hour/{hour}/min/{min} |
Assign egg timer to schedule with [id], [circuit] as circuit id, and hour 0-23 and min 0-59. |
The poolController app runs on a configuration file. As noted in the Install Instructions, you can launch from a custom file or use the default name of config.json
.
New as of 4.1.33, the app will not come with config.json
. This makes upgrading easy because the app will not overwrite your existing settings if you use the default name.
When you first launch the app, or launch the app specifying a configuration file that does not exist, it will be created from a template. The template is sysDefault.json
and this file should NOT be modified directly.
Every time the app runs, or the code is upgraded, the app will check the specified configuration file against the sysDefaults.json
file and add any new keys.
Summary
- Any edits are retained (eg you change logConfigMessages=1, but it is logConfigMessages=0 in the template, the value will not be changed in the configuration file). The only exception is the
version
key which will be updated to the latest value. - New keys are automatically added with default values.
- Old keys will output a warning, but will not be deleted*.
In the logs, you will see
Keys that can be deleted:
20:49:15.181 INFO Potential expired/deprecated keys in
file: config_local.json
key: Hi I am an extra key:0
Keys that are automatically added:
20:50:55.718 INFO New keys copied
from: sysDefault.json
to: config.json
key: poolController.log.logApi:0
If you want to only see what would be changed, or if you want the app to also delete keys, you can run an npm script.
npm run configTester %config.json% [overwriteFile or outputToScreen]
where %config.json% is the path to your configuration file. Defaults to config.json
and
[overwriteFile or outputToScreen], if present, will write the entire changed file to the file (with deletes) or output the contents to the screen.
See below for descriptions
{
"equipment": {
"controller": {
"intellicom": {
"installed": 0,
"friendlyName": ""
},
"intellitouch": {
"installed": 1,
"friendlyName": "",
"numberOfCircuits": 20,
"numberOfPumps": 2,
"numberOfCustomNames": 10
},
"virtual": {
"pumpController": "default",
"chlorinatorController": "default"
},
"id": {
"productName": "",
"productNumber": "",
"manufacturer": "",
"description": ""
},
"circuitFriendlyNames": {
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": "",
"7": "",
"8": "",
"9": "",
"10": "",
"11": "",
"12": "",
"13": "",
"14": "",
"15": "",
"16": "",
"17": "",
"18": "",
"19": "",
"20": ""
}
},
"chlorinator": {
"installed": 1,
"desiredOutput": {
"pool": -1,
"spa": -1
},
"friendlyName": "",
"id": {
"productName": "",
"productNumber": "",
"manufacturer": "",
"description": ""
}
},
"pump": {
"1": {
"type": "VS",
"externalProgram": {
"1": -1,
"2": -1,
"3": -1,
"4": -1
},
"friendlyName": ""
},
"2": {
"type": "VS",
"externalProgram": {
"1": -1,
"2": -1,
"3": -1,
"4": -1
},
"friendlyName": ""
}
}
},
"poolController": {
"appAddress": 33,
"http": {
"enabled": 1,
"expressPort": 3000,
"redirectToHttps": 0,
"expressAuth": 0,
"expressAuthFile": "/users.htpasswd"
},
"https": {
"enabled": 1,
"expressPort": 3001,
"expressAuth": 0,
"expressAuthFile": "/users.htpasswd",
"expressKeyFile": "/data/server.key",
"expressCertFile": "/data/server.crt"
},
"network": {
"rs485Port": "/dev/ttyUSB0",
"netConnect": 0,
"netHost": "raspberrypi",
"netPort": 9801,
"inactivityRetry": 10
},
"notifications": {
"version": {
"remote": {
"version": "4.0.0",
"tag_name": "v4.0.0",
"dismissUntilNextRemoteVersionBump": false
}
}
},
"log": {
"logLevel": "info",
"socketLogLevel": "info",
"fileLog": {
"enable": 0,
"fileLogLevel": "silly",
"fileName": "output.log"
},
"logPumpMessages": 0,
"logDuplicateMessages": 0,
"logConsoleNotDecoded": 0,
"logConfigMessages": 0,
"logMessageDecoding": 0,
"logChlorinator": 0,
"logIntellichem": 0,
"logPacketWrites": 0,
"logPumpTimers": 0,
"logReload": 0,
"logApi": 0
},
"database": {
"influx": {
"enabled": 0,
"host": "localhost",
"port": 8086,
"database": "pool"
}
}
},
"integrations": {
"socketISY": 0,
"outputSocketToConsoleExample": 0
},
"socketISY": {
"username": "blank",
"password": "blank",
"ipaddr": "127.0.0.1",
"port": 12345,
"Variables": {
"chlorinator": {
"saltPPM": 16
},
"pump": {
"1": {
"watts": 25,
"rpm": 24,
"currentprogram": 13,
"program1rpm": 10,
"program2rpm": 11,
"program3rpm": 12,
"program4rpm": 13,
"power": 14,
"timer": 15
}
},
"circuit": {
"1": {
"status": 8
},
"2": {
"status": 3
},
"3": {
"status": 2
}
},
"temperatures": {
"poolTemp": 17,
"spaTemp": 18,
"airTemp": 19,
"spaSetPoint": 20
}
}
},
"outputSocketToConsoleExample": {
"level": "warn"
}
}
This section defines the equipment setup.
Physical or virtual controllers
- If you have this, set
"installed": 1
friendlyName
- not implemented as of 4.0 alpha 8
-
If you have this, set
"installed": 1
-
friendlyName
- not implemented as of 4.0 alpha 8 -
If you have expansion boards, set the number in the appropriate variables. The app will expand your sections of your config.json to have the appropriate variables.
- "numberOfCircuits": default=20; increases by 10 per board up to 50
- "numberOfPumps": default=2; increases by 2 per board up to 10
- "numberOfCustomNames": default=10; increases by 10 per board up to 40
Options to use the nodejs-poolController app as the controller on your system. You should not enable these if you have another controller (intellicom/intellitouch)
pumpController
- will actively manage the pumps when they are off or runningchlorinatorController
- will actively manage the chlorinatorController
Valid options are:
default
: If intellicom and intellitouch are not installed, start the controlleralways
: Start the controller irregardless of other controllersnever
: Do not start the controller
Descriptive strings used to describe the controller. Not implemented as of 4.0 alpha 8.
If you want to expand the names of your circuits beyond the 11 (lame!) character limit, add them here. These will filter through to the UI, but more importantly if you need to name your circuit "WTRFALL 1.5" in the Pentair controller but want to refer to it as "waterfall medium" through Siri/Alexa (hint, hint) this is the place to do it.
For more detail, the app will first determine if the circuit is using one of the ~200 built-in names, then it will check if it using a Pentair custom name, and finally, it will check to see if you want to assign it a friendly name from this config file.
- If you have this, set
"installed": 1
desiredOutput
: A value between 0-100 for % of chlorination time. This value will be read/updated as it is changed in the UI or through the API.friendlyName
: Used to identify the chlorinator. Not implemented as of 4.0 alpha 8id
: Descriptive strings used to describe the chlorinator. Not implemented as of 4.0 alpha 8.
Enumerated object of the pumps.
type
:none
: if you do not have this pumpVF
: if you have a Variable Flow model pumpVS
: if you have a Variable Speed model pumpVSF
: if you have a Variable Speed/Flow model pump (Note: this will act the same as a VF model)
externalProgram
: Stores the 4 external programs on the pump when the UI or one of thesave
API's is called. For VS this will be RPM values, for VF/VSF this will be GPM values. Please set these through the UI or they will not be synced with the pump.id
: Descriptive strings used to describe the chlorinator. Not implemented as of 4.0 alpha 8.
Sets options related to how the app works
The address on the serial bus that this app will use. The pumps don't seem to care what number you use, but Intellitouch won't respond unless this address is one of 32, 33, 34.
enabled
: 1 for yes, 0 for noexpressPort
: set to the value that you want the web pages to be served to. 3000 = http://localhost:3000redirectToHttps
: 1 for yes, 0 for no. If this is 1, all other options except for the port will be ignored.expressAuth
:0
for no username/password.1
for username/password.expressAuthFile
: input the path to the file. By default,/users.htpasswd
. If you haveexpressAuth=1
then create the file users.htpasswd in the root of the application. Use a tool such as http://www.htaccesstools.com/htpasswd-generator/ and paste your user(s) into this file. You will now be prompted for authentication.
enabled
: 1 for yes, 0 for noexpressPort
: set to the value that you want the web pages to be served to. 3001 = https://localhost:3001expressKeyFile
: path to CA Key fileexpressCertFile
: path to CA Cert fileexpressAuth
:0
for no username/password.1
for username/password.expressAuthFile
: input the path to the file. By default,/users.htpasswd
. If you haveexpressAuth=1
then create the file users.htpasswd in the root of the application. Use a tool such as http://www.htaccesstools.com/htpasswd-generator/ and paste your user(s) into this file. You will now be prompted for authentication.
rs485Port
: If you are running the code on the same machine as your local rs485 adapter, set the address of the adapter here. Typically/dev/ttyUSB0
on Unix machines.- To connect to native rs485 traffic for connectivity/debugging using
SOCAT
1.netConnect
:1
to enable or0
to disable. If you enable this option, it will NOT connect to the local RS485 adapter 1.netHost
: Name/IP of your remote computer. EGraspberrypi
1."netPort":
:9801
is a standard port inactivityRetry
: time in seconds to retry a connection to the port (RS485 or Socat) if a connection is lost. Default is 10 seconds.
Section for how/when the app will notify you about certain actions/conditions.
The app will check to see if you have the latest published release.
version
: Latest published versiontag_name
: Tag of latest published versiondismissUntilNextRemoteVersionBump
: Silence the notifications until version/tag_name changes again.
###log Settings for the console, UI and file logs.
logLevel
is the console output level (see below for valid levels)socketLogLevel
is the bootstrap UI output level in the debug panel (see below for valid levels)fileLog
enable output to a fileLogenable
:1
for yes,0
for nofileLogLevel
: output file for level (see below for valid levels)fileName
:output.log
is the default. Can take an optional path relative to the main directory.
| Valid output levels | | --- | --- | | Error | Only output error messages | | Warn | Output the above, plus warnings | | Info | Output the above, plus information about circuit/status changes | | Debug | Output the above, plus debugging information | | Silly | Output the above, plus code-level troubleshooting messages |
logPumpMessages
: 1 = show messages from the pump in the logs, 0 = hidelogDuplicateMessages
: 1 = show messages that are repeated on the bus in the logs, 0 = hidelogConsoleNotDecoded
: 1 = log any messages that have not been documentedlogConfigMessages
: 1 = log messages that relate to the configuration of the pool (from controllers), 0 = hidelogMessageDecoding
: 1 = log the internal decoding of packetslogChlorinator
: 1 = log messages directly from the chlorinator, 0 = hide (If you have Intellitouch, status will be received from the controller directly)logPacketWrites
: 1 = log debug messages about packet writes, 0 = hidelogPumpTimers
: 1 = log debug messages about pump timers, 0 = hidelogReload
: 1 = log requests to reload the application, 0 = hide
See below for Integration instructions.
integrations
:_name_of_module_
:1
to enable,0
to disable
_name_of_module_
: configuration options to be used by the integration component
-
This code REQUIRES a RS485 serial module. There are plenty of sites out there that describe the RS485 and differences from RS232 so I won't go into detail here. The inexpensive JBTek adapter works great.
-
Connect the DATA+ and DATA-.
-
To see if you are getting the proper communications from the bus, before you even try to run this program, run from your unix command line
od -x < /dev/ttyUSB0
Of course, you'll need to change the address of your RS-485 adapter if it isn't the same as mine (here and in the code).
- You'll know you have the wires right when the output of this command looks like (you should see multiple repetitions of ffa5ff):
0002240 0000 0000 0000 0000 0000 ff00 ffff ffff
0002260 **ffff 00ff a5ff** 0f0a 0210 161d 000c 0040
0002300 0000 0000 0300 4000 5004 2050 3c00 0039
0002320 0400 0000 597a 0d00 af03 00ff a5ff 100a
0002340 e722 0001 c901 ffff ffff ffff ffff ff00
- This is the WRONG wiring (no ffa5ff present).
0001440 0000 0000 0000 0000 0000 0000 0000 6a01
0001460 e1d6 fbdf d3c5 fff3 ff7f ffff ffff f9ff
0001500 7fff 5ff7 bf5f 87ff ff8d f7ff ffff 4d0b
0001520 e5ff adf9 0000 0000 0000 0000 0100 d66a
0001540 dfe1 c5fb f3d3 7fff ffff ffff ffff fff9
Set the "logLevel": "info"
variable to your liking.
The RS-485 bus is VERY active! It sends a lot of broadcasts, and instructions/acknowledgements. Many commands are known, but feel free to help debug more if you are up for the challenge! See the wiki for what we know. Below are a sample of the message
Request for a status change:
08:47:51.368 INFO User request to toggle PATH LIGHTS to On
When the app starts, it will show the circuits that it discovers. For my pool, the circuits are:
08:45:46.948 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"]
08:45:50.989 INFO
Circuit Array Discovered from configuration:
Circuit 1: SPA Function: Spa Status: 0 Freeze Protection: Off
Circuit 2: JETS Function: Generic Status: 0 Freeze Protection: Off
Circuit 3: AIR BLOWER Function: Generic Status: 0 Freeze Protection: Off
Circuit 4: CLEANER Function: Master Cleaner Status: 0 Freeze Protection: Off
Circuit 5: WtrFall 1.5 Function: Generic Status: 0 Freeze Protection: Off
Circuit 6: POOL Function: Pool Status: 0 Freeze Protection: Off
Circuit 7: SPA LIGHT Function: Light Status: 0 Freeze Protection: Off
Circuit 8: POOL LIGHT Function: Light Status: 0 Freeze Protection: Off
Circuit 9: PATH LIGHTS Function: Light Status: 0 Freeze Protection: Off
Circuit 10: NOT USED Function: Generic Status: 0 Freeze Protection: Off
Circuit 11: SPILLWAY Function: Spillway Status: 0 Freeze Protection: Off
Circuit 12: WtrFall 1 Function: Generic Status: 0 Freeze Protection: Off
Circuit 13: WtrFall 2 Function: Generic Status: 0 Freeze Protection: Off
Circuit 14: WtrFall 3 Function: Generic Status: 0 Freeze Protection: Off
Circuit 15: Pool Low2 Function: Generic Status: 1 Freeze Protection: Off
Circuit 16: NOT USED Function: Spillway Status: 0 Freeze Protection: Off
Circuit 17: NOT USED Function: Spillway Status: 0 Freeze Protection: Off
Circuit 18: NOT USED Function: Spillway Status: 0 Freeze Protection: Off
Circuit 19: NOT USED Function: Generic Status: 0 Freeze Protection: Off
Circuit 20: AUX EXTRA Function: Generic Status: 0 Freeze Protection: Off
08:45:54.136 INFO Msg# 69 Schedules discovered:
ID: 1 CIRCUIT:(6)POOL MODE:Schedule START_TIME:9:25 END_TIME:15:55 DAYS:Sunday Monday Tuesday Wednesday Thursday Friday Saturday
ID: 2 CIRCUIT:(13)WtrFall 2 MODE:Schedule START_TIME:14:57 END_TIME:15:8 DAYS:Sunday Tuesday Thursday Saturday
ID: 3 CIRCUIT:(4)CLEANER MODE:Schedule START_TIME:10:15 END_TIME:11:0 DAYS:Sunday Monday Tuesday Wednesday Thursday Friday Saturday
ID: 4 CIRCUIT:(6)POOL MODE:Egg Timer DURATION:7:15
ID: 5 CIRCUIT:(4)CLEANER MODE:Egg Timer DURATION:4:0
ID: 6 CIRCUIT:(15)Pool Low2 MODE:Schedule START_TIME:21:10 END_TIME:23:55 DAYS:Sunday Monday Tuesday Wednesday Thursday Friday Saturday
ID: 7 CIRCUIT:(15)Pool Low2 MODE:Schedule START_TIME:0:5 END_TIME:9:20 DAYS:Sunday Monday Tuesday Wednesday Thursday Friday Saturday
ID: 8 CIRCUIT:(7)SPA LIGHT MODE:Egg Timer DURATION:2:0
ID: 9 CIRCUIT:(2)JETS MODE:Egg Timer DURATION:3:45
ID:10 CIRCUIT:(9)PATH LIGHTS MODE:Egg Timer DURATION:4:15
ID:11 CIRCUIT:(11)SPILLWAY MODE:Schedule START_TIME:13:0 END_TIME:13:11 DAYS:Sunday Monday Tuesday Wednesday Thursday Friday Saturday
ID:12 CIRCUIT:(5)WtrFall 1.5 MODE:Schedule START_TIME:13:20 END_TIME:13:40 DAYS:Sunday Tuesday Thursday
To display the messages below, change the logging level to VERBOSE
and enable logConfigMessages
.
08:47:51.606 VERBOSE Msg# 266:
S L V H P S H A S H
O E M M M A T OO P T I O E
D U N H O O O U L R L A R R L A C C
E R C G O M D D D O V M T T _ T T T H H
S C M T U I E E E M E D M M O M M M K K
T E D H R N 1 2 3 S E P P N P P D H L
Orig: 165, 16, 15, 16, 2, 29, 8, 57, 0, 64, 0, 0, 0, 0, 0, 0, 3, 0, 64, 4, 61, 61, 32, 0, 49, 45, 0, 0, 4, 0, 0,137,192, 0, 13, 4,13
New: 165, 16, 15, 16, 2, 29, 8, 57, 0, 65, 0, 0, 0, 0, 0, 0, 3, 0, 64, 4, 61, 61, 32, 0, 49, 45, 0, 0, 4, 0, 0,137,192, 0, 13, 4,14
Diff: *
08:47:51.609 DEBUG No change in time.
08:47:51.624 VERBOSE Msg# 266 Circuit PATH LIGHTS change: Status: Off --> On
An example of pump communication. To show these, enable logPumpMessages
.
08:50:10.805 VERBOSE Msg# 79 Main --> Pump 1: Pump power to on: [165,0,96,16,6,1,10,1,38]
=======
You can now (pretty) easily add your own code to interface with any other home automation (or other) systems. See https://github.com/tagyoureit/nodejs-poolController/wiki/Integrations-in-2.0
The outputSocketToConsoleExample
is a very simple module that echos out a few socket messages. The ISY sample is a bit more complex and keeps track of the state of variables.
Want to have a RaspberryPi Zero, or other $5 computer, sitting by your pool equipment while the main code runs elsewhere on your network? Or want to help get involved with the project and debug in an app like Netbeans?
@arrmo was super slick in getting this to run.
There are two options:
- Run socat each time to enable the pipe
- Setup a daemon to automatically start socat
Run these commands on the remote machine
sudo apt-get install socat
to install socat/usr/bin/socat TCP-LISTEN:9801,fork,reuseaddr FILE:/dev/ttyUSB0,b9600,raw
- Setup the app parameters (below)
Run these commands on the remote machine
sudo apt-get install socat
to install socatsudo apt-get install daemon
to install daemon- Copy the
poolTTY
file (in /scripts directory) to your remote machine directory/etc/init.d
- Run the following command to make the daemon run the socat upon startup:
sudo update-rc.d poolTTY defaults
- Setup the app parameters (below)
Props to @antamy. Another approach to an etc/init.d
script. The script is runAtBoot.sh
. See https://github.com/chovy/node-startup for instructions to use this script.
From your local machine, you should be able to telnet to port 9801 and see incoming packets.
In the "network"
section, set netConnet=1
. netHost
is your remote machine. netPort
should be 9801 if you followed these instructions.
Start the app and navigate to http://localhost:3000/public/pump.html. Addition of the pump control to . See #150./bootstrap
is in progress
Configuration is saved automatically to ./src/www/bootstrap/configClient.json
when you make changes in the UI.
visible
- This panel will be shown and expandedcollapse
- This panel will be shown and collapsedhidden
- This panel will not be shown
"InfluxDB" is an open-source time series database that make storage of all pool data extremely easy. Much thanks to "@johnny2678" for pointing me in this direction!
Direct Install
- Follow install instructions from "Influx install instructions"
- Create database
pool
or whatever you choose that matches yourconfig.json
file settings.
Docker Instructions
- Install Docker with a single command on RasPi3 -
curl -sSL https://get.docker.com | sh
"from" - ...
- ...
- More to come...
1.0.0 -
- Much of the code reworked and refactored
- Added Bootstrap UI by @arrmo
- Better standalone pump control (@bluemantwo was super-helpful here, too!)
- More accurate recognition of packets
- Super fast speed improvements
- Outgoing packets are now sent based on a timer (previously number of incoming packets)
- Added ISY support (@bluemantwo was super-helpful here, too!)
2.0.0 -
- https, Authentication
- Completely refactored code. Integrated BottleJS (https://github.com/young-steveo/bottlejs) for dependency injection and service locator functions
- Integrations to loosely couple add-ons
3.0.0 -
- Upgraded pump logic
3.1.x -
- Added unit testing for certain areas
- Added setDateTime API/Socket
- Bootstrap panel states are now persistent
4.0.0 -
- Changed much in the config.json file
- Save pump programs and chlorinator level to config.json
- Added support for GPM with pumps
- Check for newer versions of the app on github, and dismiss notifications until next release
- Bootstrap configuration is automatically saved in clientConfig.json via UI actions
- Started to introduce some promises into the workflow (mostly with read/write operations)
- Added log-to-file option
- Added capture for Ctrl-C/SIGINT to have a clean exit
- Added InfluxDB database capabilities
- Added support for reading the data from up to 16 pumps. (You can still only control two.)
- Support for up to 50 circuits, 8 pumps
- Delay and Cancel Delay for circuits
5.0.0 -
Make sure to run npm upgrade
. There are many package updates and changes.
-
Added add/delete/edit schedule
-
All sockets/API now singular (
circuits
->circuit
) -
All sockets/API data now returned with a JSON qualifier. EG
{pump:...}
,{circuit:...}
-
Intellichem decoding and display
-
Changes to
/config
endpoint. It's now included with the/all
end point since there would be quite a bit of duplication. It still exists standalone (for now) but has much less information in it. -
Moved
hideAux
setting fromconfigClient.json
(web UI settings) toconfig.json
template. Inconfig.json
template, moved{equipment: {controller: {circuitFriendlyNames:{1..20}}}} // to {equipment: {circuit: friendlyName:{1..20}, hideAux: boolean }, }
to be in line with the other equipment in the pool setup and accomodate the
hideAux
setting. -
Fixed issue #82
-
Extra info from
/config
was being added to the circuit section inconfig.json
-
This release includes a new mechanism for updating config.json files. See notes in config.json section.
-
mDNS server. Currently included for SmartThings integration, but in the future can be used for autodiscovery by other applications/devices.
-
New
/config
endpoint (beta) to allow applications to get a high level summary of the system. -
Support for two separate (http/https) web servers, each/both with Auth, and also the option to redirect all http to https traffic. Thanks to @arrmo for driving this with #65 and #68.
-
A UI for standalone pumps
-
All sockets and API's renamed to be SINGULAR. Circuits -> circuit, Schedules->schedule, etc.
-
All returned JSON data (API/socket) now has the type qualifier per #57
-
Make sure to run
npm upgrade
. There are many package updates and changes. -
Intellichem initial support.
-
Inactivity timer for both internal connections and web page connections. If a connection is broken, it should re-establish itself automatically now.
-
SSDP for auto-discovery by SmartThings or other services
5.0.1 -
- Fixed Influx error on startup #90
- Fixed bad characters in custom names
5.1.0 -
- Intellibrite support - API's, Sockets and a WebUI. Lights should have the 'Intellbrite' an their circuit function (set this up at the controller) to show up in this section. 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.1.1 -
- Renamed all 'valves' items to valve to be in line with singular renaming of items
- InfluxDB - moved some items that were in tag fields to field keys; added valves
- Added days of week (with editing) back to the schedules. Not sure when they disappeared, but they are back now. #92
- Added MySQL integration to log all packets to a DB
- Fixed PR #95 to allow sub-hour egg timers
- Fixed Intellibrite bugs
- Started to move some of the inter-communications to emitter events for better micro-services and shorter call stacks (easier debugging; loosely coupled code).
- Changed some Influx tags/queries.
- Node 6+ is supported. This app no longer supports Node 4.
- Update of modules. Make sure to run
npm i
ornpm upgrade
to get the latest. - 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.
- Chlorinator API calls (and UI) will now make changes through Intellitouch when available, or directly to the Intellichlor if it is standalone (aka using the virtual controller)
- Decoupled serial port and processing of packets. Should help recovery upon packet errors.
- Implementation of #89. Expansion boards are now (better) supported by setting variables in your config.json. See the config.json section below.
- Fix for #95
- Fix for #99
- Fix for #100
- Still many messages to debug
- Alexa, Siri integration coming soon!
- Integration directly with Screenlogic (IP based). Awesome job @ceisenach. https://github.com/ceisenach/screenlogic_over_ip
If you read through the below links, you'll quickly learn that the packets can vary their meaning based upon who they are sending the message to, and what they want to say. It appears the same message can come in 35, 38 or 32 bytes, but of course there will be some differences there.
- Jason Young (Read both posts, they are a great baseline for knowledge)
- Michael Russe ceesco CocoonTech - Registration required for CocoonTech. Jason Young used this material for his understanding in the protocol as well. There is a very detailed .txt file with great information
that I won't post unless I get permission. Looks like it was publicly posted to Pastebin. - Michael Usner for taking the work of both of the above and turning it into Javascript code.
- rflemming for being the first to contribute some changes to the code.
- Awesome help from @arrmo and @blueman2 on Gitter