DIY "On Air" light automation for Google Meet

Chrome extension and DIY light

Like any good project, this one started with a problem: how do I let my wife know I'm in a meeting so she doesn't let the dog barge in? (I work remotely and my office is the only way to get to the backyard.)

The solution: build my own "On Air" light that turns on/off automatically when I'm in a meeting! Sure, there are commercially-available products that do exactly this, but where's the fun in buying something when I have the skills and equipment to build my own? Just how hard could it be? (famous last words)

False Starts

My first idea was to detect whether the camera or microphone was in use so my solution would work with any conferencing software. It turns out this isn't easy to do with MacOS. The closest I got was using this Golang library - it gives the correct state of the hardware at first, but subsequent checks within the same process fail to detect state changes, and I don't know enough Objective C and MacOS API internals to figure out the solution.

So ultimately I gave up on that approach and settled on a more naive solution - create a Chrome extension that detects whether a meeting is active. Sounds easy enough, right? Extensions even have a nice feature allowing you to run code whenever a page matches a certain URL pattern! This works great for knowing that I'm in a meeting, but then how do I detect if I've left? window.onunload seems useful on the surface but isn't reliable enough. I also considered sending periodic still-in-a-meeting messages from the content script to the background script and waiting for a timeout to indicate that I've left, but that doesn't allow for instantaneous updates, and it also fails to reset the state if Chrome is closed before the timeout occurs.

A Solution Appears!

I ultimately settled on using just a service worker in a Chrome extension to listen to tab state changes. Any time a tab is opened, removed, or navigates to a new URL, I check whether any tab contains a URL that looks like an active Google Meet; for example:


The logic ends up being really simple:

const tabs = await chrome.tabs.query({
    url: "*-*-*",
const isInMeeting = tabs.length > 0;

I then check it against the previous state (to avoid sending redundant "not-in-a-meeting" messages when I'm opening/closing/navigating other non-Meet tabs). The last step was building out a configuration page to tell the extension how to update this state in Home Assistant:


And then created a virtual toggle switch in Home Assistant that my extension could toggle remotely:

Input boolean entity

And that's it! I now had a working Chrome extension that would detect my meeting presence and allow me to automate based on this in Home Assistant.

The Light

I briefly considered building a true "On Air" light like this one but decided against it because 3D prints take forever. I ended up flashing my previously-built (and unused) Weather Lamp with WLED so I could control it via Home Assistant.

Once I had both the extension data and WLED connected in Home Assistant, it was just a matter of creating an automation between the two!

Node-RED flow

(I used Node-RED for this, but HA's built-in automation system would work just fine.)

And here's the final result! When I'm not in a meeting, the light is green:

Green light

And it turns red when I'm in a meeting:

Red light

(I mounted this down low simply because it was closer to the outlet in the closet and would minimize the length of exposed USB cable)

And after documenting this project I later made a few tweaks:

  1. Animated the red in-meeting state to flash and spin the LEDs
  2. Improved the automation to turn the light off when I'm not in the office
  3. Rewrote the extension using Typescript, React, and async/await to practice my burgeoning frontend skills

Build Your Own!

You can build your own DIY "On Air" light using the same basic building blocks like the Chrome extension, tweaking them as needed.

For example, you can build or use any type of light you want - just integrate it with Home Assistant! Or you could fork my extension and add support for some other type of meeting software. The nice thing about DIY open-source solutions is the ability to customize it to fit your needs exactly!

Found this useful?

You can now sponsor me on GitHub to support my open-source development and contributions! All contributions will be matched by GitHub.

❤️ Become a Sponsor

About Colin O'Dell

Colin O'Dell

Colin O'Dell is a Senior Software Engineer at SeatGeek. In addition to being an active member of the PHP League and maintainer of the league/commonmark project, Colin is also a PHP docs contributor, conference speaker, and author of the PHP 7 Migration Guide.