HomeHome AutomationTracking daylight in NodeRED

Tracking daylight in NodeRED

It's funny, how a simpler solution comes to you AFTER you figured out the complicated way!

From time to time, I get a crazy idea of changing something small in my automation setup. Usually, an idea like that drags me into a rabbit hole, I didn’t know it had existed. Whatever I do nowadays, I always look at it with an angle: “Would that make an interesting tutorial?” and that angle has turned a simple “Is it nighttime yet?”  query, into a 200 line long coding challenge. I hope someone will appreciate the fact I made a tool to check which part of the world needs a flashlight.

The “bigtimer” node is great… until it isn’t

NodeRED flow
A bit of a convoluted way of dealing with time

I have been using the “bigtimer” node for a long time. As my NodeRED flows grow, adding the same nodes each time around becomes a burden and a complication that you have to deal with. Having a global set of values that I can use is the best coding practice, then reconfiguring a “bigtimer” node each time you want to deal with a timer or reconfigure a group of the devices that are not connected to the same node.

I wanted to have a global variable, which would store information about the time of the day: day/night. On the surface, it’s a simple idea. When you actually approach the problem (and the fact that you would like to share the idea with other people in different parts of the world) things get a little bit harder.

If you are here to rip the fruit of my work, scroll down to the bottom and download the flow. You can also buy me a coffee while you at it.

Creating a time keeping alternative

To calculate the day|night I decided to convert the current time to seconds from 1970 (EPOCH time)  and compare the numeric values against the sunset and sunrise values. To be able to do so, I need sunset and sunrise values. I can obtain this using a free sunrise-sunset.org/api. As the API delivers data in the UTC timezone, some calculations are needed to present it correctly to people around the world. But I’m getting ahead of myself. There is more to sunset/sunrise than you think.

Exploring the rabbit hole

Nautical, Astronomical and Civil twilight
Nautical, Astronomical and Civil twilight

I spare you the boring historical details. In nutshell, humans recognise three sunset/sunrise levels with different light intensity based on how deep below the horizon the sun currently is. With my NodeRED from you will be able to get all that information adjusted for your current geographical location. The flow will:

  • tell you if it’s night or day
  • tell you the sunrise and sunset time
  • tell you the day length
  • provide you with astronomical, nautical, civil dusk and dawn times
  • use these values to create timers
  • all that good stuff in your local time!

Getting sun data based on coordinates.

NodeRED flow
Tracking daylight in NodeRED

You can get the longitude and latitude from Google Maps. Point it at your location and read the URL, these values are encoded into it:

https://www.google.co.uk/maps/@55.123456,-1.123456,14z
Latitude is in red, Longitude in green

If you wonder what’s the 14z stands for, it’s the current zoom level. Save these as you will need them in my NodeRED flow settings.

Geographical coordinates are used to determine the sunset/sunrise times. Instead of hardcoding the data, I added the timezone option so you can decide if you want UTC values or recalculated values to your local timezone. The coordinates will be also saved globally should you ever need these again in another project of yours (weather info perhaps?).

Raise and shine!

To get the JSON data from the API (you can learn more about JSON here) we have to make a REST request. I covered these in my NodeRED for beginners: Connectivity – in case you need a more robust guide.

The flow will get the coordinates from global variables (make sure they are set to survive reboots – see how to enable that) and will ask API to provide us with all information about the daylight in this location. As the information is valid for 24h, the request has to be made once a day.

Using API to get sunset/sunrise info in NodeRED
Using API to get sunset/sunrise info in NodeRED

There is plenty of data to store, and all of that will be used in other projects. It makes sense to save these as global values once again. To keep the variables organised I will structure them as JSON and format the time to ISO standard. This way it will be easy to convert it into an object-alike format:

var date = new Date("TimeInISOFormat");

and as such accessed again as a date object. In my settings flow, a timezone bool value defines how I’m going to save these. By default, they are saved with a timezone offset enabled.

FUNCTION NODE: Save Recalculated times (the easy way)

var timezone_enabled = flow.get("timezone");

let date = new Date()
var timeZoneOffset = date.getTimezoneOffset()*60000;

//convert string format into time object with timezone offset
function stringToObject_withOffset(x){
let z = new Date(x).valueOf();
let adjustedTime = (z - timeZoneOffset);
let new_time = new Date(adjustedTime);
return new_time;
}

//convert string format into time object without the offset
function stringToObject_noOffset(now){
let x = new Date(now);
return x;
}

//convert seconds to human readable time in hh:mm:ss,
function secondsToHms(d) {
d = Number(d);
var h = Math.floor(d / 3600);
var m = Math.floor(d % 3600 / 60);

return ('0' + h).slice(-2) + "h " + ('0' + m).slice(-2)+"min";
}

// lets take the values and save them as global values for later use. We will convert strings to ISO date so we can easily work with this in the future

var time;

// values in UTC
if (timezone_enabled === false){
time = {
"sunrise" : stringToObject_noOffset(msg.payload.results.sunrise),
"noon" : stringToObject_noOffset(msg.payload.results.solar_noon),
"sunset" : stringToObject_noOffset(msg.payload.results.sunset),

"day_lenght" : {"in_sec": msg.payload.results.day_length,
"in_hhmmss" : secondsToHms(msg.payload.results.day_length)},
"civil_twiligh_start" : stringToObject_noOffset(msg.payload.results.civil_twilight_begin),
"civil_twiligh_stop" : stringToObject_noOffset(msg.payload.results.civil_twilight_end),
"nautical_twilight_start" : stringToObject_noOffset(msg.payload.results.nautical_twilight_begin),
"nautical_twilight_stop" : stringToObject_noOffset(msg.payload.results.nautical_twilight_end),
"astronomical_twilight_start" : stringToObject_noOffset(msg.payload.results.astronomical_twilight_begin),
"astronomical_twilight_stop" : stringToObject_noOffset(msg.payload.results.astronomical_twilight_end),
"timezone" : date.getTimezoneOffset()
}
}

// values adjusted by the local timezone
if (timezone_enabled === true){
time = {
"sunrise" : stringToObject_withOffset(msg.payload.results.sunrise),
"noon" : stringToObject_withOffset(msg.payload.results.solar_noon),
"sunset" : stringToObject_withOffset(msg.payload.results.sunset),

"day_lenght" : {"in_sec": msg.payload.results.day_length,
"in_hhmmss" : secondsToHms(msg.payload.results.day_length)},
"civil_twiligh_start" : stringToObject_withOffset(msg.payload.results.civil_twilight_begin),
"civil_twiligh_stop" : stringToObject_withOffset(msg.payload.results.civil_twilight_end),
"nautical_twilight_start" : stringToObject_withOffset(msg.payload.results.nautical_twilight_begin),
"nautical_twilight_stop" : stringToObject_withOffset(msg.payload.results.nautical_twilight_end),
"astronomical_twilight_start" : stringToObject_withOffset(msg.payload.results.astronomical_twilight_begin),
"astronomical_twilight_stop" : stringToObject_withOffset(msg.payload.results.astronomical_twilight_end),
"timezone" : 0 - date.getTimezoneOffset()
}
}

global.set("Time", time);
msg.payload = time;
return msg;

Too many timezones to worry about

In my original article, I was using another API service to convert the coordinates into a specific timezone. Unless you want to change this value to reflect a remote location, the local time offset can be easily obtained by .getTimezoneOffset().

If you want to change the default behaviour, just replace that value with your negative time zone offset in minutes. Why negative?

.getTimezoneOffset() gives you a difference between UTC and your local time (with daylight saving). This value isn’t the same as the time zone value that we are so used to. At the time of the writing, UK is running at GMT+1 – it means that we are 60 min ahead of UTC time. .getTimezoneOffset() will report -60 which tells you that your time needs to be adjusted by -60 minutes to equal UTC.

To recalculate the time, I either have to convert the value to the opposite one (0-timezone_offset) or simply subtract the offset.

Is it a night time yet?

Is it nighttime yet?

Wait, we have sunsets, sunrises even the length of the day. Where are the timers? The main objective of this project was to get some sort of information about the night and day to drive my automation.

Since we have the info about the daylight features we can create a very effective timer that always tells us if the time outside is considered day/night/twilight etc. As these values change every day it’s very useful for driving automation that relies on the day/night cycle.

To achieve this all we have to do is ask NodeRED to check if it’s nighttime yet! (Shreck reference anyone?). If we repeat the question every minute, you have a pretty reliable clock that tracks the daylight features.

FUNCTION NODE: Is it night time yet?

var time = global.get("Time");

var sunrise = time.sunrise;
var sunset = time.sunset;

var now = new Date();
var timeNow = now.getTime();

//from sunrise to sunset
if(timeNow >= new Date(sunrise).getTime() && timeNow <= new Date(sunset).getTime()){ msg.payload = "According to my calculations it's daytime"; global.set("time_of_Day", "day"); } //from sunset to sunrise if(timeNow > new Date(sunset).getTime() || timeNow < new Date(sunrise).getTime()){ msg.payload = "According to my calculations it's nighttime"; global.set("time_of_Day", "night"); } return msg;

The "bigtimer" will have its uses, and my method is not a replacement for that useful node.  If you want to check the value of the time against the existing condition inside the function node, now you have the toolset to do so.

Final thoughts

It's not always about the time, but almost always about the journey. I hope you have picked up a thing or two that will make your flows better. I decided to rewrite this article to improve the flow and get rid of one annoying bug. Easily done for in the article, but I think this topic deserves a better video. What am I going to do with this project? I will add a small switch to enable daylight tracking in my various window curtains and blinds controllers, starting with the latest Zigbee Roller Driver from Aqara. Join the discussion on Reddit about this post!

Project Download

Download project files here. Bear in mind that Patreon supporters have early access to project files and videos.

PayPal

Nothing says "Thank you" better than keeping my coffee jar topped up!

Patreon

Support me on Patreon and get an early access to tutorial files and videos.

image/svg+xml

Bitcoin (BTC)

Use this QR to keep me caffeinated with BTC: 1FwFqqh71mUTENcRe9q4s9AWFgoc8BA9ZU

Smart Ideas with

Automate your space in with these ecosystems and integrate it with other automation services

client-image
client-image
client-image
client-image
client-image
client-image
client-image
client-image
client-image

Learn NodeRED

NodeRED for beginners: 1. Why do you need a NodeRED server?

0
To server or not to server? That's a very silly question!

Best Automation Projects

Nora – Google Assistant in NodeRED

0
Integrate Google Assistant with NodeRED thanks to Nora - NodeRED home automation

NEST your old thermostat under $5

0
Nest-ing up your older thermostat under $5

Sonoff R3 DIY mode & why it sucks

0
Long awaited Sonoff R3 DIY mode is even worse than I expected.

Things they don’t tell you about IKEA Trådfri

0
There are things you should know about IKEA Tradfri before you make your purchase

Flashing ESP with Tasmotizer is a dream come true

0
How to flash Tasmota on ESP devices with new Tasmotizer tool

Smart Home

The quietest roller shade driver I used!

0
Aqara Roller Shade Driver E1 is ZigBee 3.0 wireless and the quietest controller for blinds, rollers and verticals.

M5Stack UnitV2 update brings WiFi

0
M5Stack UnitV2 finally gets much needed WiFi update and I will show you how to make the settings stick

Flashing Tasmota on Sonoff POWR3

0
How to flash tasmota on Sonoff POWR3 and set it up correctly

Amped to 25 – Sonoff POWR3

0
It's bigger, better and comes with massive 25A current limit - for all your present needs - meet Sonoff POWR3

SwitchBot made an IP Camera

0
SwitchBot Indoor Cam is small and inexpensive 1080p camera with smart assistant support and 2 way communications