Intro
All-day events in a Calendar can be annoying since they’ll cover up any non-all-day events happening that day. Here’s a way to use Google Apps Script to make a copy of your Google calendar, but with the all-day events scrubbed out. It’s pretty annoying to set-up a script, but once you do, it’ll run automatically.
Set up Google calendar and get the calendar IDs
You’ll need to create a new Google calendar for the scrubbed events to be copied to. Go to calendar.google.com. Under “Other Calendars” near the bottom left, press the + button and choose “create new calendar”. Name it something like “Homeassistant Scrubbed Calendar” and press the “Create Calendar” button.
On the left of the main calendar.google.com page, you’ll see a list of calendars under “My Calendars”. Hover over the one you want, and on the three-dot-menu that pops up, choose “Settings and sharing”. On this screen, scroll all the way down to the heading “Integrate Calendar”. The first option there is “Calendar ID”. Copy the whole thing down, including the “@group.calendar.google.com” part.
Copy down the IDs for your google calendar that you want to copy from, and the new calendar you created where the non-all-day events will be copied to.
Create the script
In Google Drive, click the “+ New” button on the top left corner, and click New > More > Google Apps Script. This takes you to a new untitled project on script.google.com
Click on the “Untitled Project” header at the top, and rename it “Calendar Scrubber Script”.
First we need to add the Google Calendar service to the project. Under “services” in the left menu, press the “+” button to add a service. Select “Google Calendar API”, choose v3, and hit Add.
In the code editor, delete the default code, and paste the code snippet into the code.gs file. Click the little floppy disc icon on top to save the code.
var SOURCE_CALENDAR_ID = 'primary'; // OR the full calendar id including @something.calendar.google.com
var SCRUBBED_CALENDAR_ID = '[email protected]';
var WEEKS_IN_ADVANCE = 1;
// The maximum script run time under Apps Script Pro is 30 minutes; this setting
// will be used to report when the script is about to reach that limit.
var MAX_PRO_RUNTIME_MS = 29 * 60 * 1000;
/**
* Look through the source calendar and copy all non-all-day events to the scrubbed calendar
*/
function syncCalendarAfterScrubbing() {
// Define the calendar event date range to search.
var today = new Date();
var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + WEEKS_IN_ADVANCE*7);
var lastRun = PropertiesService.getScriptProperties().getProperty('lastRun');
lastRun = lastRun ? new Date(lastRun) : null;
if (isTimeUp(today, new Date())) {
Logger.log('Execution time about to hit quota limit; execution stopped.');
return;
}
// find events that are not all-day in the specified date range.
// Import each of those to the scrubbed calendar.
var count = 0;
var events = findEvents(SOURCE_CALENDAR_ID, today, futureDate, lastRun);
events.forEach(function(event) {
event.organizer = {
id: SCRUBBED_CALENDAR_ID
}
event.attendees = [];
Logger.log('Importing: %s', event.summary);
try {
Calendar.Events.import(event, SCRUBBED_CALENDAR_ID);
count++;
} catch (e) {
Logger.log(
'Error attempting to import event: %s. Skipping.', e.toString());
}
});
PropertiesService.getScriptProperties().setProperty('lastRun', today);
Logger.log('Imported ' + count + ' events');
var executionTime = ((new Date()).getTime() - today.getTime()) / 1000.0;
Logger.log('Total execution time (s) : ' + executionTime); ;
}
/**
* In a given calendar, look for non-all-day events
* in events within the specified date range and return any such events
* found.
* @param {string} cal_id the ID of the source calendar
* @param {Date} start the starting Date of the range to examine.
* @param {Date} end the ending Date of the range to examine.
* @param {Date} opt_since a Date indicating the last time this script was run.
* @return {object[]} an array of calendar event Objects.
*/
function findEvents(cal_id, start, end, opt_since) {
var params = {
timeMin: formatDate(start),
timeMax: formatDate(end),
showDeleted: true
};
if (opt_since) {
// This prevents the script from examining events that have not been
// modified since the specified date (that is, the last time the
// script was run).
params['updatedMin'] = formatDate(opt_since);
}
var results = [];
try {
var response = Calendar.Events.list(cal_id, params);
results = response.items.filter(function(item) {
// Filter out events where the the event is all-day
if (item.start.date) {
return false;
}
return true;
});
} catch (e) {
Logger.log('Error retriving events for %s; skipping',
e.toString());
results = [];
}
return results;
}
/**
* Return an RFC3339 formated date String corresponding to the given
* Date object.
* @param {Date} date a Date.
* @return {string} a formatted date string.
*/
function formatDate(date) {
return Utilities.formatDate(date, 'UTC', 'yyyy-MM-dd\'T\'HH:mm:ssZ');
}
/**
* Compares two Date objects and returns true if the difference
* between them is more than the maximum specified run time.
*
* @param {Date} start the first Date object.
* @param {Date} now the (later) Date object.
* @return {boolean} true if the time difference is greater than
* MAX_PROP_RUNTIME_MS (in milliseconds).
*/
function isTimeUp(start, now) {
return now.getTime() - start.getTime() > MAX_PRO_RUNTIME_MS;
}
Add your calendar IDs
Go to your script and paste the calendar IDs into the top part of the script and hit the save icon. SOURCE_CALENDAR_ID
is the original calendar with all of your events (including all-day events), and SCRUBBED_CALENDAR_ID
is the new calendar you created to hold the scrubbed list of events.
If your source calendar is the default calendar on your google account (named “Your Name” in the list, and the calendar id is just [email protected]), you can leave the ID in the script as ‘primary’.
Authorize the script to run
Now we need to authorize the script. Make sure the “syncCalendarAfterScrubbing” function is selected in the dropdown, and hit the “Run” button.
You’ll see an “authorization required” dialog. Hit the “Review permissions” button. You’ll see a google account chooser, choose the account where your script lives. Then you’ll see a “Google hasn’t verified this app” page. Hit the small grey “advanced” button. From there you’ll see another small grey button that says “Go to Calendar Scrubber Script (unsafe)”. Click that one. From there, you’ll see a page saying “Calendar scrubber script wants to access your Google Account”. Click on the “Allow” button.
Run the script manually
Now that you’ve authorized the script to run, you should end up back on the script page. Hit the “run” button again to actually run it. You’ll see a log pop up, with all the events that get imported. If you go back to calendar.google.com and look at your calendar, you’ll see that the new “scrubbed” calendar only has the non-all-day-events copied from the original calendar.
NOTE: If you run this script again, it will only choose events that were modified since you last ran the script. Don’t be worried if you don’t see the older events in the logs if you run it a second time.
Run the script automatically
Now let’s set up an Apps Script trigger so that this can run every day. On the left of the Apps Script page, there’s an alarm clock icon. Click on that to open up the triggers menu.
Click on the “+ Add Trigger” button in the bottom right corner. Use the following options:
Choose which function to run: syncCalendarAfterScrubbing
Choose which deployment should run: Head
Select event source: Time-driven
Select type of time based trigger: Day timer
Select time of day: Midnight to 1am
Scroll to the bottom of that dialog and hit the blue “Save” button.
Closing
That’s it! You can now go into HomeAssistant and use your new “Homeassistant Scrubbed Calendar” to get your events without any of those pesky all-day events covering them up.