Homebridge, rewritten in Swift

Every device, in the Home app.

Homeswift speaks HomeKit for the lights, air conditioners, cameras and locks Apple never certified. One small binary runs your Homebridge config and plugins, with no Node.js and no npm, and idles in under 10 MB.

v0.5.0, early releasemacOS 14+ and LinuxMIT licence

Desk LightOn · 70 %
Brightness70 %
Living RoomHeating to 21.5°
19.5°Inside
21.5°Heat to
60%
Bedroom ShutterOpen · 60 %
Porch LightOn
Coffee MachineOff
Front DoorLocked · hold
PUT/characteristics {"aid":2,"iid":10,"value":true}

A live demo of the bridge's web UI. Tap, drag, or hold the door.

Performance

Small enough to forget it's running.

The same HomeKit controller drove two bridges with the same 30 lightbulbs: Homeswift, and HAP-NodeJS, the library inside Homebridge.

HomeswiftHAP-NodeJS 2.2.2

Apple M1, macOS 27.0.1, 21 October 2026, one run each over loopback. Lower is better on every row. The full table, with throughput and the rows where HAP-NodeJS uses less CPU per request, is in Benchmarks/README.md.

What's inside

Everything a bridge needs. Nothing it doesn't.

Add it with the code on the label

Pair setup with an 8-digit code over SRP-6a, encrypted sessions on every request, up to 16 controllers. Every accessory keeps its IDs, so rooms, scenes and automations survive restarts.

0052
1956

Your Homebridge config, as is

Reads a Homebridge-style config.json. Plugins keep their platform and option names, and child bridges work the same way.

Cameras and doorbells

Live video and snapshots over SRTP, HomeKit Secure Video recording, two-way audio and the doorbell's ring, with FFmpeg doing the encoding.

A web UI shaped like Home

Every accessory is a widget on a wall you arrange. Status, plugins, config, logs and backups sit beside it, behind your own sign-in.

An Assistant, if you want one

Ask why a light dropped off, explain a log, or get a daily digest. Optional, with a budget, and everything is redacted before a provider sees it.

An MCP server for AI clients

Scoped tokens, a confirmation for anything destructive, and an audit log. Locks, garage doors and alarms go through one guarded tool.

Runs where your home runs

A container for amd64 and arm64, a Synology package for DSM 7, or a plain binary on macOS 14 and Linux.

Plugins

Ten plugins, compiled in.

Each one is a port of the Homebridge plugin you may already use, tested against a fake of its device or cloud.

EK
Elgato Key LightsKey Light, Ring Light
UP
UniFi ProtectCameras, doorbells, sensors
UA
UniFi AccessDoor locks and readers
DC
Daikin CloudAir conditioners
DY
Dyson Pure CoolPurifiers and fans
LG
LG ThinQAppliances
PH
Philips Ambilight TVTelevision, remote
GV
GoveeLights
eW
eWeLinkSonoff switches and plugs
DL
DolphinPool cleaners
+
Port your ownFrom Node.js to Swift, step by step

Install

Running in five minutes.

# amd64 and arm64. Host networking lets HomeKit find the bridge over mDNS.
docker run -d --name homeswift --network host \
  --restart unless-stopped \
  -v ~/homeswift:/homeswift \
  ghcr.io/mp-consulting/homeswift:latest
  1. Open the web UI

    On port 8581 of the machine running the bridge.

  2. Create your account

    With the set-up code the bridge prints when it starts.

  3. Add plugins

    Pick them on the Plugins page and fill in their settings.

  4. Add the bridge in Home

    Scan the QR code on the Status page, or type the 8-digit code.

For developers

A lamp in twenty lines.

  • All 73 services and 252 characteristics HomeKit knows, with their units, ranges and valid values.
  • Swift 6 concurrency: handlers are async, and a thrown error shows as No Response in Home.
  • Set a value when the device changes, and every subscribed Home app updates.
import Homeswift

let light = Service(.lightbulb, name: "Desk Lamp",
                    optional: [.brightness], isPrimary: true)

light.characteristic(.on).onWrite(changesOnly: true) { value in
    try await lamp.setPower(value.boolValue ?? false)
}

let accessory = Accessory(
    id: "desk-lamp", name: "Desk Lamp",
    manufacturer: "Acme", model: "L1",
    category: .lightbulb, services: [light])

let server = try HAPServer(bridge: bridge, accessories: [accessory],
                           storage: FileStorage(name: "my-bridge"))
print("Setup code:", server.setupCode)  // 1234-5678
try await server.run()