hap #
HomeKit Accessory Protocol (HAP) controller for OCaml.
Smart plugs, lights and sensors sold for Apple Home speak HAP on the local
network, so a program that wants to switch them without going through an
iPhone has to implement the controller side itself. hap finds accessories
by mDNS, pairs with one once using its setup code (SRP-6a pair setup), and on
every later connection opens an encrypted session (Curve25519 pair verify,
then ChaCha20-Poly1305) to read and write its characteristics. SRP-6a
(RFC 5054) is a password-authenticated key exchange: the controller proves it
knows the accessory's 8-digit setup code without sending it, both ends derive
a shared key, and under that key they exchange long-term Ed25519 public keys.
Pair verify then runs an ephemeral Curve25519 Diffie-Hellman exchange signed
with those long-term keys and derives the ChaCha20-Poly1305 session keys from
it. A characteristic is one readable or writable value of an accessory, such
as an outlet's On state. Pairings are
stored under ~/.hap/pairings/. The library runs on Eio. Outlets have
shortcut functions, and other accessory types go through the generic
characteristic calls.
Installation #
Install with opam:
$ opam install hap
If opam cannot find the package, it may not yet be released in the public
opam-repository. Add the overlay repository, then install it:
$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install hap
Usage #
let run () =
Eio_main.run @@ fun env ->
Eio.Switch.run @@ fun sw ->
let net = Eio.Stdenv.net env in
let clock = Eio.Stdenv.clock env in
let fs = Eio.Stdenv.fs env in
(* Discover HomeKit accessories on the network. *)
match Hap.discover ~sw ~net ~clock ~timeout:5.0 () with
| [] -> ()
| (a : Hap.accessory_info) :: _ ->
(* Pair with an accessory (one-time setup). *)
(match
Hap.pair_setup ~net ~sw ~clock ~ip:a.ip ~port:a.port ~pin:"031-45-154"
with
| Ok _pairing -> ()
| Error (`Msg m) -> failwith m);
(* Control an outlet (uses the saved pairing). *)
match Hap.turn_on_outlet ~net ~sw ~clock ~fs a.ip with
| Ok () -> ()
| Error (`Msg m) -> failwith m
API #
- Discovery:
Hap.discoverfinds accessories by mDNS,Hap.accessory_inforeads one accessory's advertisement, andHap.category_namenames its category. - Pairing:
Hap.pair_setuppairs once with the setup code, andHap.pair_verifyopens an encrypted session from a stored pairing. - Pairing storage:
Hap.save_pairing_by_idwrites a pairing,Hap.pairing_by_idreads one back by accessory id, andHap.pairing_for_ipfinds the one for an address. - Control:
Hap.accessorieslists services and characteristics,Hap.characteristicsreads values andHap.put_characteristicwrites one;Hap.turn_on_outlet,Hap.turn_off_outletandHap.toggle_outletare shortcuts for outlets. - Encoding:
Hap.Tlvencodes and decodes HAP's TLV8 format, with its type constants inHap.Tlv_typeand error codes inHap.Hap_error.
License #
ISC. See LICENSE.md.