How HomeKit Pairing and QR Code Registration Work in atomcam_tools

HomeKit pairing in atomcam_tools works by generating a QR code from a Setup URI produced by the go2rtc daemon, which bridges the ATOM camera to Apple HomeKit via a locally-generated YAML configuration file and a REST API that reports real-time pairing status.

The mnakada/atomcam_tools repository transforms ATOM, ATOMCam, and AtomSwing cameras into native Apple HomeKit accessories without requiring cloud services or external bridges. This local-only integration relies on three coordinated components: a Vue.js web interface for user interaction, a patched go2rtc streaming daemon that exposes HomeKit pairing APIs, and an initialization script that generates the required configuration files.

Architecture Overview

The HomeKit implementation spans three distinct layers that communicate through local HTTP APIs and filesystem state:

  • Web Interface (web/source/vue/Setting.vue): Renders the HomeKit toggle switch, polls for pairing status, displays the QR code using qrcode.vue, and handles un-pair requests via a DELETE API call.

  • go2rtc Daemon (custompackages/package/go2rtc/): A modified streaming server that exposes GET /api/homekit/pairing to retrieve the current SetupURI, Pin, and pairing status, and DELETE /api/homekit/pairing?stream=video0 to force un-pairing. The patches 0001-go2rtc-homekit-pairing-api.patch and 0003-go2rtc-homekit-qrcode.patch add these handlers.

  • Startup Script (overlay_rootfs/scripts/rtspserver.sh): Reads HOMEKIT_* variables from hack.ini and writes /media/mmc/homekit.yaml containing the device_id, setup_id, pin, and name before launching go2rtc with the -config flag.

End-to-End Pairing Flow

When a user enables HomeKit through the web interface, the following sequence executes locally on the camera:

1. Enable HomeKit in the Web UI

The Vue component in Setting.vue (lines 245-259) displays a toggle switch that is only enabled when the main RTSP stream (RTSP_VIDEO0) is active. If the user turns on HOMEKIT_ENABLE while the required fields are empty, the mounted() hook (lines 221-233) automatically generates a random 4-letter setup ID and random 8-digit PIN.

<SettingSwitch i18n="HomeKit"
               :value="(config.RTSP_VIDEO0 == 'on') ? config.HOMEKIT_ENABLE : 'off'"
               @input="config.HOMEKIT_ENABLE=$event"
               :disabled="config.RTSP_VIDEO0 !== 'on'" />

2. Configuration File Generation

On the next boot or service reload, rtspserver.sh (lines 159-170) checks if HOMEKIT_ENABLE equals on. If enabled, it creates /media/mmc/homekit.yaml with the accessory parameters:

device_id: <MAC-ADDRESS>
setup_id: ABXZ
name: atomcam
pin: 12345678

3. Daemon Initialization

The startup script appends -config /media/mmc/homekit.yaml to the go2rtc command line (line 175). The daemon reads this file and begins advertising the camera as a HomeKit accessory on the local network.

4. Real-Time Status Polling

The web interface runs CheckHomeKit() (lines 1039-1065) every 1-5 seconds to query the pairing state:

async function CheckHomeKit () {
  const localhost = window.location.protocol + '//' + window.location.host;
  const pairingInfo = await axios.get(`${localhost}:1984/api/homekit/pairing`)
    .catch(() => ({}));
  
  this.homeKitPairing = pairingInfo?.video0?.Status ?? '';
  this.homeKitSetupURI = pairingInfo?.video0?.SetupURI ?? '';
  this.homeKitSetupCode = pairingInfo?.video0?.Pin ?? '';
}

The API returns JSON containing the SetupURI (formatted as X-HM://<setup-id>#...), the Pin (formatted as 123-45-678), and the Status ("unpaired" or "paired").

5. QR Code Rendering

The Vue template renders the scannable QR code using the SetupURI value:

<div class="homekit">
  <QrcodeVue class="homekit-qrcode"
             :value="homeKitSetupURI"
             size="150" />
  <div class="homekit-setupcode">{{ homeKitSetupCode }}</div>
</div>

Scanning this QR code with the iOS Home app initiates the pairing handshake directly with the camera.

6. Un-Pairing via API

To remove the accessory from HomeKit, the user clicks the Unpair button, which sends a DELETE request to the go2rtc API:

curl -X DELETE "http://<camera-ip>:1984/api/homekit/pairing?stream=video0"

The apiPairingHandler in the patched go2rtc source deletes the stored pairing data, resets the status to "unpaired", and regenerates the SetupURI and PIN for future pairing attempts.

Key Source Files and Implementation Details

Web Interface Controller (web/source/vue/Setting.vue)

This file manages the entire user-facing state. It binds the HomeKit toggle to the HOMEKIT_ENABLE configuration key, validates that RTSP is enabled first, and coordinates the polling loop that keeps the QR code display synchronized with the daemon's internal state.

Startup Configuration Script (overlay_rootfs/scripts/rtspserver.sh)

Lines 93-165 handle the translation from hack.ini environment variables to the YAML format that go2rtc expects. The script specifically checks for HOMEKIT_ENABLE, HOMEKIT_SETUP_ID, HOMEKIT_DEVICE_ID, HOMEKIT_PIN, and HOMEKIT_SOURCE to construct a valid accessory definition.

go2rtc HomeKit Patches

  • Patch 0001: Implements the apiPairingHandler function that responds to GET and DELETE requests at /api/homekit/pairing, maintaining the pairing state in memory and exposing it via HTTP.
  • Patch 0003: Generates the X-HM:// URL format required by Apple HomeKit for QR code encoding, ensuring compatibility with iOS and macOS Home applications.

Summary

  • atomcam_tools implements HomeKit pairing entirely on the camera hardware without external cloud dependencies.
  • The go2rtc daemon exposes REST endpoints at port 1984 for retrieving SetupURI/PIN and forcing un-pair operations.
  • Configuration persists in /media/mmc/homekit.yaml, generated by rtspserver.sh from hack.ini variables.
  • The web interface polls the daemon every 1-5 seconds to render an accurate QR code and pairing status.
  • Un-pairing is performed via a DELETE request that resets the accessory to an unpaired state with fresh credentials.

Frequently Asked Questions

How do I force un-pair a camera from HomeKit without using the web interface?

Send a DELETE request directly to the go2rtc API endpoint using curl: curl -X DELETE http://<camera-ip>:1984/api/homekit/pairing?stream=video0. This immediately clears the pairing data stored by the daemon and returns the accessory to an unpaired state, allowing you to pair it with a different Apple ID or HomeKit home.

Where does the camera store the HomeKit pairing credentials?

The credentials are stored in two locations: the persistent configuration file /media/mmc/homekit.yaml (generated by rtspserver.sh) contains the setup_id, device_id, and pin, while the active pairing state (including controller public keys) is maintained in memory by the go2rtc daemon and lost when the service restarts unless paired again.

What format does the HomeKit QR code use, and can I enter the code manually?

The QR code encodes a SetupURI in the format X-HM://<setup-id>#<device-id>, which is the standard HomeKit Accessory Protocol format. If scanning fails, you can manually enter the 8-digit Setup Code displayed beneath the QR code (formatted as XXX-XX-XXX) into the iOS Home app during manual accessory setup.

Does enabling HomeKit disable the RTSP stream?

No, HomeKit requires the RTSP stream to be active. The web interface enforces this dependency by disabling the HomeKit toggle unless RTSP_VIDEO0 is set to on. The go2rtc daemon serves both the HomeKit accessory protocol and the RTSP feed simultaneously from the same video source.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →