Skip to content

Repository files navigation

Module: MMM-GoogleCalendar

This module is a customization from MagicMirror's default calendar module to display your Google calendars (including the Google Family calendar) without needing to make calendars public or using iCals. Inspired by the GoogleTask module.

Screenshot

screenshot

How to Setup

This guide will walk you through installing and configuring the MMM-GoogleCalendar module.

Prerequisites

  • MagicMirror² installed.
  • Node.js: This module requires Node.js version 22.14.0 or higher. Please ensure your MagicMirror installation is running on a compatible Node.js version. You can check this via the .nvmrc file in the module directory and use a Node version manager like nvm to switch if needed (e.g., nvm use).

Installation Steps

  1. Open Your Command Line Tool: Access your terminal or command prompt.

  2. Navigate to MagicMirror's Modules Folder: Change to the directory where your MagicMirror modules are stored. Typically, this is: cd ~/MagicMirror/modules

  3. Clone the Module Repository: Download the MMM-GoogleCalendar module: git clone https://github.com/randomBrainstormer/MMM-GoogleCalendar.git

  4. Install Dependencies:

    • Navigate into the newly cloned module's directory: cd MMM-GoogleCalendar
    • Install the necessary Node.js dependencies. This module uses updated libraries like googleapis for better performance and security. npm install

With the module downloaded and dependencies installed, the next step is to configure Google Cloud access.

Setting Up Google Cloud Project and Credentials

To allow this module to access your Google Calendar, you need to set up a project in Google Cloud Platform and get API credentials.

  1. Create or Select a Google Cloud Project:

  2. Enable the Google Calendar API:

    • In your Google Cloud Project, ensure the Google Calendar API is enabled.
    • Follow the instructions at Enable the Google Calendar API (you only need to do the "Enable the API" part).
  3. Configure the OAuth Consent Screen:

    • This is what lets the module ask your permission to read your calendar.
    • In the Console this now lives under Google Auth platform (in older Consoles: APIs & Services > OAuth consent screen). Google's guide: Configure the OAuth consent screen.
    • Choose External as the user type unless your account is part of a Google Workspace organization and you only need your own accounts.
    • If you are asked to select scopes, add https://www.googleapis.com/auth/calendar.readonly. That is the single permission this module requests — see What access does the module ask for? below.
    • Important: while your app is in Testing mode, add your own Google account address to the Test users list. If you skip this, authorization fails with a 403: access_denied error.
  4. Create OAuth 2.0 Client ID Credentials:

    • In the Google Cloud Console, go to Google Auth platform > Clients (in older Consoles: APIs & Services > Credentials).
    • Click + CREATE CREDENTIALS and select OAuth client ID.
    • Application type: select Desktop app. This is the only type this module supports. A "Web application" client will not work: it is issued a different kind of key file, and the module rejects it with INVALID_CREDENTIALS_TYPE.
    • Give it a name (e.g. "MMM-GoogleCalendar Client") and click CREATE.
    • A dialog shows your client ID and client secret. Click DOWNLOAD JSON; the file is named something like client_secret_[...].json.
    • Rename the downloaded file to credentials.json. A Desktop app key file has an installed section containing client_id, client_secret and redirect_uris. You do not need to edit any of it — in particular you never have to add a redirect URI by hand, because Google lets a Desktop app redirect to any local address.
  5. Place credentials.json in the Module Folder:

    • Move the renamed credentials.json file into the MMM-GoogleCalendar module directory (e.g., ~/MagicMirror/modules/MMM-GoogleCalendar/credentials.json).

Authorizing the Module

This step signs the module in to your Google account once and saves the result to token.json. It works over SSH and on a headless Raspberry Pi — the machine running the script does not need a browser or a screen.

  1. Run the Authorization Script:

    • In your terminal, make sure you are in the module's directory: cd ~/MagicMirror/modules/MMM-GoogleCalendar
    • Run: node authorize.js
    • The script prints an authorization URL and then waits.
  2. Authorize in Your Browser:

    • Open that URL in a browser on any device — the Pi itself, your laptop, or your phone.
    • Sign in with the Google account whose calendar you want to display.
    • You may see a "Google hasn't verified this app" warning. That is expected while your consent screen is in Testing mode. Click Advanced, then Go to [Your Project Name] (unsafe).
    • Grant the requested calendar permission.
  3. Hand the Authorization Code Back:

    • If you opened the URL in a browser on the same machine, you are done — the script catches the redirect itself and moves on.
    • Otherwise your browser lands on a localhost address that shows "This site can't be reached". That is expected and harmless: the address bar still holds the code.
    • Copy the entire URL out of the address bar, paste it into the waiting terminal, and press Enter. (Pasting just the code= value works too.)
  4. Token Generation:

    • The script exchanges the code for tokens and writes token.json into the module directory. That file is what lets the module read your calendar from then on.
    • token.json holds a credential for your account — treat it like a password and don't commit or share it.

To re-authorize later (for example after revoking access), delete token.json and run node authorize.js again.

You have now configured the module to access your Google Calendar. The next step is to add it to your MagicMirror config.js.

Remote and headless setups (SSH, Docker, no screen)

node authorize.js needs no browser on the device, so the steps above already work over SSH. Three ways to run it, easiest first:

1. Copy the URL back by hand (works everywhere, nothing to set up)

Exactly the flow described above: run node authorize.js, open the printed URL on your laptop or phone, then paste the resulting localhost URL back into the terminal. The page not loading is expected — you only need what's in the address bar.

2. Forward the port over SSH (no copying at all)

Pin the port with --port and tunnel it, and the script catches the redirect itself:

ssh -L 9999:localhost:9999 pi@your-mirror
cd ~/MagicMirror/modules/MMM-GoogleCalendar
node authorize.js --port 9999

Open the printed URL in a browser on your own machine. When Google redirects to http://localhost:9999/..., SSH carries it to the Pi and authorization finishes on its own. Any free port works as long as both numbers match — Google accepts any loopback port for a Desktop app client.

3. Authorize on another machine and copy the token over

token.json isn't tied to the device that created it. Run the authorization on a laptop that has both credentials.json and this repo checked out, then copy the resulting token.json to the module directory on the Pi:

scp token.json pi@your-mirror:~/MagicMirror/modules/MMM-GoogleCalendar/

Useful for Docker setups where the module directory is a mounted volume.

A note on "TV and Limited Input device" credentials: Google's device flow — the one where you type a short code at https://google.com/device — would be a natural fit here, but Google restricts it to a fixed set of scopes covering only sign-in, Drive and YouTube. Calendar scopes are not on that list, so that flow cannot be used by this module.

What access does the module ask for?

The module requests exactly one OAuth scope:

https://www.googleapis.com/auth/calendar.readonly

Google describes it as "See and download any calendar you can access using your Calendar." It is read-only: the module can list your calendars and their events, and cannot create, edit or delete anything. If a consent screen ever asks you for write access to your calendar, something is misconfigured — check that you are authorizing this module and not another one.

Your calendar data goes from Google straight to your own MagicMirror. credentials.json and token.json never leave the device, and this module sends your data to no third party.

A note for contributors: The codebase has been modernized to ES6+ standards, includes ESLint for code quality, and has initial unit tests with Jest.

Using the module

Configuration

Now it's time to bring your calendar to life on your MagicMirror! To do this, you need to add a specific configuration block to your MagicMirror's settings. This block of code tells your MagicMirror how to display your Google Calendar and which calendar to show.

Here's how you set it up:

  1. Open the config/config.js file in your MagicMirror directory. Remember, you should now be in the main directory of MagicMirror², not in the MMM-GoogleCalendar module directory. If you're not sure you're in the right place, the path should look something like this: ~/MagicMirror/config/config.js.
  2. Add Module Configuration:
    • Now, integrate the module into your MagicMirror² by adding the following configuration block to the modules array in the config/config.js file. It's crucial to replace "MyGoogleCalendarIDHere" with your actual calendar ID.

      {
          module: 'MMM-GoogleCalendar',
          header: "My Google Private Cal",
          position: "top_left",
          config: {
              calendars: [
                  {
                    symbol: "calendar-week",
                    calendarID: "MyGoogleCalendarIDHere"
                  },
                  // To add more calendars, replicate the above entry within this array with the respective ID
              ],
          }
      },

      Finding Your Google Calendar ID: If you're unsure where to find your Google Calendar ID, it's quite straightforward:

      • Head over to your Google Calendar by visiting https://calendar.google.com.
      • Look for the settings icon (resembling a gear) in the upper right corner and click on it to access Settings.
      • On the left side, navigate to Settings for my calendars, and select the calendar you wish to display on MMM-GoogleCalendar.
      • Scroll until you find the Integrate calendar section. Your calendar ID is listed there. This ID usually ends with @group.calendar.google.com.
      • There's no need to modify any settings here—just copy the Calendar ID and replace "MyGoogleCalendarIDHere" in the module configuration with your actual ID.

Configuration Options

This module is designed specifically for Google calendars, but it inherits many customizable features from the original MagicMirror² calendar module. To explore all the available options and tailor your calendar display to your liking, please refer to the MagicMirror² documentation.

The sections below cover the options that behave differently here, or that come up most often.

Showing more than one calendar

calendars is an array, so you can list as many calendars as you like. Each entry needs a calendarID; everything else is optional and overrides the module-level setting for that calendar only:

calendars: [
    {
      calendarID: "you@gmail.com",
      symbol: "user",
      color: "#82b1ff"
    },
    {
      calendarID: "family1234567890@group.calendar.google.com",
      symbol: "users",
      color: "#b9f6ca",
      maximumEntries: 5
    }
]

Per-calendar options: symbol, color, name, symbolClass, titleClass, timeClass, repeatingCountTitle, maximumEntries, maximumNumberOfDays, pastDaysCount, broadcastPastEvents and excludedEvents.

Holidays and other subscribed calendars

You are not limited to your own calendars. Any calendar the authorizing Google account can read works, including Google's regional holiday calendars, sports schedules, and calendars other people have shared with you.

Google's holiday calendars follow the pattern <language>.<region>#holiday@group.v.calendar.google.com:

calendars: [
    {
      calendarID: "you@gmail.com",
      symbol: "user"
    },
    {
      calendarID: "en.usa#holiday@group.v.calendar.google.com",
      symbol: "flag",
      color: "#ff8a80"
    },
    {
      calendarID: "en.uk#holiday@group.v.calendar.google.com",
      symbol: "flag"
    }
]

To find the exact ID for any calendar you are subscribed to, open Google Calendar > Settings (gear icon) > look under Other calendars in the left sidebar (this is where holiday, shared, and subscribed calendars live, as opposed to Settings for my calendars) > select the calendar > Integrate calendar > Calendar ID.

If a holiday calendar is not listed there yet, subscribe to it first: in Google Calendar, click the + next to Other calendars > Browse calendars of interest > pick your region under Regional holidays.

Coloring events

There are three ways to color entries. If you just want the short version, here is a complete, working config block you can copy into config.js and edit:

{
    module: 'MMM-GoogleCalendar',
    header: "My Calendar",
    position: "top_left",
    config: {
        // Turn coloring on. Without this, everything below is ignored.
        colored: true,

        // Use the colors you already picked for events in Google Calendar.
        // Set to false if you'd rather colors came only from this file.
        useGoogleEventColors: true,

        // "modern" matches Google Calendar today. Switch to "classic" if the
        // colors on your mirror look paler than the ones on your screen.
        googleEventColorPalette: "modern",

        // Your own colors. These win over anything Google sends.
        customEvents: [
            { keyword: "Birthday", symbol: "cake-candles", color: "Gold" },
            { keyword: "Dentist",  symbol: "tooth",        color: "red" }
        ],

        calendars: [
            // "color" is the fallback for events you never gave a color to.
            { calendarID: "you@gmail.com", color: "#82b1ff" }
        ]
    }
}

To turn Google's event colors off again, set useGoogleEventColors: false. Every event then falls back to its calendar's color, and your customEvents keep working exactly as before.

Your own colors always win. Anything you write in config.js — a customEvents keyword, or a googleEventColorOverrides entry — overrides the color coming from Google. Google's colors only apply where you haven't said otherwise. The full order is in Precedence below.

The rest of this section explains each piece.

1. One color per calendar. Set colored: true at the module level, then give each calendar a color. Use coloredSymbolOnly: true if you want the color applied to the icon but leave the event text in the default color.

config: {
    colored: true,
    coloredSymbolOnly: false,
    calendars: [
        { calendarID: "you@gmail.com", color: "#82b1ff" },
        { calendarID: "en.usa#holiday@group.v.calendar.google.com", color: "#ff8a80" }
    ]
}

2. Per-event colors and icons by title, using customEvents. Each entry is { keyword, symbol, color }. keyword is matched against the event title as a case-insensitive regular expression, symbol is any Font Awesome icon name (without the fa- prefix), and color is any CSS color. Symbol and color are resolved independently — for each, the first entry whose keyword matches wins — and a match overrides the symbol and color the event would otherwise inherit from its calendar. Symbol replacement requires displaySymbol (on by default).

config: {
    customEvents: [
        { keyword: "Birthday", symbol: "cake-candles", color: "Gold" },
        { keyword: "Dentist|Dental", symbol: "tooth", color: "red" },
        { keyword: "Flight|Travel", symbol: "plane-up", color: "Orange" }
    ]
}

customEvents works whether or not colored is set, and coloredSymbolOnly: true is respected for custom events too.

3. Per-event colors from Google Calendar itself. If you already color-code events inside Google Calendar, set useGoogleEventColors: true to carry those colors over instead of describing them again in your config:

config: {
    colored: true,
    useGoogleEventColors: true,
    calendars: [
        { calendarID: "you@gmail.com", color: "#82b1ff" }
    ]
}

useGoogleEventColors requires colored: true; on its own it does nothing (the module logs a warning if you set one without the other).

Which colors you get. Google ships two event palettes, selectable in its own settings. Both use the same eleven IDs and the same names; only the shades differ. Pick the one matching what you see with googleEventColorPalette, which defaults to "modern" (Google Calendar's current default):

ID Name modern classic
1 Lavender #7986cb #a4bdfc
2 Sage #33b679 #7ae7bf
3 Grape #8e24aa #dbadff
4 Flamingo #e67c73 #ff887c
5 Banana #f6bf26 #fbd75b
6 Tangerine #f4511e #ffb878
7 Peacock #039be5 #46d6db
8 Graphite #616161 #e1e1e1
9 Blueberry #3f51b5 #5484ed
10 Basil #0b8043 #51b749
11 Tomato #d50000 #dc2127

If your colors come out looking washed out, or Graphite shows up near-white instead of grey, you are on the wrong one — switch to "classic".

The palette is a Google UI preference that the API does not report, so the module cannot detect it for you. (The API's own colors.get always returns the classic values whichever set you use, which is why the module carries the table rather than fetching it.)

Adjusting individual colors. These are Google's values, picked to sit on a white background; some read poorly as text on a black mirror. googleEventColorOverrides replaces any ID you don't like and leaves the rest alone:

config: {
    colored: true,
    useGoogleEventColors: true,
    googleEventColorOverrides: {
        8: "#bdbdbd",  // Graphite, lightened for a dark background
        11: "#ff5252"  // Tomato, softened
    }
}
Precedence

Anything you set explicitly beats anything taken from Google. Highest to lowest:

# Source Beats
1 a customEvents keyword match everything below
2 googleEventColorOverrides for that colorId the built-in palette
3 the event's own color in Google Calendar the calendar color
4 the calendar's color the #fff default

The calendar's color deliberately sits below the event color: it's the fallback for events that have no color of their own, not an override. Events left on their calendar's default color carry no colorId at all, which is why it's still worth setting.

We are committed to improving and updating this module, so if you have enhancements or updates, feel free to contribute. Merge requests with the latest changes are always appreciated and welcome!

Internationalization (i18n)

We welcome translations! If you'd like to translate the module into your language:

  1. Copy the translations/en.json file.
  2. Rename it to your language code (e.g., de.json for German).
  3. Translate the string values in your new file.
  4. Add your new translation file to the getTranslations function in MMM-GoogleCalendar.js.
  5. Submit your changes as a pull request or issue on GitHub. Thank you for your contribution!

FAQ

What happened to the old types of OAuth credentials? Older versions of this module (and its documentation) referenced the Web application and TV & Limited Input devices credential types. The module now supports only the Desktop app type. That is the type Google intends for an app running on your own hardware, and it is the reason authorization works without you registering any redirect URL: Google lets a Desktop app redirect to any local address. Download credentials.json for a Desktop app client and the setup above is all you need.

If you pick the wrong type, the module reports INVALID_CREDENTIALS_TYPE — the key file will have a web section instead of the installed one the module expects.

Can I show holidays, or calendars other than my own? Yes. Any calendar the authorizing Google account can read can be added to the calendars array, including Google's regional holiday calendars (en.usa#holiday@group.v.calendar.google.com), shared calendars, and anything under Other calendars in your Google Calendar settings. See Holidays and other subscribed calendars.

Can I color-code my events? Yes, in three ways: a color per calendar (with colored: true), customEvents to color and icon events by matching their title, or useGoogleEventColors: true to reuse the colors you already assigned to events inside Google Calendar (matching either of Google's two palettes). See Coloring events.

Can this module display .ICS calendars or any other format? Unfortunately, this module is designed to work exclusively with Google Calendar. Google Calendar data is formatted differently, which is why there's no support for other calendar types within this module. If you need to display .ICS calendars or other formats, consider using the default MagicMirror² calendar module.

I'm having trouble getting this module to work. What should I do? First, don't worry! We have a troubleshooting guide that addresses common issues and their solutions. If you're still stuck after consulting the guide, please feel free to open an issue here.

Troubleshooting

Error Solution
You find yourself needing to delete the existing token and reauthorize the application every week or so. This issue typically arises when your app remains in "test" mode on Google Cloud Platform, leading to tokens expiring approximately every seven days. To resolve this, you need to publish your app: 1. Navigate to your Google Cloud Project. 2. Go to the "OAuth consent screen" tab. 3. Locate the "Publishing Status" section. 4. Click "Publish App." 5. Review and confirm the necessary permissions. By publishing your app, you extend the token's lifespan.
I ran the authentication steps but I didn't get the calendar ID printed. If your calendar ID was not printed during the node authorize.js process (which no longer prints it), you can find it on your Google Calendar settings page. Visit https://calendar.google.com, go to Settings (gear icon) > Settings for my calendars > select your calendar > Integrate calendar. Your Calendar ID is listed there.
While installing the module I get Error: Cannot find module... You're probably trying to execute a command in the wrong directory. Ensure you are in the MMM-GoogleCalendar directory (e.g., ~/MagicMirror/modules/MMM-GoogleCalendar) when running npm install or node authorize.js. Use the ls command to check your current directory's contents.
Module shows an error like "INVALID_CREDENTIALS_TYPE" or "WRONG_CREDENTIALS_FORMAT". This means the credentials.json file is not what the module expects. Ensure you have downloaded credentials for a "Desktop application" from Google Cloud Console and that the file is named credentials.json in the module's directory. "WRONG_CREDENTIALS_FORMAT" specifically might mean the client_id or client_secret is missing within the file's installed object. "INVALID_CREDENTIALS_TYPE" means the installed key itself is missing (indicating you might have used a different credential type by mistake).
Module shows "Error loading credentials.json" or "Error parsing credentials.json". "ERROR_LOADING_CREDENTIALS" means the credentials.json file could not be read. Ensure it's in the MMM-GoogleCalendar directory and has correct read permissions. "ERROR_PARSING_CREDENTIALS" means the file is present but its content is not valid JSON. Ensure you copied the content correctly or re-download it.
I restarted my MagicMirror/Raspberry Pi, and my calendars don't show anymore. This could be an expired token, especially if your app is in "test" mode (see first troubleshooting point). Try re-running node authorize.js as detailed in the setup. If that doesn't work, delete token.json from the MMM-GoogleCalendar directory and run node authorize.js again. If issues persist, consider creating new "Desktop application" credentials and repeating the authorization.
Error: invalid_grant or "Failed to exchange authorization code for token" during authorization. This usually means there's an issue with the authorization code or refresh token. Common causes: a) The system clock on your MagicMirror device is incorrect. Ensure it's synchronized. b) The authorization code was already used or expired. c) You revoked the module's access in your Google account settings. Try deleting token.json and re-running node authorize.js. If it persists, ensure the Google account used for authorization has access to the calendar and that the OAuth consent screen settings are correct. "ERROR_TOKEN_EXCHANGE" is a specific message from the module for this type of issue.
Module shows "Authentication is required..." or "Please click here to authorize..." This indicates that the token.json file is missing or invalid. You need to run (or re-run) the node authorize.js script as described in the setup steps.
Error in the MMM-GoogleCalendar module, and the logs say "Could not fetch calendar..." or "CALENDAR_ERROR". Check the specific error message in the logs (pm2 logs mm or via browser console). This could be due to various reasons: incorrect Calendar ID in config.js, network issues, or Google API errors. If the error from Google is "accessNotConfigured" or similar, ensure the Google Calendar API is enabled in your Google Cloud Project. If it's "notFound", double-check your Calendar ID.
ENOENT: no such file or directory, open '.../MMM-GoogleCalendar/token.json' The module has not been authorized yet. Run node authorize.js to generate token.json. See Authorizing the Module. (Older versions printed this as a scary stack trace from authorize.js itself even on a normal first run; that is fixed.)
I run node authorize.js and it prints a URL and waits. That is the expected behavior. Open the URL in a browser on any device, grant access, then copy the full localhost URL you land on out of the address bar and paste it back into the waiting terminal. The page failing to load is normal — the code is in the address bar.
I'm on a headless Pi, over SSH, or in Docker, and authorization used to be impossible. It works now: node authorize.js prints the URL instead of trying to launch a browser on the device, and takes the pasted redirect URL back. No VNC, desktop environment or attached screen needed.
When attempting to log in after opening the authorization URL, I encounter a 403 Error ("Access Denied", "Error 403: access_denied", or similar). This usually means the OAuth Consent Screen is not configured correctly or your account is not authorized. Ensure: 1. Your app is not set to "Internal" users unless you are part of the Google Workspace organization. 2. If your app is in "Testing" mode, your Google account email must be added to the "Test users" list under the OAuth Consent Screen settings in Google Cloud Console.
When attempting to authenticate I get an "unknown error" from Google after pasting the code. This can be caused by browser extensions interfering with Google's authentication scripts or an issue with the copied URL. Try disabling browser extensions or using an incognito/private browsing window. Ensure you copy the entire URL after granting permissions and paste it back into the terminal.
The module displays "Wrong Credentials. An error occurred while reading the credentials.json file..." This means credentials.json is either missing, malformed, or doesn't contain the expected client_id or client_secret under an installed key. Ensure you downloaded the correct "Desktop application" credentials and named the file credentials.json. This specific message often points to issues within the installed object in your credentials.json.

About

Google Calendar for MagicMirror²

Resources

Stars

102 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages