Tuesday, January 21, 2020

#1 2016-10-03 04:47:12 pm

Riku
Member
Registered: 2016-05-23
Posts: 51

Treating retrieved JSON results as times to compare with current time

Haven't been doing Applescript for too long, and now I find myself dealing with JSON for the first time.

So I have wifi lights and I frequently use my own Applescript "normalize lighting conditions", which first checks what time it is now, then goes through a list of conditions such that "if the time now is between hours x and x, adjust the lighting like so". The point is that "normal lighting" depends on how dark it is outside, the lighting should promote energized mood in the daytime and aid sleepiness later on. But in my geographic location, the seasons vary so dramatically that in the summer the night barely comes, and in the winter, the opposite is true. The only way to keep this script on point throughout the year is to make it understand the sun's position.

I found this API for sunset and sunrise times.
I got JSON Helper to help.
As a total JSON newbie I just spent hours and hours just to get everything right and I'm finally able to not just fetch the results but quote an individual one and display it to myself as a notification. (not the result I need, just used the notification action to indicate if and when the script actually works).

In my earlier time comparison scripts, I've used this hour only form, here's a little demo in the spirit of it:

Applescript:

---Asking what time it is
set rightNow to (get current date)
set hoursNow to hours of rightNow
delay 3
---Act based on what time it is
if (hoursNow ≥ 7) or (hoursNow ≤ 23) then
   display notification "You should be in bed."
else if (hoursNow ≥ 22) or (hoursNow ≤ 8) then
   display notification "Good luck staying awake."    
end if

With JSON Helper installed, this test script with test coordinates finally gives me just one of the results, in this case, the sunrise time:
(the others are there in place at this point for nothing, but will actually be necessary in the final script because the time comparison will have to perform quite a bit of cross-checking.)

Applescript:

tell application "JSON Helper"
   
   set AstroQuery to fetch JSON from ("[url]http://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400[/url]")
   set nowSunrise to sunrise of results of AstroQuery as string
   set nowSunset to sunset of results of AstroQuery as string
   set nowSolarNoon to solar_noon of results of AstroQuery as string
   set nowDayLength to day_length of results of AstroQuery as string
   set nowCivilTwilightBegin to civil_twilight_begin of results of AstroQuery as string
   set nowCivilTwilightEnd to civil_twilight_end of results of AstroQuery as string
   set nowNauticalTwilightBegin to nautical_twilight_begin of results of AstroQuery as string
   set nowNauticalTwilightEnd to nautical_twilight_end of results of AstroQuery as string
   set nowAstronomicalTwilightBegin to astronomical_twilight_begin of results of AstroQuery as string
   set nowAstronomicalTwilightEnd to astronomical_twilight_end of results of AstroQuery as string
   display notification nowSunrise
   
end tell

But the result of it is in the excessive form of "6:15:25 AM". I'd like it to be in 24 hour form, and the seconds are an unnecessary piece of information when it comes to determining the luminosity level of the sky. Is there any smart way to manipulate the result into 24-hour form and get rid of the seconds?
And from there, how would I make this format comparable to my Mac's local time now that the minutes would be included too?
Some kind of "if the last two characters of result includes 'PM', then remove the last three characters of result, take the first number of result and add 12, then take this newly tweaked result and whatever characters come before the first separator character, set it as Hour24" -logic?

With my old logic, I manage to isolate the current minutes in the same way as I do hours:

Applescript:

---Test to retrieve hours and minutes
set rightNow to (get current date)
set minutesNow to minutes of rightNow
set hoursNow to hours of rightNow

display notification hoursNow
delay 3
display notification minutesNow

From there on again apparently not quite experienced enough to figure out how I would combine the retrieved hours and minutes into one, I get errors when I try to include both green items in the same test notification. Even so – in this whole project, the time separator character might not even be necessary, if inserting it back in there again would require additional steps.


Filed under: Time, comparing, JSON, results

Offline

 

#2 2016-10-04 04:58:11 am

DJ Bazzie Wazzie
Member
From:: the Netherlands
Registered: 2004-10-20
Posts: 2809
Website

Re: Treating retrieved JSON results as times to compare with current time

When you have AppleScript Toolbox.osax(AST) installed (you need the latest version) you can easily create date objects from strings using a formatter and get them as strings later using a formatter as well. Notice the formatted=0 parameter in the query because ISO time is more accurate.


The reason not grabbing the time from the string is that the API returns GMT time:

http://sunrise-sunset.org/api wrote:

NOTE: All times are in UTC and summer time adjustments are not included in the returned data.


AST will take time zone into consideration including daylight saving time (summer time). You can also do the trick with AppleScriptObjC using a date formatter as well.

Applescript:

tell application "JSON Helper"
   set AstroQuery to fetch JSON from ("[url]http://api.sunrise-sunset.org/json?lat=52&lng=6&formatted=0[/url]")
   set nowSunrise to my isoDateTimeString2Date(sunrise of results of AstroQuery)
   set nowSunset to my isoDateTimeString2Date(sunset of results of AstroQuery)
   set nowSolarNoon to my isoDateTimeString2Date(solar_noon of results of AstroQuery)
   set nowDayLength to day_length of results of AstroQuery as string
   set nowCivilTwilightBegin to my isoDateTimeString2Date(civil_twilight_begin of results of AstroQuery)
   set nowCivilTwilightEnd to my isoDateTimeString2Date(civil_twilight_end of results of AstroQuery)
   set nowNauticalTwilightBegin to my isoDateTimeString2Date(nautical_twilight_begin of results of AstroQuery)
   set nowNauticalTwilightEnd to my isoDateTimeString2Date(nautical_twilight_end of results of AstroQuery)
   set nowAstronomicalTwilightBegin to my isoDateTimeString2Date(astronomical_twilight_begin of results of AstroQuery)
   set nowAstronomicalTwilightEnd to my isoDateTimeString2Date(astronomical_twilight_end of results of AstroQuery)
end tell

display notification (AST format date "'The Sun goes up at: 'HH:mm" of date nowSunrise)

on isoDateTimeString2Date(str)
   return AST date with format "yyyy-MM-dd'T'HH:mm:ssZ" date string str
end isoDateTimeString2Date

Last edited by DJ Bazzie Wazzie (2016-10-04 05:05:01 am)

Offline

 

#3 2016-10-04 06:23:42 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 6143

Re: Treating retrieved JSON results as times to compare with current time

DJ Bazzie Wazzie wrote:

You can also do the trick with AppleScriptObjC using a date formatter as well.


And if you're going to do that, you might as well dispense with JSON Helper too:

Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set theURL to current application's |NSURL|'s URLWithString:"http://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&formatted=0"
set theData to current application's NSData's dataWithContentsOfURL:theURL
set AstroQuery to (current application's NSJSONSerialization's JSONObjectWithData:theData options:0 |error|:(missing value)) as record
set nowSunrise to sunrise of results of AstroQuery as string
set HHmmString to my HHmmFromRFC3339:nowSunrise

on HHmmFromRFC3339:dateString
   set df to current application's NSDateFormatter's new()
   df's setLocale:(current application's NSLocale's localeWithLocaleIdentifier:"en_US_POSIX")
   df's setDateFormat:"yyyy-MM-dd'T'HH:mm:ssZZZZZ"
   df's setTimeZone:(current application's NSTimeZone's timeZoneForSecondsFromGMT:0)
   set theDate to df's dateFromString:dateString
   df's setTimeZone:(current application's NSTimeZone's systemTimeZone())
   df's setDateFormat:"HH:mm"
   return (df's stringFromDate:theDate) as text
end HHmmFromRFC3339:


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#4 2016-10-04 04:54:07 pm

Riku
Member
Registered: 2016-05-23
Posts: 51

Re: Treating retrieved JSON results as times to compare with current time

Ah that's definitely excellent so I don't have to deal with the AM/PM format at all!
I had to pick one so I picked DJ Bazzie Wazzie's example to follow because with my current skills I understood its content better. (Of course I can copypaste in solutions I don't yet understand, but then I'm not very independent in trying to derive manipulations from that.)

Some absolute pain with some things but it compiles. Here's all the solutions explained along the way. I'm not including the actual actions in the end because I haven't written them yet and they wouldn't be useful on other people's computers anyway, but there are demo notifications reporting back every step of the way.
What do you think?

Applescript:

-----Obtaining the astronomical time of the day data from the internet for a specific geographical location.
-----In the internet address, change the numbers coming after lat and lng to correspond the latitude and longitude values of your location, in the same decimal format as you see there. The numbers there now point to New York Times Square.

tell application "JSON Helper"
   set AstroQuery to fetch JSON from ("[url]http://api.sunrise-sunset.org/json?lat=40.758896&lng=-73.985130&formatted=0[/url]")
   set nowSunrise to my isoDateTimeString2Date(sunrise of results of AstroQuery)
   set nowSunset to my isoDateTimeString2Date(sunset of results of AstroQuery)
   set nowSolarNoon to my isoDateTimeString2Date(solar_noon of results of AstroQuery)
   set nowDayLength to day_length of results of AstroQuery as string
   set nowCivilTwilightBegin to my isoDateTimeString2Date(civil_twilight_begin of results of AstroQuery)
   set nowCivilTwilightEnd to my isoDateTimeString2Date(civil_twilight_end of results of AstroQuery)
   set nowNauticalTwilightBegin to my isoDateTimeString2Date(nautical_twilight_begin of results of AstroQuery)
   set nowNauticalTwilightEnd to my isoDateTimeString2Date(nautical_twilight_end of results of AstroQuery)
   set nowAstronomicalTwilightBegin to my isoDateTimeString2Date(astronomical_twilight_begin of results of AstroQuery)
   set nowAstronomicalTwilightEnd to my isoDateTimeString2Date(astronomical_twilight_end of results of AstroQuery)
end tell

on isoDateTimeString2Date(str)
   return AST date with format "yyyy-MM-dd'T'HH:mm:ssZ" date string str
end isoDateTimeString2Date

-----Setting the date format to be month first, day last, in order to perceive dates as a chronologically ascending number sequence.
set nowSeason to (AST format date "MMdd" of date nowSunset) as number

-----Comparing the current date number to find out which one of the four calendar seasons we're at (season changes were roughly calculated from the solstices and equinoxes)
-----Note: If you live in the Southern hemisphere and these don't make sense to you, feel free to swap season names and tweak the numbers.
-----Note#2:Where I live, astronomical and nautical twilight don't exist in the summer at all, so in order to avoid errors, those values should not be quoted during the summer season.
if (nowSeason ≥ 809) is true and (nowSeason ≤ 1110 is true) then
   set nowQuarter to "autumn"
   
else if (nowSeason ≥ 1111) is true and (nowSeason ≤ 1231 is true) or (nowSeason ≥ 101) is true and (nowSeason ≤ 210 is true) then
   set nowQuarter to "winter"
   
else if (nowSeason ≥ 510) is true and (nowSeason ≤ 808 is true) then
   set nowQuarter to "summer"
   
else if (nowSeason ≥ 211) is true and (nowSeason ≤ 509 is true) then
   set nowQuarter to "spring"
   
end if

-----Add hashes to the beginning of the rows to disable this testing stage step where you get a notification displaying which season the script found out it is today. If you're done testing, these two rows can be deleted completely.
display notification ("It's " & nowQuarter & ".") with title "Season identification test"
delay 4

----Determine the local time from the user's Mac
set today to current date
set rightnow to hours of today
set rightnow to minutes of today
set currentShortDate to short date string of today
----Represent time as a four digit value that consists of hours and minutes and no separator
set dateTime to (text 4 thru 5) of currentShortDate & (text 1 thru 2) of currentShortDate as string
set clockTime to (hours of today) & (minutes of today) as string as number

-----Add hashes to the beginning of the rows to disable this testing stage step where you get a notification displaying your local time in hours and minutes but no separator. If you're done testing, these two rows can be deleted completely.
display notification ("It's " & clockTime & ".") with title "Local time value formatting test"
delay 4

-----Get the isolated minutes of the astro times and current time
-----Note:If you don't need to perform additional time calculations based on the existing astronomical times, this secation is unnecessary.
set clockTimeMinutes to (minutes of today) as string as number
set nowSunriseMinutes to (AST format date "mm" of date nowSunrise) as number
set nowSunsetMinutes to (AST format date "mm" of date nowSunset) as number
set nowSolarNoonMinutes to (AST format date "mm" of date nowSolarNoon) as number
set nowCivilTwilightBeginMinutes to (AST format date "mm" of date nowCivilTwilightBegin) as number
set nowCivilTwilightEndMinutes to (AST format date "mm" of date nowCivilTwilightEnd) as number
set nowNauticalTwilightBeginMinutes to (AST format date "mm" of date nowNauticalTwilightBegin) as number
set nowNauticalTwilightEndMinutes to (AST format date "mm" of date nowNauticalTwilightEnd) as number
set nowAstronomicalTwilightBeginMinutes to (AST format date "mm" of date nowAstronomicalTwilightBegin) as number
set nowAstronomicalTwilightEndMinutes to (AST format date "mm" of date nowAstronomicalTwilightEnd) as number

------Note:This section is a useful resource only if you might want to mix in arbitrary quotations such as "between the sunrise and five o'clock".
-----Get the isolated hours of the astro times and current time
set clockTimeHours to (hours of today) as string as number
set nowSunriseHours to (AST format date "HH" of date nowSunrise) as number
set nowSunsetHours to (AST format date "HH" of date nowSunset) as number
set nowSolarNoonHours to (AST format date "HH" of date nowSolarNoon) as number
set nowCivilTwilightBeginHours to (AST format date "HH" of date nowCivilTwilightBegin) as number
set nowCivilTwilightEndHours to (AST format date "HH" of date nowCivilTwilightEnd) as number
set nowNauticalTwilightBeginHours to (AST format date "HH" of date nowNauticalTwilightBegin) as number
set nowNauticalTwilightEndHours to (AST format date "HH" of date nowNauticalTwilightEnd) as number
set nowAstronomicalTwilightBeginHours to (AST format date "HH" of date nowAstronomicalTwilightBegin) as number
set nowAstronomicalTwilightEndHours to (AST format date "HH" of date nowAstronomicalTwilightEnd) as number

----Strip the fetched sun position times into the same format as the user's local time got converted into in the previous step. Required.
set nowSunrise to (AST format date "HHmm" of date nowSunrise) as number
set nowSunset to (AST format date "HHmm" of date nowSunset) as number
set nowSolarNoon to (AST format date "HHmm" of date nowSolarNoon) as number
set nowCivilTwilightBegin to (AST format date "HHmm" of date nowCivilTwilightBegin) as number
set nowCivilTwilightEnd to (AST format date "HHmm" of date nowCivilTwilightEnd) as number
set nowNauticalTwilightBegin to (AST format date "HHmm" of date nowNauticalTwilightBegin) as number
set nowNauticalTwilightEnd to (AST format date "HHmm" of date nowNauticalTwilightEnd) as number
set nowAstronomicalTwilightBegin to (AST format date "HHmm" of date nowAstronomicalTwilightBegin) as number
set nowAstronomicalTwilightEnd to (AST format date "HHmm" of date nowAstronomicalTwilightEnd) as number


-----Add hashes to the beginning of the rows to disable this testing stage step where you get a notification listing a few values from the astronomical times list. If you're done testing, these two rows can be deleted completely.
display notification ("Sunrise at " & nowSunrise & ", civil twilight end at " & nowCivilTwilightEnd & ", solar noon at " & nowSolarNoon & ".") with title "Astro time value formatting test"
delay 4


-----THE CONSEQUENCES TO HAPPEN DEPENDING ON THE SEASON-----

----Below, defining two special time concepts that we can use later
----This one creates a time concept of the first 30 minutes of the morning sun visible:
set halfhourAfterCalculation to nowSunriseMinutes
if halfhourAfterCalculation ≥ 30 and halfhourAfterCalculation is not 0 then
   set halfhourAfterSunrise to (nowSunrise + 70) as number
else if halfhourAfterCalculation ≤ 29 then
   set halfhourAfterSunrise to (nowSunrise + 30) as number
end if
----This one creates a time concept of the 30 minutes before actual sunset
set halfhourBeforeCalculation to nowSunsetMinutes
if halfhourBeforeCalculation ≥ 30 and halfhourBeforeCalculation is not 0 then
   set halfhourBeforeSunset to (nowSunset - 30) as number
else if halfhourBeforeCalculation ≤ 29 then
   set halfhourBeforeSunset to (nowSunset - 70) as number
end if

----Start of the part that handles either spring or autumn.
if (nowQuarter contains "spring") or (nowQuarter contains "autumn") then
   ---Every possible consequence that can happen if it's spring or autumn will be below this line.
   ---Here is some demo content to show the idea. Delete and replace with your own.
   
   -----The consequence below happens if it's past sunrise but pre-sunset.
   if (clockTime ≥ nowSunrise) is true and (clockTime ≤ nowSunset is true) then
       display notification "It should be bright outside right now." with title "Consequence test 1"
       
       ----The consequence below happens if it's the darkest it can be.
   else if (clockTime ≥ nowAstronomicalTwilightEnd) is true or (clockTime ≤ nowAstronomicalTwilightBegin is true) then
       -----Under this line go the consequences
       display notification "It couldn't be a more perfect time to stargaze." with title "Consequence test 2"
       
       ----The consequence below happens if it's about to start getting dark.        
   else if (clockTime ≥ halfhourBeforeSunset) is true then
       -----Under this line go the consequences
       display notification "Time to light up the candles." with title "Consequence test 3"
       
       ----The consequence below happens if it's very early.        
   else if (clockTime ≥ nowNauticalTwilightBegin) is true and (clockTime ≤ halfhourAfterSunrise is true) then
       -----Under this line go the consequences
       display notification "You're up early, aren't you!" with title "Consequence test 4"
       
   end if
   
   
   ----Start of the part that handles summer.
else if (nowQuarter contains "summer") is true then
   ----Every possible consequence that can happen if it's summer will be below this line.
   display notification "This part of the script hasn't been written yet but it's summer." with title "Summer consequence test"
   
   
   ----Start of the part that handles winter.
else if (nowQuarter contains "winter") is true then
   ----Every possible consequence that can happen if it's winter will be below this line.
   display notification "This part of the script hasn't been written yet but it's winter." with title "Winter consequence test"
   
   
end if

Actually right now I notice that if this script deduces that it's autumn, but at the time of running, the current time doesn't fall between any of the "if" ranges, there's no handler for that.
Well, those were meant to be examples to demonstrate the format in which to define time range conditions. In the consequences that I'll be doing for my own use, the point is to create a set of conditions that cover the span of the entire 24 hours so a "never mind then" -handler probably doesn't need to exist.

Last edited by Riku (2016-10-04 04:59:03 pm)

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)