Astronomical Data API



Having sunrise and sunset times for automation is great but…

  • By default, both of these variables are limited to a resolution of whole minutes. I want that resolution down to the second.

  • For my lighting, I want to be able to do some calculations for controlling the levels based on things like twilight, dusk, dawn, etc.

I created this piston that calls an API which gives me the data and resolution I wanted. I have it setting global variables that I then use in various pistons. For example, I have one piston (which I’ll post separately) that sets the levels of my light switches based morning and afternoon twilight as well as the mode so the next time they are turned on at the switch, their level is set appropriately, so I don’t get blinded by full brightness in the morning, evening, or at night.

Here are the variables it sets:


The two boolean flags change dynamically. During the morning and evening twilight periods, @twilight automatically changes to true. @shabbat can be used so devices won’t be turned on when @shabbat = true.

Initially, I was only getting whole seconds in the variables despite the API returning data to the second. It took me a while to figure out there is a bug in the webCoRE’s datetime function. The ISO 8601 specification uses hyphens as the date delimiter but webCoRE expects slashes. If your string has hyphens, the function drops the date and only captures the time portion of the string. To get around the bug, it works if you substitute slashes for hyphens (e.g. 2020-01-02T19:05:30+00:00 becomes 2020/01/02T19:05:30+00:00). The piston accounts for that bug when parsing the API data which is why it looks more complicated than it should.

There is no way to programmatically create global variables so you’ll need to create them manually one you load the piston. If there are variables you don’t think you’ll ever use (e.g. day_length) you can delete the line that sets that variable from the piston and just don’t create the global for it.

To make this work, the only thing you need to change is are the latitude and longitude constants. Just copy yours from the IDE or your cell’s GPS.

Due to webCoRE’s obfuscation of URIs, here is the URI to paste in to the dialog box when you import the piston or replace in the GET request:{latitude}&lng={longitude}&formatted=0

Based on your personal preference, you can change which of the three astronomical twilights you wish to use for dawn, dusk, or the twilight periods. There are some really good posts on

The piston to fire (three hours before sunset) so it wouldn’t be impacted by daylight saving time. It also helps to randomize the load to the API server.

I really like being able to set actions based on dusk and dawn and being able to set lights to ramp their brightness from actual sunset to dark.

I hope this is helpful Enjoy!

Ramp light levels based on twilight and mode
Ramp up the lights and close the shades when it's dark
Gauge for Length of Day (showing Solstices & Equinoxes)

Thank you for your contribution…

To help keep organized, here is the link to the piston that sets the lights.


I’m wanting to import this, but the import code “ubxfx” is not working, getting an error. Any thoughts?


What error are you getting? I just tried it myself and it worked fine.


The following banner pops up at the top


Of note the other piston imported flawlessly.


I just tried that code again and it works for me.


I would highly recommend removing that red snapshot, @MHedish.
(as well as the import code in your text as well)

Red import code are specific to your installation, and can not be used by others to import.
(but it can be used by nefarious people)


Tried this and got the same error. Must be something I am doing wrong. I would also remove the red snip/code per ^ as this can be used for nefarious purposes.

Thanks for trying though!


I don’t have any PII in that code so I wasn’t worried about someone else viewing it or being able to import it. I wasn’t aware that others couldn’t import it to their instance.

When I use the green snapshot webCoRe strips out the URI used for the API call. Catch 22.

What’s your recommendation for sharing a piston like this? Paste the green snapshot and then adding the URI in the text here so other can paste it in when they import?


put the URI in a comment so folks know to edit it


That works well… although I like to keep it attached to the image for future reference…

You can make the URL into a string variable up top in the define section…
(those are never anonymized, and works right out of the box)

or if the user can copy/paste, you can add it in a comment line…



I just reposted it with the URI in the update. I’ll probably go with your suggestion to move it to a string in the next update.

Thanks for your help and patience. My coding is fine, learning how to share it here was the struggle.


Wanted to report that all imported cleanly and I’m starting testing this out in my home! Great piston set!

One featureset I’ve seen elsewhere that I would like to see is a “weather factor” (cloud cover factor or lux factor or fog factor, etc.) that can offset these values. This would make it a bit better on cloudy mornings.


Imported as well and using these as new global variable conditions on lighting triggers. Thanks!



I’ve been working on some updates to the Astronomical API that creates tiles based on the data:

I’ll post the new piston code once I’m happy with it.

As for the WX info, none of that should change this API. I have been playing with the TWC data.

I suppose the TWC data could be used to alter the lighting piston (e.g. check hourly and if partly cloudy or cloudy, turn on the lights). Is that what you had in mind?


Honestly I’m thinking of an IF Cloudy THEN offset = offset + cloudoffset or something. That way I could set cloudoffset as 30. Only problem is limiting it to 100, but you seem way more experienced in that stuff that I am!


I see where you’re going now. Not sure it’d produce the effect you want but… buckle up 'cause here we go!

The available TWC data doesn’t tell us the type of cloud cover. 100% cumulonimbus is going to have a different impact to our lighting needs than 100% altostratus. In some instances, cloud cover can actually increase the ambient light level such as thunderstorm to the west reflecting light in the morning. All we know is the percentage of cloud cover.

Also, the TWC data cannot be used as a trigger:

It is unfortunate, but a weather alert cannot be used as a trigger. To get alerts for your area, you can program a piston to periodically make a query. (mine is set to run every 15 minutes, but other triggers can work as well, such as right before you go to work) Please program strategically so we don’t hammer the weather server too frequently. Any more often than 4 times an hour is a waste of resources and pointless for alerts. We don’t want to encourage SmartThings to start charging us for this data. (since they are billed for each query)

There’s no way to trigger a lighting event based on a weather alert. Granted, cloud cover isn’t a weather alert but the concept is the same, we can’t adjust the lights because the clouds are rolling in. If that’s what you’re after, getting a sensor that measures lux is the way to go. While we could set up a polling loop to get the current cloud cover condition, that is very costly from a resource perspective. I wouldn’t set this up to check the current conditions every 15 minutes.

The best we can do is to use the $twcweather.conditions.cloudCoverPhrase variable to get the forecasted cloud cover when a lighting event is triggered and adjust based on that.

Assuming 100% cloud cover is “dark” and 0% cloud cover is “light” we can use the forecasted conditions (they update twice a day) to create the adjustment factor you seek. We can adjust both the day_level and twilight_level based on the percentage of cloud cover. The night_level is our absolute lowest setting. Once night hits, we wouldn’t need to make any adjustments based on cloud cover.

Here’s a chart showing the adjustment:



  1. If we normally set the daytime lights to 90% of full, we would set them to 65% of full if there was a prediction for 50% cloud cover.
  2. If we normally set the daytime lights to 90% of full, we would set them to 87% of full if there was a prediction for 10% cloud cover.
  3. If we normally set the twilight levels to 40% of full, we would set them to the default night level (10%) if there was a prediction for 100% cloud cover. This presumes the cloud cover makes the ambient light the same as night since the clouds are obscuring any sunlight and moonlight.

Here’s an updated piston that has the adjustment ready to go: r3y84

If you wanted a less dramatic change, you could adjust the $twcweather.forecast.daypart[0].cloudCover[x] values by dividing them by two (or three) which would cut the effect proportionally.


I hope this is helpful. Let me know if you give it a try and it does what you’re after. As always, YMMV.



I’ve updated the original piston with some additions (tiles, status, etc.) and split it in to two separate pistons per this thread.


Astronomical Data API: tybg

Astronomical Data Synodic: e8kiz

The major change is having the parent piston restart the child pistons (or any others you create) so they immediately subscribe to the updated times from the API call.

You’ll need the synodic API if you want to do any triggers based on the twilight and/or shabbat booleans. Update the API piston (lines 69-73) with any of your pistons that use the global variables and you’re good-to-go.


:Jawdrop: thanks!!! This is amazing. Implemented widespread throughout my home. Will report back if any issues arise!


:wink: Happy to help.