Never Miss a Zoom Call Ever Again With This Impossible to Ignore Google Calendar Warning
Over the last few years, as I have spent more time sharpening up my coding skills, I've followed the posts and videos of the various tech influencers who have become increasingly popular on social media. I’ve learned a lot from many of these people and a lot of them are obviously super-talented. 
I have noticed, however, that despite their immense talent, huge followings and sometimes rockstar-level egos, many of them use their powers mostly to write code for tasks that feel completely abstracted away from anything I would care about doing in my own life. Some of them frequently say things like “I wouldn’t bother interviewing someone for a job who didn’t graduate from a top-tier CS program or Ivy League school.” And then you look up their company and it’s like… what does your company even do, dude? Like, I know what Facebook does, I know what Apple or Google or Twitch or even Gumroad does. But I read the about page for Xerxes Distributed Solutions and it’s just some word salad about b2b services that makes my eyes glaze over.
On a sort of related note, I think the biggest mistake in education is that so often we are taught to do things without ever being told how that knowledge is relevant to our actual lives. If someone had simply told me that AI was going to take over the world, and that the backbone of machine-learning is mostly just basic algebra and statistics, perhaps I would have paid closer attention to that stuff in high school!  
Why am I telling you all this? Well, I think people learn the best when the learning is done in conjunction with making something useful. So now I will show you how I learned to access APIs via Python (specifically for Google Calendar and the Elgato Key Light) to make a useful tool for anyone who works with a remote team.​​​​​​​
If you’re a compositor like me, you spend most of your day trying to focus on your shots. You are deeply engrossed in your work and try your best to avoid distractions, because any little disturbance will totally ruin your concentration. But you also have a bunch of zoom calls you need to jump on throughout the day. And the start times are also always changing. And if you’re late to one, especially with someone who is way busier and more important than you, you’re going to look like a total jackass.
This happens to me sometimes, and it is super embarrassing. The little Google Calendar pop-ups frequently do not work properly and there seems to be no better solution for some reason. After missing an important one-on-one meeting with a supe last year, I promised him that I would write a Python script that would fix this. I think he thought I was joking, but I actually did it on my next day off. 
Here’s what I needed: 
An alert that is triggered by my company’s Google calendar events that is IMPOSSIBLE TO MISS.
Here’s what I did: 
First I googled to see if a solution already existed. As far as I could tell, it didn’t. Which is stupid. Second, I looked for a guide on how to access the GCal API via Python. This DOES exist. Great. The guide contains some easy-ish instructions on how to access the calendar and they come along with example code that I copy-pasted into my new script to use as a base.
The instructions are in the link, but basically, you will need to authorize API access for your work account, register your app to be used with your GCal, and download a “token” and “credentials” file specific to your account and place them in the same folder as your script. This all might seem a little confusing, but it’s less complicated than filing for unemployment.
You will also need to install the following Google Python modules so the script can access them:
— google-api-python-client
— google-auth-httplib2
— google-auth-oauthlib
OK, now I ran the example script to see if it works. All it really does is set up access to your API and then run a “main” function that looks for the next ten upcoming events on your calendar and prints them out. After confirming that this all works, I can now modify this script for myself.
Like most compositors, my office is about as dimly lit as Dracula's castle. For zoom calls however, I would like to not look like I am in a hostage video, so I purchased an inexpensive zoom-call light from Best Buy from a company called Elgato. It’s nice because I can control it from my computer via wi-fi. I can turn it on and off, adjust brightness and even fine tune the color temperature. Pretty cool for only around a hundred bucks.
I never want to miss another work call ever again, so I decided to hack my Elgato light via its wi-fi and code it to flash on and off a bunch of times as a one-minute warning. I also wanted an air-raid siren to go off at the same time. This combination of annoyances should be impossible to ignore. 
First things first, I need to trigger the alarm and light to go off one minute before my meeting. To do this, I used the “sched” module which allows me to schedule an event that the script will run at a later point in time. I imported the module at the top with the other module imports and then in the main function I initiated the “scheduler” function from the module like this:
scheduler = sched.scheduler(time.time, time.sleep)
Later down in the example code I went to the “for” loop that cycles through your events and then initiates an “if” statement that compares the events listed start time and checks to see if it is greater than the current time (so events that have already happened are ignored):
start = event["start"].get("dateTime", event["start"].get("date"))
start = datetime.datetime.fromisoformat(str(start))
timestamp = time.mktime(start.timetuple())
What does this code do? The first line gets an event from the index of all events in the calendar. This date comes to us in the ISO time format, but the module we are using to schedule the warning notification requires input formatted in “Epoch Time” aka “Unix Time”.
What the hell is “Epoch Time”?! In the 70s, computer scientists at AT&T decided that all computers should be synced to a single agreed upon beginning of time and their chosen beginning of time would be Midnight on January 1st, 1970 in the UTC timezone. Time in this format is simply the number of seconds since the epoch. As I am writing this, it is currently 1704830552 seconds since Midnight on January 1st, 1970. Wow.
OK, so we get the start time of the upcoming event from the calendar. The next line uses the “datetime” module to convert the time into the datetime format which is accepted by the “time” module to be converted into “Epoch Time”. We now have the start time as one single number calculated as seconds. What a world we live in!
if timestamp > time.time():
    specific_time = timestamp - 60
    scheduler.enterabs(specific_time, 1, call_warning, ())
We only want to schedule a warning for the next upcoming event so we run an “if” statement and make sure the event’s start time is greater than the current time. If that is the case we take the event’s time and subtract sixty seconds, so we are scheduling the warning one minute ahead of the event start time. The next line instructs our instance of the scheduler to call the function “call_warning” one minute before the event start time. Finally, we call the “run” method on the scheduler. Now all we have to do is build the “call_warning” function that does all the cool stuff.
Let’s take a look at the call_warning” function:
def call_warning():
    root = Tk()
    root.withdraw()  # Hide the main Tkinter window
In the first three lines, we are calling the declaring the function, declaring the variable “root” as our tkinter instance and using tkinter’s “withdraw” method to hide some UI we don’t need.
        winsound.SND_FILENAME | winsound.SND_ASYNC | winsound.SND_LOOP,
In the next block of code, we are calling the “Playsound” function from the “winsound” module (we also need to import this module at the top of the script). As you might have guessed, this module is for Windows only, but for Linux you can use a module called “playsound”
I am feeding a wav file for a military alarm  into the method that I downloaded from the free stock footage website Pixabay. It’s super annoying. Perfect. 
As mentioned earlier, I also want my Elgato light to flash on and off to signal the one-minute warning, so I need to figure out how to access the light via wi-fi. I found this Youtube tutorial from some eccentric digital nomad in Dubai who already figured out how to do this. How did he do it? First he ran an app called Wireshark that can be used to detect the IP addresses of all the wi-fi enabled devices in your proximity. As you can see in the video, the default address for Elgato lights is so if you get the same light, as I did, you don’t even need to bother running Wireshark to detect the light. Great!
On to the next block of code:
    for i in range(30):
        # To update wifi ip address to specific elgato light, open light controller
        # and click on "advanced settings" button to bring up menu which displays
        # ip address
                "numberOfLights": 1,
                "lights": [{"on": i % 2, "brightness": 50, "temperature": 200}],
In the code above is a “for” loop that runs thirty times. This loop runs the “put” function from the “requests” module. You will, of course, need to import the request module at the top of the script. We need to call this function with two arguments: A string with the Elgato light’s IP address, and a dictionary with the light settings we want the Elgato light to adjust to.
In case you are new to Python, a dictionary is simply a way of storing data in pairs, grouped as "keys" and corresponding "values"
In the above code block, the variable “json” is declared as a dictionary with two keys: “numberOfLights” and “lights”. I only have one light so the value of the first key is 1. The “lights” key has a corresponding value that is… another dictionary! 
The three keys inside this dictionary are “on” which can be set to 0 or 1 whether you want it “on” or “off”, “brightness” which can be set from 0 to 100 and “temperature” which (very annoyingly) does not correspond to the actual Kelvin light temperature that you see in the normal Elgato light UI control panel. I fooled around with it a bit and found that 200 seems to be the magic number for neutral white light, but this doesn’t really matter.
So how did I get the light to blink on and off? If you look at the value for the “on” key, it is “ i % 2”. The variable “i” refers back to the index of the range declared in the for loop. I am using “%” to get the remainder of the value of “i” after being divided by 2. Every time this loop runs, the remainder will either be 0 or 1 so it will alternate “on” or “off” on each loop cycle. 
Finally, I am running the time method “sleep” with a value of .2” so that the loop will pause every .2 seconds to give the light a chance to update its settings. It’s not really designed to flash on and off like a strobe light.
        title="Upcoming Video Call", message="Video Call Beginning in 1 Minute!"
    winsound.PlaySound(None, winsound.SND_PURGE)
    root.destroy()  # Destroy the hidden Tkinter window
In the next code block, I am calling the “messagebox” function from tkinter — I imported it at the top of the script with the line “from tkinter import messagebox” so I don’t need to reference tkinter again. The message that will pop up on the user’s screen is “Video Call Beginning in 1 Minute!” Once the user closes the messagebox, the script will continue to the next line, which calls the “Playsound” function again. Iit is set to “None” so it will stop the air siren from playing. The final line in this block destroys the tkinter object since we are done with it.
And that’s it. If you run this script, it will look for your next event, and one minute beforehand, start flashing your zoom light on and off, blast an air raid siren and send you a warning pop up message. You will never miss an important zoom call ever again.