Rules and Expressions with Open Weather API

I would add that you can combine that all together into a single expression to build your string or use a fallback string.

You can even do it with expression-scoped variables so you don’t have to have a bunch of other global variables if you don’t need them.

alerts = $context.response.data.alerts

hasAlerts = count(alerts) > 0

myString = map(alerts, concat(x.event, " until ", formatDate(x.end * 1000, "h:mm a")))
join(myString, "\r\n")

hasAlerts ? myString : "No alerts"

So from top to bottom:

  1. Alias the alerts data with a simpler (expression-scoped) variable
  2. Check if we even have alerts
  3. Build the alert string to format the various (potential) events
  4. If we have events, show the string, otherwise show a default message
2 Likes

Wow, this is good!

(The alerts field doesn’t exist in the feed when there are no alerts, for anyone down the road…)

I tried both the Initialize Array and the Conditionally Count options and they both work great!

Thanks so much.

1 Like

I’ve found this site to be very helpful in parsing json to give me the proper sytanx: https://jsonpathfinder.com/ just paste in the reply you get from the API and then click around to build the expression

1 Like

Can the map function be nested? Or maybe there is a different way to think about this?

I’m trying to cycle through two nested arrays and I can only grab the first value of the second array. On the below json, I’m trying to cycle through both the ‘games’ array and the ‘promotions’ array. The code that I have is below (which somewhat works as it will grab the 1st promotion name, but won’t grab the second. The code is the same basically as the above, but the difference is that the values in the promotion can be an array.

Thoughts on how to handle?

What I’m looking to return is:
2023 Magnetic Schedule Gate Giveaway
2023 Magnetic Schedule Gate Giveaway
Friday Night Fireworks (this is what isn’t returned in the below code)

schedule = $context.response.data.dates
myString = map(schedule, x.games[0].promotions[0].name)              
join(myString, "\r\n")

The json that the above code is referencing is (coming from here):

{
    "dates": [
        {
            "date": "2023-04-06",
            "games": [
                {
                    "gamePk": 718681,
                    "gameDate": "2023-04-06T23:20:00Z",
                    "officialDate": "2023-04-06",
                    "promotions": [
                        {
                            "name": "2023 Magnetic Schedule Gate Giveaway",
                            "description": "All in attendance will receive a 2023 magnetic schedule. ",
                            "order": 0,
                            "offerType": "Giveaway"
                        }
                    ],
                    "description": "Braves home opener"
                }
            ]
        },
        {
            "date": "2023-04-07",
            "games": [
                {
                    "gamePk": 718676,
                    "gameDate": "2023-04-07T23:20:00Z",
                    "officialDate": "2023-04-07",
                    "promotions": [
                        {
                            "name": "2023 Magnetic Schedule Gate Giveaway",
                            "description": "All in attendance will receive a 2023 magnetic schedule. ",
                            "order": 0,
                            "offerType": "Giveaway"
                        },
                        {
                            "name": "Friday Night Fireworks",
                            "description": "Following every Friday night game, the sky above Truist Park lights up with the #1 rated fireworks show in the Southeast! Every show is different. See them all!",
                            "order": 1,
                            "offerType": "Day of Game Highlights"
                        }
                    ]
                }
            ]
        }
    ]
}

Yes, you could map within a map for nested arrays. Something like the following:

schedule = $context.response.data.dates
names = flatten(map(schedule, map(x.games, map(y.promotions, z.name))))
join(names, ", ")

The second line is doing all the work. Basically iterating through the dates → games → promotions → name which results in a nested array of just the names and then flattens all that nesting into a single top-level array.

There’s some other neat tricks you can do with nested arrays like concatenating data from the top level map iteration in with the lower-level iteration:

schedule = $context.response.data.dates
names = flatten(map(schedule, map(x.games, map(y.promotions, concat(x.date, "▸", z.name)))))
join(names, "\r\n")

Which would result in:

2023-04-06▸2023 Magnetic Schedule Gate Giveaway
2023-04-07▸2023 Magnetic Schedule Gate Giveaway
2023-04-07▸Friday Night Fireworks

(PS. I had to push an update to support this as expressions were previously using matrix iteration which didn’t work with mismatched nested array sizes)

This is great!!!

Can I also grab data not in the top level but at the previous level?

I thought I could do something like this:

schedule = $context.response.data.dates
names = flatten(map(schedule, map(x.games,isEmpty(y.promotions) ? "-" : #// not all games have promotions
 map(y.promotions, concat(z.name, " ( ",formatDate(x.games.gameDate, "ddd"), " | ", formatDate(x.games.gameDate, "h:mm a"),")")))))
join(names, ", ")

I was expecting the output to look like:


Magnetic Schedule Gate Giveaway (Thur | 7:05 pm) #// or whatever the time is
Magnetic Schedule Gate Giveaway (Fri | 7:05 pm) #// or whatever the time is
Friday Night Fireworks (Fri | 7:05 pm) #// or whatever the time is

You can, but you have to reference the variables and properties with the appropriate context.

In your example, y is equivalent to a ‘game’ object (eg. one of the entries in the x.games array), so you would use y.gameDate as the reference.

I just used x, y, z as arbitrary names as most of my map() examples just are working on a single item and x is a generic name. You could use a different variable name if you prefer.

schedule = $context.response.data.dates
names = flatten(map(schedule, map(scheduleEntry.games, isEmpty(game.promotions) ? "-" : #// not all games have promotions
 map(game.promotions, concat(promo.name, " (",formatDate(game.gameDate, "ddd"), " | ", formatDate(game.gameDate, "h:mm a"),")")))))
join(names, "\r\n")
1 Like

Hi all,

I’m trying to use the OpenWeather API for the first time and having mixed results. I am not a programmer so apologies if this is basic.

I have my API key and can get results from the API in a browser. I was hoping to leverage Josh’s code snippet to write a rule to populate a text variable with either “No Alerts” or the alert(s) text. Apparently I am doing something wrong. Here is my rule:

When I trigger it, I get image. I’m expecting “No alerts” since there are no alerts in the API response at this time.

I played around a bit and my variable can be populated successfully by just calling $context.response.data.current.weather.0.main, so the API, trigger, and first action are working fine. I hope someone can tell me what I’m doing wrong in the expression.

I don’t know if this helps, but this is what I use.

weather = $context.response.data
alerts = isEmpty(weather.alerts) ? [] : weather.alerts
hasAlerts = count(alerts) > 0


myString = map(alerts, concat(x.event, " until ", formatDate(x.end * 1000, "h:mm a")))
join(myString, "/r/n")

hasAlerts ? myString : "No alerts today!"
1 Like

Thank you, that worked!! Looks pretty similar, I guess the problem was with initializing the arrays in the first two lines.

Hi Vinod_K, when you trigger your rule, do you get [" before and "] after the alert text? I am, and not sure what is causing the extraneous characters.

image

Hmm, I’ll take a look as I haven’t seen an alert for a while.

@josh any ideas how to fix the formatting that @T_Shoaf pointed out? I tried a couple different ways with the replace function but most I could do is get rid of the square brackets.

The join() in your example isn’t doing anything.

Originally, join() was being called as the last line in the expression, so the result of the join() method was being output as the result of the expression. In your current example, you’re calling join(), but not assigning the output of it anywhere, so it’s effectively a meaningless method call. You would want to assign the result of the join() method call to a variable and then reference that variable.

weather = $context.response.data
alerts = isEmpty(weather.alerts) ? [] : weather.alerts
hasAlerts = count(alerts) > 0


myString = map(alerts, concat(x.event, " until ", formatDate(x.end * 1000, "h:mm a")))
myString = join(myString, "/r/n") 

hasAlerts ? myString : "No alerts today!"

The only change I’ve made to your expression is in the second to last line to assign the output of the join() method call to the variable that you use later.

1 Like

Thank you both! I made the change, but no alerts in my area right now so I’ll update after I get to see the alert formatting. Thanks again!

Unreal! Thanks Josh. That worked…here I am reinventing the wheel lol

1 Like

Well, I think it is set up, but I can’t get it to evalute the variables. Here is my rule flow

I added all the excludes because the reponse included everything. Even with the excludes, the excluded stuff is still being received.

I am setting two variables. After the script runs, they evaluate to NaN, which I assume is Not a Number.

Any thoughts on what I am doing wrong?

It looks like your expressions are formatted incorrectly. Each step of the expression has to be on its own line.

:no_entry_sign: WRONG
So instead of:

positions = $context.response.data.current.temp toFixed(positions,1)

 

:white_check_mark: RIGHT
You would need each step on its own line for the syntax to be valid:

positions = $context.response.data.current.temp 
toFixed(positions,1)

The exclude would need to be a single parameter with a comma separated list:

&exclude=hourly,minutely,daily,alert

All is good now. Thanks!

I thought all was well until Openweather told me I used 2000 API calls yesterday. I run a rule that should only execute every 10 minutes to update the weather info

Looking at the log, it seems to be triggering every minute

Am I doing something wrong?