镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

About

macOS Google Nest camera wall with configurable per-camera snapshot widgets

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Google Home Camera Widget for macOS

Native macOS apps and companion capabilities for viewing and sharing Google Nest cameras through Google's Device Access / Smart Device Management API:

  • Nest Camera Viewer: a launchable SwiftUI viewer with a grouped camera browser and focused live feed.
  • Nest Camera Snapshot Widget: a configurable WidgetKit widget where each widget instance can show a different camera's latest snapshot.
  • Nest Broadcast Bridge: a selected-camera broadcast surface for stream/conference workflows.

The viewer app handles Google OAuth through Partner Connections Manager, stores tokens in macOS Keychain, discovers authorized SDM cameras across homes, and presents a focused single-camera live viewer. RTSP cameras use native AVKit playback. WebRTC cameras use the same full-motion go2rtc WebRTC renderer proven by the OBS output, with JPEG/MJPEG retained only as an automatic compatibility fallback.

The widget app is a configurable snapshot surface. After the viewer discovers cameras, the widget exposes those cameras in the standard Edit Widget camera picker. You can add one widget per camera. While the viewer app is running, an independent serialized scheduler keeps per-camera snapshot files fresh without interrupting the selected live preview.

The broadcast bridge exposes a stable localhost Browser Source for OBS and a direct MPEG-TS fallback. Changing the selected camera updates the existing OBS source without changing its URL. OBS Virtual Camera then presents that output as a signed macOS camera device in Microsoft Teams, Zoom, and other camera clients. The app also retains a clean 16:9 output window for ordinary window capture.

Status

  • Google Partner Connections Manager OAuth: implemented
  • SDM camera discovery: implemented
  • Single streamable-camera live viewer: implemented
  • Multi-home camera discovery and selection: implemented
  • Home grouping with room labels for camera lists: implemented
  • Focused one-camera live view: implemented
  • RTSP stream command: implemented
  • RTSP playback attempt with AVKit: implemented
  • Google Nest WebRTC playback through the local go2rtc bridge: implemented
  • Full-motion WebRTC playback in viewer and broadcast windows: implemented
  • Compact floating viewer mode: implemented
  • WidgetKit snapshot widget with per-widget camera configuration: implemented
  • Per-camera snapshot files for widget instances: implemented
  • Serialized stream snapshot scheduler with a target 60-second capture cycle: implemented
  • Stable OBS Browser Source with live camera switching: implemented
  • MPEG-TS OBS fallback and live output test: implemented
  • OBS Virtual Camera handoff for Teams/Zoom camera menus: implemented
  • First-party Core Media I/O virtual camera extension: deferred; requires Developer ID signing, Apple entitlements, and notarization
  • Packaged macOS .app: local build script support
  • Distributed signed/notarized release: not implemented

Runtime Requirements

  • macOS 26 or newer
  • Apple Silicon Mac (the current app and bundled go2rtc helper are arm64)
  • Homebrew for installer-managed media dependencies
  • FFmpeg for live preview frames and widget snapshots; the package installs the Homebrew formula automatically when Homebrew is present
  • OBS Studio 30 or newer for Broadcast Bridge output in Teams, Zoom, and other camera clients; ordinary viewer and widget use does not require OBS
  • A consumer Google Account that manages compatible Nest cameras
  • Google Device Access registration and a Device Access project
  • A Google Cloud OAuth client associated with that Device Access project
  • Smart Device Management API enabled in Google Cloud

Build Requirements

  • Xcode 26 or newer, including the macOS 26 SDK
  • Swift 5 toolchain supplied by Xcode
  • Internet access when rebuilding the patched go2rtc helper or installing FFmpeg

See THIRD_PARTY.md for required Google access, scopes, redirect URI, and software dependencies.

Google Setup

  1. Register for Google Device Access and create a Device Access project.

  2. Create or select the Google Cloud project used by that Device Access project.

  3. Enable the Smart Device Management API.

  4. Configure the OAuth consent screen and include the SDM scope:

    https://www.googleapis.com/auth/sdm.service
    
  5. Create an OAuth client and add this redirect URI:

    http://127.0.0.1:53682/oauth2callback
    
  6. Add your Google Account as a test user if the OAuth app is still in testing.

  7. In Partner Connections Manager during sign-in, grant the project access to the cameras you want to view.

Google's Device Access docs describe the required Partner Connections Manager flow and note that the SDM API uses the sdm.service scope. Google's camera live stream docs state that camera sessions are short-lived and that WebRTC streams require a valid SDP offer, a returned SDP answer, and a client that applies that answer promptly.

Local Configuration

Copy the example config and fill in your personal values:

cp Config/oauth2.example.json Config/oauth2.local.json

Config/oauth2.local.json is ignored by git. It should look like this:

{
  "clientId": "YOUR_GOOGLE_OAUTH_CLIENT_ID.apps.googleusercontent.com",
  "clientSecret": "YOUR_GOOGLE_OAUTH_CLIENT_SECRET_IF_REQUIRED",
  "deviceAccessProjectId": "YOUR_GOOGLE_DEVICE_ACCESS_PROJECT_ID",
  "usePKCE": false
}

For a Google OAuth Web application client with a client secret, leave usePKCE as false. Set it to true only if you intentionally created a public/native OAuth client that expects PKCE.

You can also use environment variables:

export GOOGLE_CLIENT_ID="..."
export GOOGLE_CLIENT_SECRET="..."
export GOOGLE_DEVICE_ACCESS_PROJECT_ID="..."
export GOOGLE_OAUTH_USE_PKCE="false"

Build and Run

From the repo root:

swift build
swift run GoogleHomeCameraWidget

Run automated smoke checks:

scripts/integration_smoke.sh

The smoke script builds the app and verifies streamable-camera selection rules, application state, and the stable localhost broadcast source using headless fixtures. It does not launch a visible test window or require Google credentials.

It also runs scripts/widget_e2e_smoke.sh against the current real camera catalog and cached Nest snapshots. That check verifies a dynamically labeled primitive camera option, persists its camera ID into the widget configuration, builds the same timeline entry used by WidgetKit, renders the actual widget view in accented mode, rejects empty or uniform image output, and writes build/widget-e2e-smoke.png for inspection. It does not install the app or modify desktop widgets.

To exercise the exact WebKit/go2rtc renderer against an already active local camera source without opening a visible app window:

CAMERA_WIDGET_SMOKE_SOURCE_ID=camera_SAFE_HASH \
  .build/release/GoogleHomeCameraWidget --go2rtc-player-smoke-test

The test requires advancing decoded video and reports the offscreen frame rate. macOS may heavily throttle a hidden WebKit view, so use the visible app and OBS for frame-rate acceptance.

After selecting a camera in Broadcast Bridge, run the credentialed broadcast check:

scripts/broadcast_smoke.sh

It validates the stable OBS page, receives a real camera JPEG, decodes 30 frames from the exact MPEG-TS fallback transport, and verifies the signed OBS camera extension. See BROADCAST_TEST_PLAN.md for the complete OBS, Teams, and Zoom acceptance test.

Run the credentialed real-camera smoke test after signing in:

build/GoogleHomeCameraWidget.app/Contents/MacOS/GoogleHomeCameraWidget --nest-camera-smoke-test

This test uses the configured Google Device Access project, discovers your actual SDM devices, chooses a streamable real Nest camera, calls Google's live-stream executeCommand, and then waits for real media from that camera. RTSP cameras must decode an AVPlayer frame. WebRTC cameras must receive remote video media in WKWebView after Google returns an SDP answer. If the command cannot read the app's Keychain token, run it with a short-lived OAuth access token in GOOGLE_ACCESS_TOKEN.

To build a local .app bundle:

./build.sh
open build/GoogleHomeCameraWidget.app

To build a macOS installer package that installs the app into /Applications and registers the embedded WidgetKit extension with LaunchServices/PlugInKit:

./build.sh --pkg
sudo installer -pkg build/GoogleHomeCameraWidget-0.2.1.pkg -target /
open /Applications/GoogleHomeCameraWidget.app

The installer checks the macOS version, CPU architecture, embedded go2rtc helper, and FFmpeg. If FFmpeg is missing and Homebrew is available, it runs brew install ffmpeg as the signed-in user. If Homebrew itself is absent, the installer stops with a clear message instead of downloading and executing a package manager without separate user consent.

The installer package does not include Config/oauth2.local.json or OAuth tokens. Keep local credentials in ~/Library/Application Support/GoogleHomeCameraWidget/oauth2.json with permissions limited to your user account.

See RELEASE.md for package verification and GitHub release publishing.

First Run

  1. Confirm Config/oauth2.local.json exists and has your client ID and Device Access project ID.
  2. Install build/GoogleHomeCameraWidget-0.2.1.pkg for the system /Applications install that registers the desktop widget.
  3. Open /Applications/GoogleHomeCameraWidget.app.
  4. Click Sign In with Google.
  5. Complete Google's Partner Connections Manager flow and grant camera access.
  6. Verify the app lists real cameras.
  7. Select one streamable camera from the grouped camera list.
  8. Confirm the selected camera starts streaming in the preview panel.
  9. Toggle Widget Mode to keep the viewer as a compact floating camera window on the desktop.
  10. Switch to Broadcast Bridge, select a camera, and wait for the live output test to report ready.
  11. Copy the stable OBS Browser Source URL or open the clean broadcast feed window.
  12. Add the Nest Camera Snapshot widget from macOS widget editing.
  13. Edit the widget and choose the camera for that widget instance. You can add one widget per camera.

Apps

Nest Camera Viewer

The viewer is the live monitoring app. It is intentionally independent from the widget snapshot surface.

  • Shows one selected streamable camera at a time.
  • Filters the picker to cameras that report RTSP or WebRTC support.
  • Starts streaming immediately after selecting a camera.
  • Keeps the video preview non-interactive while debugging click-related WebKit/AVKit crashes.

Nest Camera Snapshot Widget

The widget is the configurable per-camera snapshot surface.

  • Each widget instance chooses one camera.
  • Widget camera choices come from the camera catalog written by the viewer app.
  • Each widget displays the latest saved snapshot and timestamp for its selected camera.
  • The viewer app runs one serialized scheduler across discovered stream-capable cameras.
  • The local bridge keeps stable per-camera sources, while the scheduler captures and writes one camera snapshot at a time. It targets a new capture cycle every 60 seconds while the viewer app is running; a large camera list or a slow Google stream startup can make a full cycle take longer.
  • The visible all-feed viewer is independent from the widget snapshot scheduler; changing zoom state in the viewer does not choose or limit which widget camera snapshots update.

Apple's WidgetKit renders widgets from timelines in a separate process, and widgets are not continually active while onscreen. After every successful capture, the companion app asks WidgetKit to reload the widget timeline; the widget also requests another timeline after 60 seconds as a fallback. Neither is a guaranteed display deadline. Apple recommends timeline entries at least about five minutes apart and says a frequently viewed widget's normal budget commonly works out to roughly one reload every 15–60 minutes. macOS may refresh sooner when the containing app is foreground-active or after a configuration change. Keep the companion app running so it can obtain credentialed Google streams and refresh the snapshot files WidgetKit reads.

Nest Broadcast Bridge

The broadcast bridge is the selected-camera sharing surface for OBS and conferencing apps.

  • Selects any camera discovered by the authenticated viewer session.
  • Hosts a stable Browser Source at http://127.0.0.1:11985/; changing cameras updates the source automatically.
  • Provides a camera-specific MPEG-TS fallback URL for OBS Media Source.
  • Tests the selected source by retrieving a real frame before reporting it ready.
  • Opens OBS directly and copies either source URL without exposing Google credentials.
  • Opens a dedicated 16:9 output window titled for the selected camera.
  • Uses the same live-feed negotiation as the viewer while staying independent from the viewer grid and widget snapshot workers.
  • Uses OBS Virtual Camera to provide the signed camera device selected in Teams and Zoom.
  • Does not carry audio through the virtual camera; select a microphone separately in OBS or the conferencing app.

Security

  • Credentials are not committed.
  • Config/oauth2.local.json is for local use only and is ignored by git. Rotate its Google OAuth values if the file is ever copied into logs, screenshots, shared archives, or synced storage you do not fully control.
  • Keep Config/oauth2.example.json as placeholders only.
  • OAuth tokens are stored in macOS Keychain.
  • Sign out deletes the stored Keychain token.
  • Local OAuth callback listens on 127.0.0.1:53682 only during sign-in.

Project Structure

.
├── Config/
│   ├── App-Info.plist
│   ├── Widget-Info.plist
│   ├── Widget.entitlements
│   └── oauth2.example.json
├── Sources/
│   ├── CameraWidgetApp.swift
│   └── GoogleHomeCameraWidgetExtension/
│       └── WidgetExtension.swift
├── CameraWidget.xcodeproj/
├── LICENSE
├── Package.swift
├── README.md
├── THIRD_PARTY.md
└── build.sh

License

MIT. See LICENSE.

About

macOS Google Nest camera wall with configurable per-camera snapshot widgets

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages