Quick project: Adding support for swimmingpool pump activation schedule [nodejs]
Hi everyone, here is a very quick post on the update I'm adding on my “NervSwimPool” project, which is a simple NodeJS project I built to get remote access to our swimmingpool devices (pump, robot, light, etc). I could explain this further to give a bit more context for this article, but, I feel lazy to do it, because I already lost some good time restoring my network access to the raspberry pi device hosting the program (stupid old/invalid network gateway setting of course).
⇒ So let's just get straight into the problem.
Main app entry point
- Oaky, so looking into the NervSwimPool project repository, I now see that I have a main (single) src/index.ts file, ending with this content:
// add router in the Express app. app.use("/", router); // start the Express server let server = app.listen(port, () => { console.log(`server started at http://localhost:${port}`); }); process.on('SIGINT', _ => { console.log("process killed: releasing GPIO pins.") for (var i = 0; i<pins.length;i++) { pins[i].writeSync(Gpio.HIGH); pins[i].unexport(); } console.log("Done releasing GPIO pins.") server.close(function () { console.log('Closed server.'); }); });
- So we start a server at that location, all good. But what I need now is to also periodically read a config file, and then check if I should activate the pool pump at the current time or not.
- Side Note: just found this page on the building of a REST API in python: if I had to redo this project again that's how I would do it I think.
Adding support for schedule handling
- ⇒ To handle this automatic schedule I added the following section in the swimpool.json file:
"pump_schedule": [ { "start": "10:00", "end": "12:00" }, { "start": "00:00", "end": "03:00" } ]
- And then I added a function to handle those schedule entries:
function process_pump_schedule() { // Should read the contend of our config file: console.log("Processing pump schedule..."); let cfg = getConfig(); let ranges: any = cfg["pump_schedule"]; // Get the current time: const cur_date = new Date(); const cur_hour = cur_date.getHours(); const cur_min = cur_date.getMinutes(); const cur_ts = cur_hour * 60 + cur_min; let pinIdx: number = pinMap["pool_pump"]; for (let range of ranges) { // Check if we just crossed the "start" time: let parts = range["start"].split(":"); let start_ts = parts[0] * 60 + parts[1]; if (cur_ts > start_ts && cur_ts - 60 < start_ts) { // Should start the pump here: console.log("Enabling pump at " + range["start"]); pins[pinIdx].writeSync(Gpio.LOW); } parts = range["end"].split(":"); let end_ts = parts[0] * 60 + parts[1]; if (cur_ts > end_ts && cur_ts - 60 < end_ts) { // Should stop the pump here: console.log("Disabling pump at " + range["end"]); pins[pinIdx].writeSync(Gpio.HIGH); } } }
- Finally, calling this function every minute:
var interval_id = setInterval(process_pump_schedule, 60000);
- ⇒ Let's now restart the server:
- First I kill the existing node process:
$ ps aux | grep node
- Then I run the process manually with:
$ sudo /home/ubuntu/swimmingpool/scripts/start_pool_server.sh
Fixing the timezone
- But the timezone on that system is not correct:
ubuntu@ubuntu:~$ date Sat May 14 11:45:14 UTC 2022 ubuntu@ubuntu:~$ timedatectl Local time: Sat 2022-05-14 11:45:46 UTC Universal time: Sat 2022-05-14 11:45:46 UTC RTC time: n/a Time zone: Etc/UTC (UTC, +0000) System clock synchronized: yes NTP service: active RTC in local TZ: no
- I found this page on how to set the timezone on ubuntu: https://linuxize.com/post/how-to-set-or-change-timezone-on-ubuntu-20-04/
- So now setting the correct timezone:
$ timedatectl list-timezones $ timedatectl set-timezone Europe/Paris
- OK, now we are good:
ubuntu@ubuntu:~$ timedatectl Local time: Sat 2022-05-14 13:49:13 CEST Universal time: Sat 2022-05-14 11:49:13 UTC RTC time: n/a Time zone: Europe/Paris (CEST, +0200) System clock synchronized: yes NTP service: active RTC in local TZ: no ubuntu@ubuntu:~$ date Sat May 14 13:49:52 CEST 2022
Updated pump schedule function
- After some additional tweaking, here is the final version of the
process_pump_schedule
function I got:function process_pump_schedule() { // Should read the contend of our config file: let cfg = getConfig(); let ranges: any = cfg["pump_schedule"]; // Get the current time: const cur_date = new Date(); const cur_hour = cur_date.getHours(); const cur_min = cur_date.getMinutes(); const cur_ts = cur_hour * 60 + cur_min; // console.log("Processing pump schedule at " + cur_hour + ":" + cur_min); // console.log("Current timestamp: " + cur_ts); let pinIdx: number = pinMap["pool_pump"]; for (let range of ranges) { // Check if we just crossed the "start" time: let parts = range["start"].split(":"); // console.log("parts: " + JSON.stringify(parts)); let start_ts = Number(parts[0]) * 60 + Number(parts[1]); // console.log("Checking start timestamp: " + start_ts); if ( cur_ts >= start_ts && cur_ts - 2 < start_ts && pins[pinIdx].readSync() == Gpio.HIGH ) { // Should start the pump here: console.log(cur_date.toLocaleString() + ": Enabling pool pump."); pins[pinIdx].writeSync(Gpio.LOW); } parts = range["end"].split(":"); // console.log("parts: " + JSON.stringify(parts)); let end_ts = Number(parts[0]) * 60 + Number(parts[1]); // console.log("Checking end timestamp: " + end_ts); if ( cur_ts >= end_ts && cur_ts - 2 < end_ts && pins[pinIdx].readSync() == Gpio.LOW ) { // Should stop the pump here: console.log(cur_date.toLocaleString() + ": Disabling pool pump."); pins[pinIdx].writeSync(Gpio.HIGH); } } }
Auto-disable pool robot
- While I'm at it, I think I should also add support to automatically disable to pool robot after some time of cleaning (like 2hours maybe): Let's add support for that.
- To achieve this I updated the /handle endpoint as follow:
var robot_timeout: any = null; function stop_robot() { let pinIdx: number = pinMap["pool_robot"]; if (pins[pinIdx].readSync() == Gpio.LOW) { const cur_date = new Date(); console.log( cur_date.toLocaleString() + ": Automatic stop of robot cleaning session." ); } } router.post("/handle", (request, response) => { //code to perform particular action. // We start with reading our configuration file first here: let cfg = getConfig(); let auth = request.body.auth; let user = getAuthenticatedUser(auth, cfg); if (user == null) { console.log(`Cannot authenticate user with auth: ${auth}`); response.json({ status: "Error", message: "Cannot authenticate user." }); return; } //To access POST variable use req.body()methods. // console.log("Received body: "); // console.log(request.body); // response.json({ result: 'OK', name: request.body.username }); let act: string = request.body.action; let tgt: string = request.body.target; // Inverted pin values below: let relayVal = act == "enable" ? Gpio.LOW : Gpio.HIGH; let pinIdx: number = pinMap[tgt]; console.log(`Setting pin ${pinIdx} to ${relayVal}`); pins[pinIdx].writeSync(relayVal); if (tgt == "pool_robot") { // We are activating/deasctivating the robot, so we should cancel the timeout if any: if (robot_timeout != null) { // console.log("Cancelling pool robot timeout callback."); clearTimeout(robot_timeout); robot_timeout = null; } // If we jsut enabled the robot, start the timeout: if (act == "enable") { // Duration below given in minutes: let dur = cfg["robot_cleaning_duration"] * 60 * 1000; robot_timeout = setTimeout(stop_robot, dur); } } // Send the state: sendStatus(cfg, response); });
- And of course I added the required entry in the config file:
"robot_cleaning_duration": 120,
Conclusion
- ⇒ And this is it for this time, now I just need to monitor the pool and the log file for a few days
- But right now, what I desperately need is a short nap lol. I can't keep my eyes opened anymore 😁