‘Spritz that Shit’ is an inside joke. You can change the title if it doesn’t suit you.

A certain cherished member of my household has accumulated a lot of indoor and outdoor plants – 29 indoor ones, dozens of outdoor ones at last count. Plants don’t take care of themselves, and unlike most other living things you might keep near you, will only ask for what they need in frustrating and ambiguous ways, and usually when it’s a bit too late. On second thought, many adults work that way too.

Watering the indoor plants is managed using a spreadsheet that changes colors in disconcerting and heart palpitating ways, but it still tells you what to do and when to do it. Watering the outdoor plants is trickier; you want to account for rain to avoid wasting water and overwatering the plants that, as I mentioned, won’t tell you that you’ve been doing that for months until they’re dying. I definitely don’t have recent personal experience with this.

One day we were talking and the Household Plants Czar was thinking aloud about how annoying it was to keep track of recent rain and know whether to water outdoor plants or not, and how cool it would be if their phone just told them how much it’s been raining recently so they can make a decision.

I thought it would be fun to try to make it happen. I did, and now I want to write about how I did it. The summary is: getting weather data and sending notifications were the easy parts. Dates and timezones, as usual, were the harder parts.

OpenWeatherMap

Nothing will happen without a source of weather data. I searched for an API I could use without having to pay (at least at this early stage) and OpenWeatherMap was at the top of the list. It might’ve even been the first service I checked out, and it looked like it would do exactly what I wanted, so I didn’t keep looking.

The free tier is limited to a small set of APIs, but it ends up being enough. The historical weather data API gives hourly weather conditions for any provided latitude and longitude up to five days in the past. Perfect.

It takes just a few lines of code to get some data:

import requests
API_KEY = '...'
LOCATION = (47.588993, -122.306286)
TIMESTAMP = 1621794493
def get_weather():
    hist = "http://api.openweathermap.org/data/2.5/onecall/timemachine" \
           f"?lat={LOCATION[0]}&lon={LOCATION[1]}&dt={TIMESTAMP}&appid={API_KEY}"
    return requests.get(hist).json()

Notifications

I could resort to automated emails if I had to, but the nicer way is to get a proper iOS notification waiting for you in the morning, telling you exactly how much it’s been raining recently.

I ended up using Pushover, a popular notification service. I primarily chose Pushover because python-pushover exists. The Python client may or may not be actively maintained, but hey, it works now.1 Being able to attach an image to the notification is a nice bonus.

from pushover import Client
pushover_client = Client(PUSHOVER_USER_KEY, PUSHOVER_APP_TOKEN)
client.send_message(MESSAGE, TITLE)

With Pushover you get a 30 day free trial per device, and then you’ll need to hand over $5 (as a one-time payment) for every device you want to send notifications to. Since the payment is per device, not application, all other automations you might set up to the same device are already paid for. Very much worth $5.

Dates, hours, and UTC

There’s a somewhat famous quote that goes like this: “There are only two hard things in computer science: cache invalidation and naming things”. Maybe. I’m no computer scientist, I’m a simple programmer, and in my lower orbit, the hardest problem I always run into is timezones.

I live in the Pacific timezone, but OpenWeatherMap does everything in UTC. So far so normal, most computer things use UTC, and if the API was hourly, this would be no problem. If I wanted the total precipitation for 1 AM, May 23rd, I’d find the UTC equivalent of 2021-05-23 01:00 PDT (which would be 2021-05-23 08:00), hand that to the API, get the response, and move on to the next hour.

The API, however, is daily. I assumed that meant that if I gave it a specific hour, it would give me hourly historical weather for that hour and the next 23. But that’s not how it works, the API’s day is the UTC day.

This means that if I hand the API 2021-05-23 08:00, OpenWeatherMap will give me the hourly weather for 2021-05-23 00:00 to 2021-05-23 23:00, which is 5pm May 22nd to 5pm May 23 Pacific.

This means that for every full Pacific day of weather you want, you’ll need to make two API calls, one for the day, and one for the day after.2

I decided to give up on thinking about my days vs. UTC days, and just accumulate hourly data and then filter them into days later.

The full script

Find it on GitHub.

This is one of those times where I post code I’m almost certain is written in roundabout and inefficient ways. If you get this far, and it’s clear to you how this could’ve been a lot better, please let me know! Also, pull requests are welcome.

  1. The last commit in the library is three years ago. When I first saw that I though no way this is going to work. But it did, which I guess is a testament to the stability of Pushover’s API. ↩︎

  2. I had already written the script assuming the API gave me my requested hour and the 23 hours after that. I think having written that code already made the change even more mind-bending, because I wasn’t thinking of how to write code from scratch, I was thinking of how to repurpose what I had already written to work with the new reality. ↩︎