Example - Retrieving Calendar Events

Share your macOS, iOS, and iPadOS shortcuts here. Post your shortcut in a new topic and be sure to describe thoroughly (screenshots, full text of any scripts, apps used, etc).
User avatar
DaveL17
Posts: 7291
Joined: Tue Aug 20, 2013 11:02 am
Location: Chicago, IL, USA
Contact:

Example - Retrieving Calendar Events

Post by DaveL17 »

Level of Difficulty: Moderate
Knowledge of Shortcuts: Moderate

This example shows how to retrieve Calendar event information using Shortcuts and a short Python script. This is a basic example in that it only shows the mechanism to get the requested events information back to Indigo. What you do with the data after that is up to you. This example doesn't use Indigo's HTTP API or require authentication. It also runs entirely on the Indigo Server machine (i.e., you can run it from your iPhone, but it won't do anything). It's probably best to be at least somewhat familiar with how to build Shortcuts before attempting to use this example.

Python Script
You'll need a way to run the following Python script--whether it be as an embedded script, linked file or something similar.

Code: Select all

import subprocess

def build_list(my_result):
    """ Construct a list of event tuples """
    result_str = result.decode('utf-8')  # Change bytes object into Unicode string
    result_split = [_.split('\n') for _ in result_str.split(' %% ')]  # Split payload into a list of lists, first on '%%' then on '\n'
    zipped = list(zip(*result_split))  # Zip the lists into a list of tuples (the contents are driven by the shortcut)
    return zipped

num_appt = b'3.0'
result = subprocess.run(['/usr/bin/shortcuts', 'run', 'Get Calendar Events'], input=num_appt, check=True, capture_output=True).stdout
appts = build_list(result)
indigo.server.log(f"{appts}")
There are some nuances about the way this script works.
  • First, to set the number of requested events, the number must be provided as a bytes() object. Values less that 10 must be provided as a float. If you send b'3', it *won't* work (but b'10' *will* work for some reason).
  • Second, the data that the shortcut returns to the script is text data in bytes() format. This script doesn't account for complex objects like file attachments to events.
Shortcut
The shortcut layout:
Screenshot 2023-03-16 at 8.36.09 AM-2.png
Screenshot 2023-03-16 at 8.36.09 AM-2.png (868.63 KiB) Viewed 13386 times
The Shortcuts app isn't currently designed to output "complex" data containers like JSON or ordered lists, so the example uses a simple "hack" to work around this limitation. It puts all the requested information on one line in the list element to ensure that the data are all provided in a single payload. It separates the data elements with [space][percent][percent][space] which is used in the Python script to parse the event data. Setting the event details within the list is a bit tricky (and beyond the scope of this example).

There are some nuances about the way this shortcut works:
  • First, notice the tick mark next to "Provide Output". This option must be enabled or the shortcut won't return anything. Even though it looks like the "Provide Output" setting isn't meaningful because "Use as a Quick Action" is disabled, that's not the case.
  • Second, this shortcut outputs the data as a bytes() object that contains nothing but text. If you're going to try to work with Event attachments or other complex objects, you'll need to base64 encode them (beyond the scope of this example). If you use base64 encoding, you can forego the "Stop and Output" action as base64 encoding outputs by default.
  • Third, "Home" is the name of my calendar. You'll need to change it to one of your named calendars (or set it to examine all calendars).
  • Lastly, for simplicity, this example shortcut doesn't include any error handling.
Result
If the script (and shortcut) run successfully, you'll receive output that looks like this (assuming you haven't changed anything)

Code: Select all

 [('My First Event', 'Mar 20, 2023 at 9:30 AM', 'Mar 20, 2023 at 12:30 PM'), ('My Second Event', 'Mar 21, 2023 at 12:00 AM', 'Mar 21, 2023 at 11:59 PM'), ('My Third Event', 'Mar 24, 2023 at 12:00 AM', 'Mar 27, 2023 at 11:59 PM')]
You can download the shortcut from the Indigo File Library.
I came here to drink milk and kick ass....and I've just finished my milk.

My Plugins and Scripts
digtrail
Posts: 28
Joined: Thu Jun 02, 2016 8:45 am

Re: Example - Retrieving Calendar Events

Post by digtrail »

Dave,
Thank you soooo much for this script. It's close to getting me back to where I want to be w/ calendar integration. I have no problems getting it to run and output to the log. I know my next question is a pretty basic one, but I'm a pretty basic user of Indigo and don't mess with scripting expect on occasion once or twice every couple of years.
So that said, here's my output

Code: Select all

Script                          [('Night Work', 'Nov 29, 2023 at 5:00 PM', 'Nov 30, 2023 at 7:00 AM'), ('OFF', 'Nov 30, 2023 at 12:00 AM', 'Nov 30, 2023 at 11:59 PM'), ('Put keys in Car 10', 'Nov 30, 2023 at 6:45 AM', 'Nov 30, 2023 at 6:46 AM')]

How do I get chosen info from the output above, in this case OFF (it's the all day event that I want) into its correct variable. I can't remember how to call the log and then output from the log and put it into a script. Here's my old script that did that but it referenced another old calendar input that I've left out.

Code: Select all

if "Day Work" in the_string:
   indigo.variable.updateValue(1422763041, value='true')
else:
   indigo.variable.updateValue(1422763041, value='false')

if "Night Work" in the_string:
   indigo.variable.updateValue(15646541, value='true')
else:
   indigo.variable.updateValue(15646541, value='false')

if "OFF" in the_string:
   indigo.variable.updateValue(790632292, value='true')
else:
   indigo.variable.updateValue(790632292, value='false')

if "VAC" in the_string:
   indigo.variable.updateValue(41667262, value='true')
else:
   indigo.variable.updateValue(41667262, value='false')
Thanks!
User avatar
DaveL17
Posts: 7291
Joined: Tue Aug 20, 2013 11:02 am
Location: Chicago, IL, USA
Contact:

Re: Example - Retrieving Calendar Events

Post by DaveL17 »

No problem! I'm glad you found it useful. The line at the bottom of the script

Code: Select all

indigo.server.log(f"{appts}")
is just a placeholder so you can see if the script has worked. You could leave it in if you wanted, but what you're going to need to do is add some more code to the script to process the data that comes in. So ,

Code: Select all

[('Night Work', 'Nov 29, 2023 at 5:00 PM', 'Nov 30, 2023 at 7:00 AM'), ('OFF', 'Nov 30, 2023 at 12:00 AM', 'Nov 30, 2023 at 11:59 PM'), ('Put keys in Car 10', 'Nov 30, 2023 at 6:45 AM', 'Nov 30, 2023 at 6:46 AM')]
is a list of calendar event tuples that you're going to have to parse.

Code: Select all

for appt in appts:
    if appt[0] == "OFF":
        indigo.variable.updateValue(790632292, value='true')
I came here to drink milk and kick ass....and I've just finished my milk.

My Plugins and Scripts
digtrail
Posts: 28
Joined: Thu Jun 02, 2016 8:45 am

Re: Example - Retrieving Calendar Events

Post by digtrail »

Thanks! Got it working with my else statements and all. Great little thing to build off of. Much appreciated.
User avatar
DaveL17
Posts: 7291
Joined: Tue Aug 20, 2013 11:02 am
Location: Chicago, IL, USA
Contact:

Re: Example - Retrieving Calendar Events

Post by DaveL17 »

That's great. Good luck!
I came here to drink milk and kick ass....and I've just finished my milk.

My Plugins and Scripts
User avatar
durosity
Posts: 4810
Joined: Thu May 10, 2012 3:21 pm
Location: Newcastle Upon Tyne, Ye Ol' England.

Re: Example - Retrieving Calendar Events

Post by durosity »

Hey Dave,

This is great stuff. I'm trying to modify this slightly to break it down into events for today and tomorrow. I've run into a slight issue though if a particular day doesn't have any events I get an error at this line:

date, details = event

with the error:

not enough values to unpack (expected 2, got 1)


I've tried to find a way to edit the script to account for this, but I'm coming up short. Any ideas of what I can do to change this?
Computer says no.
User avatar
DaveL17
Posts: 7291
Joined: Tue Aug 20, 2013 11:02 am
Location: Chicago, IL, USA
Contact:

Re: Example - Retrieving Calendar Events

Post by DaveL17 »

Happy to help you get this working. Can you share your work either here or in a PM?
I came here to drink milk and kick ass....and I've just finished my milk.

My Plugins and Scripts
User avatar
durosity
Posts: 4810
Joined: Thu May 10, 2012 3:21 pm
Location: Newcastle Upon Tyne, Ye Ol' England.

Re: Example - Retrieving Calendar Events

Post by durosity »

DaveL17 wrote: Sat Dec 14, 2024 7:27 pm Happy to help you get this working. Can you share your work either here or in a PM?
Here's perfect if that's ok, then if anyone else is trying to make these modifications they can see the solution :)

So here's a link to the slightly modified shortcut:

https://www.icloud.com/shortcuts/d369ae ... dfca12718d

It's basically the original one, just with the output structure slightly amended, and it's set to only show today's events.

Here's the updated script that I've made:

Code: Select all

import subprocess

def build_list(my_result):
    """ Construct a list of event tuples """
    result_str = my_result.decode('utf-8')  # Change bytes object into Unicode string
    result_split = [_.split('\n') for _ in result_str.split(' %% ')]  # Split payload into a list of lists
    zipped = list(zip(*result_split))  # Zip the lists into a list of tuples
    return zipped

# Define Indigo variables
Cal1time = indigo.variables[686646229]
Cal1details = indigo.variables[466610557]
Cal2time = indigo.variables[1097805849]
Cal2details = indigo.variables[1438489604]
Cal3time = indigo.variables[1492511380]
Cal3details = indigo.variables[690843896]
Cal4time = indigo.variables[1637482807]
Cal4details = indigo.variables[983982313]

# Fetch calendar events
num_appt = b'4.0'  # Max number of entries to fetch
result = subprocess.run(['/usr/bin/shortcuts', 'run', 'Get Todays Calendar Events'], input=num_appt, check=True, capture_output=True).stdout
appts = build_list(result)

# Define a list of Indigo variable pairs for time and details
cal_variables = [
    (Cal1time, Cal1details),
    (Cal2time, Cal2details),
    (Cal3time, Cal3details),
    (Cal4time, Cal4details)
]

# Update variables with calendar data, and blank out unused ones
for i, (time_var, details_var) in enumerate(cal_variables):
    if i < len(appts):
        event = appts[i]
        event_time = f"{event[1]} - {event[2]}"
        event_details = event[0]
        indigo.variable.updateValue(time_var, event_time)
        indigo.variable.updateValue(details_var, event_details)
    else:
        indigo.variable.updateValue(time_var, "")
        indigo.variable.updateValue(details_var, "")
It's basically the same, but pops the output into various variables to show on control pages (It's a bit rough at the moment but I have plans to expand this out for multiple day and see if I can also get it to include reminders too!)

Now if I run this and there's an event on the day it works exactly as I want it to. But on days where nothing is listed, I get the error:

Code: Select all

   Script Error                    embedded script error:
   Script Error                    tuple index out of range
   Script Error                    Exception Traceback (most recent call shown last):

     embedded script, line 37, at top level
IndexError: tuple index out of range
Now interestingly that seems to be slightly different to what I was getting last night, and I don't think I've changed anything since, but either way it just doesn't want to run when there's no entries for that day.
Computer says no.
User avatar
durosity
Posts: 4810
Joined: Thu May 10, 2012 3:21 pm
Location: Newcastle Upon Tyne, Ye Ol' England.

Re: Example - Retrieving Calendar Events

Post by durosity »

Oh actually, having now got that different error I've finally been able to work out what to do (with a little help from ChatGPT :D )

The new script is as follows in case anyone is interested in replicating it:

Code: Select all

import subprocess

def build_list(my_result):
    """ Construct a list of event tuples """
    result_str = my_result.decode('utf-8')  # Change bytes object into Unicode string
    if not result_str.strip():
        return []  # Return an empty list if no results
    result_split = [_.split('\n') for _ in result_str.split(' %% ')]  # Split payload into a list of lists
    zipped = list(zip(*result_split))  # Zip the lists into a list of tuples
    return zipped

# Define Indigo variables
Cal1time = indigo.variables[686646229]
Cal1details = indigo.variables[466610557]
Cal2time = indigo.variables[1097805849]
Cal2details = indigo.variables[1438489604]
Cal3time = indigo.variables[1492511380]
Cal3details = indigo.variables[690843896]
Cal4time = indigo.variables[1637482807]
Cal4details = indigo.variables[983982313]

# Fetch calendar events
num_appt = b'4.0'  # Max number of entries to fetch
result = subprocess.run(['/usr/bin/shortcuts', 'run', 'Get Todays Calendar Events'], input=num_appt, check=True, capture_output=True).stdout
appts = build_list(result)

# Define a list of Indigo variable pairs for time and details
cal_variables = [
    (Cal1time, Cal1details),
    (Cal2time, Cal2details),
    (Cal3time, Cal3details),
    (Cal4time, Cal4details)
]

# Update variables with calendar data, and blank out unused ones
for i, (time_var, details_var) in enumerate(cal_variables):
    if i < len(appts):
        event = appts[i]
        event_time = f"{event[1]} - {event[2]}"
        event_details = event[0]
        indigo.variable.updateValue(time_var, event_time)
        indigo.variable.updateValue(details_var, event_details)
    else:
        indigo.variable.updateValue(time_var, "")
        indigo.variable.updateValue(details_var, "")
Computer says no.
User avatar
DaveL17
Posts: 7291
Joined: Tue Aug 20, 2013 11:02 am
Location: Chicago, IL, USA
Contact:

Re: Example - Retrieving Calendar Events

Post by DaveL17 »

Glad you were able to get it working.

ChatGPT doesn't always get it right, but it's been helpful for me in the past, too.
I came here to drink milk and kick ass....and I've just finished my milk.

My Plugins and Scripts
CliveS
Posts: 858
Joined: Sun Jan 10, 2016 5:31 am
Location: Medomsley, County Durham, UK

Re: Example - Retrieving Calendar Events

Post by CliveS »

durosity wrote: Sun Dec 15, 2024 3:35 am Oh actually, having now got that different error I've finally been able to work out what to do (with a little help from ChatGPT :D )
I love ChatGPT, are you using their addons (or what ever they are called) , that are specific to a certain subject like Python, Apple Shortcuts, Home Assistant.
and it understands Indigo and writes Indigo specific code and sorts out Indigo error logs very well.

It has improved over the last few months to the point I am thinking of paying the monthly fee!
Screenshot 2024-12-15 at 11.17.31.png
Screenshot 2024-12-15 at 11.17.31.png (31.28 KiB) Viewed 1694 times
CliveS

Indigo 2024.2.1 : macOS Sequoia 15.6.1 : Mac Mini M2 : 8‑core CPU and 10‑core GPU : 8 GB : 256GB SSD

The best way to get the right answer on the Internet is not to ask a question, it's to post the wrong answer
User avatar
durosity
Posts: 4810
Joined: Thu May 10, 2012 3:21 pm
Location: Newcastle Upon Tyne, Ye Ol' England.

Re: Example - Retrieving Calendar Events

Post by durosity »

DaveL17 wrote: Sun Dec 15, 2024 5:06 am Glad you were able to get it working.

ChatGPT doesn't always get it right, but it's been helpful for me in the past, too.
Indeed, but it's better than me! :D
Computer says no.
Post Reply

Return to “Apple Shortcuts”