A macOS background service that detects when your microphone stops being used (end of a call), prompts you to log the activity, and auto-fills a Coda timesheet form in a browser.
Works with any app that uses your microphone — Zoom, Slack, Google Meet, FaceTime, Teams, etc.
- The service polls your Mac's default audio input device every 5 seconds using the CoreAudio framework.
- When the microphone goes from active to inactive, a macOS dialog asks if you want to log the activity.
- If you click Yes, a Chromium browser opens with your Coda form, auto-fills the fields with your configured defaults (account name, role, activity type, sentiment) and the calculated time spent, then waits for you to review, edit anything, and submit manually.
- Close the browser window when done.
On first use, you'll need to log into Coda/Google in the browser. The login session is saved so you won't need to log in again.
- macOS (Intel or Apple Silicon)
- Python 3.10+
- Access to the Coda form you want to fill
Copy the project folder to your home directory:
cp -r meeting-to-coda ~/meeting-to-coda
cd ~/meeting-to-codaCopy the example config and edit it with your values:
cp config.example.json config.jsonThen edit config.json:
form_url— the full URL of your Coda form.defaults— pre-filled values for each form field. These must match the exact dropdown option text in your Coda form. Remove any you don't need.
chmod +x install.sh
./install.shThis will:
- Check that
config.jsonexists. - Create a Python virtual environment (
.venv). - Install dependencies (
playwright,pyobjc-framework-CoreAudio). - Download Chromium for Playwright.
- Register and start a
launchdservice that runs automatically on login.
On first run, macOS may prompt you to allow:
- Accessibility access (for the native dialog prompts)
- Microphone access (for detecting audio input activity — the script does not record audio, it only checks if the device is in use)
The service runs automatically in the background after installation. No manual intervention needed.
When your microphone becomes inactive (you leave a call), a dialog appears:
Your microphone was just in use (2:00 PM - 2:30 PM, ~30 min). Log this activity to Coda?
- Yes — opens a browser, fills the form with defaults, and waits for you to review and submit.
- No — dismisses the prompt.
# Check status
launchctl list | grep meeting-to-coda
# Stop the service
launchctl stop com.user.meeting-to-coda
# Start the service
launchctl start com.user.meeting-to-coda
# Unload (disable until next login)
launchctl unload ~/Library/LaunchAgents/com.user.meeting-to-coda.plist
# Reload (after updating the script)
launchctl unload ~/Library/LaunchAgents/com.user.meeting-to-coda.plist
launchctl load ~/Library/LaunchAgents/com.user.meeting-to-coda.plist# Live log output
tail -f ~/meeting-to-coda/logs/meeting_to_coda.log
# Stdout/stderr from launchd
tail -f ~/meeting-to-coda/logs/stdout.log
tail -f ~/meeting-to-coda/logs/stderr.loglaunchctl unload ~/Library/LaunchAgents/com.user.meeting-to-coda.plist
rm ~/Library/LaunchAgents/com.user.meeting-to-coda.plist
rm -rf ~/meeting-to-codaEdit config.json and restart the service. The default values must match the exact text of the dropdown options in your Coda form.
Edit POLL_INTERVAL_SECONDS in meeting_to_coda.py (default: 5 seconds).
The script fills fields in the order defined by FORM_FIELD_ORDER in meeting_to_coda.py. If your form has different field names, update that list and the DROPDOWN_FIELDS list to match. The field-filling logic uses the form's visible label text to locate each field.
| Problem | Fix |
|---|---|
| Dialog doesn't appear | Check that the service is running: launchctl list | grep meeting-to-coda |
| Microphone not detected | Ensure pyobjc-framework-CoreAudio is installed in the venv |
| Form fields not filling | Coda may have changed its DOM structure — check logs for warnings |
| Browser opens but shows login | Log in manually; the session is saved in .browser-profile/ for next time |
| Need to re-login to Coda | Delete .browser-profile/ and the next run will prompt a fresh login |
meeting-to-coda/
├── meeting_to_coda.py # Main script
├── config.json # Your form URL and default values
├── requirements.txt # Python dependencies
├── install.sh # One-time setup script
├── com.user.meeting-to-coda.plist # launchd service template
├── .venv/ # Python virtual environment (created by install.sh)
├── .browser-profile/ # Persistent Chromium profile (created on first run)
└── logs/ # Log files (created on first run)