External Lighting Control

Why?

Let me start from the fact that I am not a fan of controlling my Smart Home from the public cloud based applications (Smart Plugs connected to Alexa/Siri/Google etc.). I will write a post on why separately, I promise! This is why I am choosing Tasmota flashed Smart Plugs. They allow you local control as well as cloud based if you wish, so best of both worlds. The ones I am using for my Smart Home control are below, but literally any Tasmota flashed ones will do.

TASMOTA Smart Home preflashed WiFi

Although they (and most of the other smart plugs) are capable of turning your lights on/off based on the Sunrise/Sunset, I often find these not scientific enough, and, not fun enough! They don’t know your precise geographical location (GPS Coordinates a.k.a. Latitude and Longitude) and the time of Sunset/Sunrise is different in every location.

So, we want to turn our outdoor lights on in the evening and turn them off in the morning, right? And what time if so. Let’s have a brief look at what a twilight (dusk or dawn, this is when we want the on/off to happen) really is and how do we introduce some science (and fun!) into controlling our external lighting.

By the way, I noticed many large car parks where lights turn on too early. Doing so potentially costs businesses big money (compared to us, DIY-ers just having fun) as well as puts an unnecessary load onto power grid too early. Introducing something similar into their control can help them to save quite substantial amounts of power.

Twilight Zone

Let’s now have a look into some science as what the sunset/sunrise and twilight actually are. As you might be thinking, the sunrise/sunset is when the sun is exactly 0 degrees of the horizon. This is very close to the truth. There is an exact definition of astronomical Sunset on Wikipedia: “The time of actual sunset is defined in astronomy as two minutes before the upper limb of the Sun disappears below the horizon.”

Is this the correct time to turn our ambient light on? The answer is no! Don’t forget, at this point part of the sun is still above the horizon, and it is pretty light outside. It is not even a twilight yet, which starts only when the entire sun disappears over horizon, so slightly past sunset.

Is it a good time now? Not yet! In astronomy, they define three so-called “stages” of a twilight, and using them smartly will allow you to find your perfect moment and make sure you are turning your lights on and off at the optimal time. I strongly encourage you to read a Wikipedia article to get a better understanding of what twilight stages are:

https://en.wikipedia.org/wiki/Sunset

From my experience, I prefer midpoint of the civil twilight (3° below the horizon). What I suggest you do is try turning your lights on at different sun angles and see which one you prefer.

Book your time!

If you want to do the same thing I did, you will need to spend one (or more) evenings outside in your garden. Make sure you have you drink and phone/laptop ready, and prepare to take notes whilst watching Solar “elevation” angle. Technically the elevation will be negative at the time you will want your lights to be turned on, however, we will still call it “elevation” angle.

I use Infocom community’s free API to get solar elevation angle in my area. It is reasonably precise and simple to use in the script. You can obtain a free token here:

https://community.infocom.uk/client

Your token will look similar to 11a594a6a3d290ae435b7776138a4a81b and you will be able to get solar elevation reading using the API link below. Let’s also get GPS coordinates (latitude and longitude) of the point of interest (your home). We will take The British Museum in London as an example. Click it in Google Maps, then right click and you will see GPS coordinates in the first line of the menu:

Let’s check the angle of the altitude!

We are all set, let’s try it in action. Copy the below URL into text editor (e.g. notepad) and change the “token”, “lat” and “lon” values to your ones. Then upen the URL you just created in your browser:

https://api.infocom.uk/solar/altitude/?token=11a594a6a3d290ae435b7776138a4a81b&lat=51.52181&lon=-0.12718

You will receive a JSON output similar to the below:

{ “value”:19.73, “message”:”This is the current solar altitude at [51.52181, -0.12718]” }

Or, if you view page source:

{
   "value":19.73,
   "message":"This is the current solar altitude at [51.52181, -0.12718]"
}

As we can see, the sun is way above the horizon at the moment. In the evening, when it will get closer to 0 degrees – grab your drink and start observing. Keep refreshing the page every minute or so and take your notes down, e.g.:

  • -1.23 – still no need for lighting
  • -3.67 – think this is the good point
  • -6.12 – now it is too dark – need to turn lights on a bit earlier

What we will need

As we will be executing scripts to get the data from API and control our Tasmota flashed local Smart Plug, we will need a Linux device (or multiple) on your LAN. I am using two laptops for all my Smart Home automation tasks (I will write a separate article on this at some point) but you can use anything like Raspberry Pi or any other Linux based, even virtual machine on your NAS, for example. As long as it can run Linux, has internet access (to connect to API) and can communicate to your Smart Plug devices on your LAN – you are good to go.

Hardware

This is a very simple part. Just plug your external lighting (I use 18V power supply on my one) into Smart Plug and you are done. Test if you can turn your lighting ON and OFF by pressing “Toggle” button on your Smart Plug interface:

Software

I will be using bash to script the automation side. I know people prefer Python these days – feel free to produce a Python script and I will be more than happy to upload it here if you like to contribute.

Please install ‘bc’, ‘jq’ and ‘curl’ on your Linux box. For Debian/Ubuntu and likes that use apt, this would be something like (remove sudo if not using and running as root):

sudo apt install bc jq curl

Now copy the following script into your Linux box, any location you want. I am using “~/lights.sh” on my box. Don’t forget to edit the settings at the top of the script, including your API token. Don’t forget to change “192.168.0.88” to the IP address of your Smart Plug as well. When creating the below script, I prioritised simplicity and readability for the beginners, and despite some bits might have been done using less lines or more advanced syntax – that was not my objective.

#!/bin/bash

# Replace the below values as needed. You can obtain free API token
# at https://community.infocom.uk/client/

target="-3.0"
lat="51.52181"
lon="-0.12718"
smartplug="192.168.0.88"
token="11a594a6a3d290ae435b7776138a4a81b"
debug=1

# Let's get the solar elevation from API

url="https://api.infocom.uk/solar/altitude/?token=${token}&lat=${lat}&lon=${lon}"

altitude_str=`/usr/bin/curl -s \
	--connect-timeout 10 --max-time 30 \
	--retry 2 --retry-delay 0 --retry-max-time 25 $url`
rc1=$?

elevation=`echo $altitude_str | jq -r '.value'`
rc2=$?

if [[ $debug -eq 1 ]]
then
  message=`echo $altitude_str | jq -r '.message'`
fi

regex='^[+-]?[0-9]+([.][0-9]+)?$'
if ! [[ $elevation =~ $regex ]]
then
  data=`date`
  echo $data - Error: Not a number
  exit 1
fi

check1=`echo $elevation'<='90 | bc -l`
check2=`echo $elevation'>='-90 | bc -l`

if [[ $debug -eq 1 ]]
then
  echo DEBUG: API value:   $elevation
  echo DEBUG: API message: $message
fi

# Only proceed if we downloaded and decoded the elevation successfully
# and value is within range of -90 to +90 degrees:

if [[ $rc1 -eq 0 && $rc2 -eq 0 && $check1 -eq 1 && $check2 -eq 1 ]]
then
  data=`date`
  ffile="/run/shm/${smartplug}.on"
  isless=`echo $elevation'<'$target | bc -l`
  if [[ "$isless" -eq "1" ]]
  then
    if [[ ! -f $ffile ]]
    then
      echo $data - Turning ON as sun is now lower than $target: $elevation
      url="http://${smartplug}/cm?cmnd=Power%20On"
      result_str=`curl -s \
        --connect-timeout 10 --max-time 30 \
        --retry 2 --retry-delay 0 --retry-max-time 25 $url`
      result=`echo $result_str | jq -r '.POWER'`
      if [[ "$result" = "ON" ]]
      then
        data=`date`
        /bin/touch $ffile
        echo $data - Success
      else
        data=`date`
        echo $data - Unable to turn ON - will retry on the next run
      fi
    else
      if [[ $debug -eq 1 ]]
      then
        echo DEBUG: $data - Already ON - No actions needed
      fi
    fi
  else
    if [[ -f $ffile ]]
    then
      echo $data - Turning OFF as sun is now higher than $target: $elevation
      url="http://${smartplug}/cm?cmnd=Power%20Off"
      result_str=`curl -s \
        --connect-timeout 10 --max-time 30 \
        --retry 2 --retry-delay 0 --retry-max-time 25 $url`
      result=`echo $result_str | jq -r '.POWER'`
      if [[ "$result" = "OFF" ]]
      then
        data=`date`
        /bin/rm $ffile
        echo $data - Success
      else
        data=`date`
        echo $data - Unable to turn OFF - will retry on the next run
      fi
    else
      if [[ $debug -eq 1 ]]
      then
        echo DEBUG: $data - Already OFF - No actions needed
      fi
    fi
  fi

else
  data=`date`
  echo $data - Error: Altitude value out of range: $elevation
  message=`echo $altitude_str | jq -r '.message'`
  echo $data - Server Response: $message
fi

Testing time!

Now you have your script in “~/lights.sh”, let’s make it executable and run it!

cd ~
chmod +x lights.sh
./lights.sh

You should see an output similar to the below:

user@linux:~/$ ./lights.sh
DEBUG: API value: 6.82
DEBUG: API message: This is the current solar altitude at [51.52181, -0.12718]
DEBUG: Wed 13 Nov 15:12:02 GMT 2024 - Already OFF - No actions needed

Now change the target setting in the script to a “100” and re-run:

user@linux:~/$ ./lights.sh
DEBUG: API value: 6.46
DEBUG: API message: This is the current solar altitude at [51.52181, -0.12718]
Wed 13 Nov 15:15:11 GMT 2024 - Turning ON as sun is now lower than 100: 6.46
Wed 13 Nov 15:15:11 GMT 2024 - Success

Now set the target to “-100” and re-run the script again:

user@linux:~/$ ./lights.sh
DEBUG: API value: 6.34
DEBUG: API message: This is the current solar altitude at [51.52181, -0.12718]
Wed 13 Nov 15:16:34 GMT 2024 - Turning OFF as sun is now higher than -100: 6.34
Wed 13 Nov 15:16:34 GMT 2024 - Success

Congratulations, it is working as expected!

Scheduling periodic run

Now that we are happy with our script, let’s schedule it to run every minute. It will check the altitude, compare to your target and turn the lighting on and off accordingly. There will be a delay of up to one minute because of that, please keep that in mind if you are trying to set very precise angle setting in the script.

To schedule periodic runs we will use “crontab -e” command and add a line below:

* * * * *       cd ~/; ./lights.sh 1>/dev/null 2>&1

Save the crontab file and you are all done! I bet you will now be watching your lights turning ON and OFF just like I did for the first few days. Your external lighting is now managed according to science. Enjoy!