# How HomeKit Pairing and QR Code Registration Work in atomcam_tools

> Learn how HomeKit pairing and QR code registration work seamlessly with atomcam_tools. This guide explains the QR code generation process via go2rtc for smooth Apple HomeKit integration with your ATOM camera.

- Repository: [Mitsuru Nakada/atomcam_tools](https://github.com/mnakada/atomcam_tools)
- Tags: deep-dive
- Published: 2026-03-07

---

**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`](https://github.com/mnakada/atomcam_tools/blob/main/web/source/vue/Setting.vue))**: Renders the HomeKit toggle switch, polls for pairing status, displays the QR code using [`qrcode.vue`](https://github.com/mnakada/atomcam_tools/blob/main/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`](https://github.com/mnakada/atomcam_tools/blob/main/overlay_rootfs/scripts/rtspserver.sh))**: Reads `HOMEKIT_*` variables from [`hack.ini`](https://github.com/mnakada/atomcam_tools/blob/main/hack.ini) and writes [`/media/mmc/homekit.yaml`](https://github.com/mnakada/atomcam_tools/blob/main//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`](https://github.com/mnakada/atomcam_tools/blob/main/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**.

```html
<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`](https://github.com/mnakada/atomcam_tools/blob/main/rtspserver.sh) (lines 159-170) checks if `HOMEKIT_ENABLE` equals `on`. If enabled, it creates [`/media/mmc/homekit.yaml`](https://github.com/mnakada/atomcam_tools/blob/main//media/mmc/homekit.yaml) with the accessory parameters:

```yaml
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:

```javascript
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:

```html
<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:

```bash
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`](https://github.com/mnakada/atomcam_tools/blob/main/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`](https://github.com/mnakada/atomcam_tools/blob/main/overlay_rootfs/scripts/rtspserver.sh))

Lines 93-165 handle the translation from [`hack.ini`](https://github.com/mnakada/atomcam_tools/blob/main/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`](https://github.com/mnakada/atomcam_tools/blob/main//media/mmc/homekit.yaml), generated by [`rtspserver.sh`](https://github.com/mnakada/atomcam_tools/blob/main/rtspserver.sh) from [`hack.ini`](https://github.com/mnakada/atomcam_tools/blob/main/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`](https://github.com/mnakada/atomcam_tools/blob/main//media/mmc/homekit.yaml) (generated by [`rtspserver.sh`](https://github.com/mnakada/atomcam_tools/blob/main/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.