Unified ASCOM Alpaca Switch driver that exposes multiple hardware backends as a single Switch device to astronomy software such as N.I.N.A.
Currently supports two backends, all visible as numbered switches inside one ASCOM device. Mi switches to turn devices on and off and Hikvision cameras to turn off IR lights to prevent stray lights for astrophotography.
| Backend | Hardware | Protocol |
|---|---|---|
Xiaomi Mi ![]() |
Mi Smart Plug (Wi-Fi power switches) | Xiaomi UDP protocol, AES-CBC encryption |
Hikvision ![]() |
IP camera IR illuminators | Hikvision ISAPI over HTTP, Digest auth |
Switch IDs are assigned in the order backends are listed: Mi plugs first (IDs 0–N), then Hikvision cameras (IDs N+1–M).
- Go 1.22 or later
- Xiaomi Mi plugs: device IP and 32-character hex token (obtainable with miio)
- Hikvision cameras: camera IP, admin credentials, and Configuration → System → Maintenance → System Service → Hardware enabled in the camera web UI
- Tested on: DS-2CD2343G0-I, DS-2CD2335-I
- Supports optional port in host field, e.g.
"192.168.1.3:65005"
Copy config/settings.json.example to config/settings.json and fill in your device details:
{
"alpaca_port": 11111,
"mi_devices": [
{
"ip": "192.168.1.100",
"token": "your32hexcharactertoken00000000",
"name": "12V Power",
"description": "12V dew heater power",
"min": 0,
"max": 1,
"step": 1,
"canwrite": true,
"value": 0
}
],
"hikvision_cameras": [
{
"host": "192.168.1.4",
"username": "admin",
"password": "yourpassword",
"name": "Backyard IR",
"description": "Control IR light on backyard security camera",
"uniqueid": "00000000-0000-0000-0000-000000000001",
"value": 0
}
]
}Add as many entries to each array as needed.
go build -o alpaca-switch.exe ../alpaca-switch.exeThe driver listens on port 11111 (standard ASCOM Alpaca port) and responds to ASCOM discovery broadcasts on UDP port 32227.
- Open N.I.N.A. → Equipment → Switch
- Click the Scan for devices
- In the dropdown list under the sub heading ASCOM Alpaca select the Alpaca Switch Controller item
- Click Connect — all switches appear by name
| Field | Description |
|---|---|
alpaca_port |
HTTP API port (default: 11111) |
mi_devices |
Array of Xiaomi Mi smart plug configs |
hikvision_cameras |
Array of Hikvision camera configs |
| Field | Description |
|---|---|
ip |
Device IP address |
token |
32-character hex authentication token |
name |
Title shown in NINA |
description |
Subtitle shown in NINA (optional; falls back to name) |
min / max / step |
Value range (0/1/1 for on/off switches) |
canwrite |
false to make the switch read-only in NINA |
value |
Cached last-known state (0=off, 1=on) |
| Field | Description |
|---|---|
host |
Camera IP address, optionally with port: "192.168.1.4" or "192.168.1.3:65005" |
username |
Camera admin username (usually admin) |
password |
Camera password |
name |
Title shown in NINA |
description |
Subtitle shown in NINA (optional; falls back to "<name> IR illuminator") |
uniqueid |
Stable UUID for the ASCOM device (any unique value, e.g. "00000000-0000-0000-0000-000000000001") |
value |
Cached last-known IR state (0=off, 1=on) |
alpaca-switch/
├── main.go # Entry point: loads config, wires backends, starts server
├── backend/
│ ├── backend.go # SwitchBackend interface + Router (ID mapping)
│ ├── mi/
│ │ ├── mi.go # Xiaomi Mi plug state management
│ │ └── xiaomi.go # Xiaomi UDP protocol (AES-CBC encrypted)
│ └── hikvision/
│ └── hikvision.go # Hikvision ISAPI IR control (HTTP Digest auth)
├── server/
│ ├── api.go # HTTP server, request helpers, response builder
│ ├── discovery.go # ASCOM Alpaca UDP discovery (port 32227)
│ ├── management.go # /management/* endpoints
│ ├── common.go # /api/v1/switch/0/connected, name, description…
│ ├── switch.go # /api/v1/switch/0/getswitch, setswitch…
│ └── types.go # ASCOM Alpaca response structs
└── config/
├── settings.json # Your local config (excluded from git — contains credentials)
└── settings.json.example # Safe template to commit
- Create
backend/<name>/<name>.goimplementing thebackend.SwitchBackendinterface - Add a config struct and load it in
main.go - Pass the new backend to
backend.NewRouter()
config/settings.jsonis excluded from git because it contains device tokens and camera passwords. Commitsettings.json.exampleinstead.- Hikvision IR state is read live from the camera each time NINA polls
GetSwitch. - Xiaomi plug state is refreshed on
Connectand cached; updates are sent on eachSetSwitch. - Discovery binds to the primary outbound network interface to avoid NINA discovering the driver multiple times on multi-adapter machines.

