I have several home automations that run based on the day of the week. For example, there is no point opening the curtains or turning on the heating in my home office if I'm not going to be in there. As a result, most of these day-specific automations either run crudely midweek or at the weekend.

Most of the time this works; however, on days when I do work from home, I find myself having to trigger and run some of these automations manually. Wouldn't it be great if Home Assistant could look at my work calendar to determine if I'm working from home or the office that day?

Google Calendar - Working Location

At work, we use Google Workspaces, which means that I use Google Calendar to manage my work schedule.Since launching working locations, Google has added a number of improvements over time, including the ability to specify the working location for different times during a working day and making working locations available via the API! Since August 2021, Google Calendar has supported allowing you to configure and specify your work location.

Specify working hours and location in Google Calendar settings
Configuring your work location — from Share where you’re working from in Google Calendar

Since launching working locations Google has added a number of improvements over time, including the ability to specify the working location for different times during a working day, and making working locations available via the API!

Getting access to the Working Location information

Whilst I use the Google Calendar integration for Home Assistant, it doesn't currently support interacting with working location information. However, I've done my research and know this information is fundamentally available via the Google APIs.

I already use AppDaemon in my Home Assistant setup to provide additional bits of functionality. I therefore plan to put together a basic REST endpoint in AppDaemon that will query the Google Calendar API, and will allow me to define a RESTful sensor in Home Assistant.

I have used AppDaemon in the past to provide my Home Assistant setup with additional functionality. AppDaemon defines itself as:

a loosely coupled, multi-threaded, sandboxed python execution environment for writing automation apps for home automation projects, and any environment that requires a robust event driven architecture.

Building a Working Location Sensor in Home Assistant

I will assume that you already have AppDaemon setup. If not, you can either refer to the installation instructions in my first blog post on using AppDaemon or the official documentation.

Setting up API Access

First, an API key will be required to use the Google APIs — start by creating a new project in the Google Cloud Console.

New Project in Google Cloud Console

Under "Library", we will need to select and enable the Google Calendar API:

Enabling Google Calendar API in a new Google Cloud project

We can now create a set of API keys for Google Calendar. To do this, we will need to navigate to the credentials section of the Google Cloud console.

Google Cloud Credentials screen

Click on "Configure Consent Screen" – I opted for "Internal"

Google Cloud Credentials Consent configuration screen

On the next screen, you will prompted for a series of details. Only "App name", "User support email", and "Developer contact information" email are required.

Consent screen details for Google Credentails

Finally, on the next screen, you will have the option to select the scopes that are needed. Select the calendar.readonly scope:

Select the scopes that the consent screen should use

After selecting the scopes and finalising the consent screen, return to the main credentials screen and pick the option to create an "OAuth client ID" set of credentials:

Create Oauth Client ID credentials

Select "Desktop app" as the application type:

"Desktop app" application type for oauth client

Finally, on the next screen, download the attached credentials JSON file and save it as credentials.json – this will be needed later.

Download JSON credentials from OAuth Client

Building the Sensor

We first need to define and build our REST endpoint in AppDaemon, which will then be consumed by Home Assistant.

Before delving into the script that we'll use with AppDaemon to query the API, we first need to generate a token to use with the API from our credentials.json file.

On your desktop, run the following script that will launch a browser window asking you to provide Google Calendar with consent to accept API calls from this project/set of credentials. Once consent has been provided, this script will generate a google_calendar_token.json file, which will be needed later.

This script will require the following python dependencies to be installed:

pip install google-auth
pip install google-auth-oauthlib
pip install google-auth-httplib2
pip install google-api-python-client
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']

if __name__ == '__main__':
    try:
        Credentials.from_authorized_user_file('google_calendar_token.json', SCOPES)
    except FileNotFoundError:
        flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
        creds = flow.run_local_server(port=0)
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

authenticate.py

When this script is run, it will launch a browser window that will look as follows:

Google APIs Consent Screen

When you press "Allow", the script will output a google_calendar_token.json file that we will need later.

We've now completed all of the preparation work and can switch over to Home Assistant.

Within your AppDaemon installation folder, there will be a app/apps.yaml file, add an entry for working_location:

...
working_location:
  module: working_location
  class: WorkingLocation
...

Next, create the working_location.py module file with the following:

import appdaemon.plugins.hass.hassapi as hass
import datetime
import os

from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']


class WorkingLocation(hass.Hass):
    def initialize(self):
        self.register_endpoint(self.working_location, 'working_location')
        self.creds = Credentials.from_authorized_user_file(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'google_calendar_token.json'), SCOPES)

    async def working_location(self, request, kwargs):
        service = build('calendar', 'v3', credentials=self.creds)

        now = datetime.datetime.now()
        start_of_day = now.replace(hour=0, minute=0, second=0)
        end_of_day = now.replace(hour=23, minute=59, second=59)

        events_result = service.events().list(calendarId='primary',
                                              timeMin=start_of_day.isoformat() + 'Z',
                                              timeMax=end_of_day.isoformat() + 'Z',
                                              singleEvents=True,
                                              eventTypes='workingLocation',
                                              orderBy='startTime').execute()

        working_locations = []
        for event in events_result.get('items', []):
            start = datetime.datetime.fromisoformat(event['start'].get('dateTime', event['start'].get('date')).replace('Z', ''))
            end = datetime.datetime.fromisoformat(event['end'].get('dateTime', event['end'].get('date')).replace('Z', ''))
            working_locations.append({
                "start": start,
                "end": end,
                "location": event['workingLocationProperties']
            })

        current_working_locations = sorted([wl for wl in working_locations if wl['start'] <= now and wl['end'] >= now],
                                           key=lambda x: x['end'] - x['start'])

        if not current_working_locations:
            return {"type": "Home", "label": "Home"}, 200
        else:
            payload = current_working_locations[0]["location"]
            if payload['type'] == 'homeOffice':
                payload['label'] = 'Home'
            else:
                payload['label'] = payload[payload['type']]['label']
            return payload, 200

Copy your google_calendar_token.json file so that it sits in the same directory as your Python script.

This API implementation that we have written relies on a number of third-party libraries provided by Google therefore, we will need to add these to the AppDaemon configuration in Home Assistant:

Add dependencies to AppDaemon configurtion in Home Assistant

Under Developer Tools reload the configuration for Reest Entities and Notify Services so that Home Assistant picks up the new RESTful sensor.

Working location sensor in Home Assistant

Now, we have a sensor that reflects our working location that can be displayed on a dashboard or incorporated into the logic of home automation!

Working Location Card in Home Assistant