Protocol::HAP::Controller(3p) Perl Library Manual Protocol::HAP::Controller(3p)

Protocol::HAP::Controller - a blocking HAP controller client

    use Protocol::HAP::Controller;
    my $controller = Protocol::HAP::Controller->new(
        host          => '127.0.0.1',
        port          => 51827,
        pin           => '123-45-678',
        controller_id => 'my-controller',
    );
    $controller->pair_setup  or die $controller->last_error;
    $controller->pair_verify or die $controller->last_error;
    my $response = $controller->request('GET', '/accessories');

This module is a minimal HomeKit controller. It completes pair-setup (SRP M1-M6) and pair-verify (X25519), speaks the encrypted session framing, and sends plain and encrypted requests. The conformance suite drives it; an embedder uses it to talk to any HAP accessory.

The controller is the documented exception to the sans-IO rule of Protocol::HAP: it is a blocking convenience client that owns its TCP socket. An embedder with an event loop uses the codec modules directly instead.

The constructor takes "host", "port", "pin", "controller_id", "timeout", an optional "logger" (default: the null logger), and an optional "transport" code reference that replaces the socket with an in-process byte exchange. "controller_id" is required and "new" dies without it: the id goes into the pair-setup signature, so the library must not invent one. "timeout" is the socket read timeout in seconds; the default is 5.

This complete program pairs with an accessory and reads its database. It is the shortest path for a new implementer to a working controller. Run an unpaired "openhapd" locally, then:

    use v5.36;
    use Protocol::HAP::Controller;
    my $controller = Protocol::HAP::Controller->new(
        host          => '127.0.0.1',
        port          => 51827,
        pin           => '1995-1018',
        controller_id => 'my-controller',
    );
    $controller->pair_setup
        or die 'pair-setup failed: ' . $controller->last_error;
    $controller->pair_verify
        or die 'pair-verify failed: ' . $controller->last_error;
    my $response = $controller->request('GET', '/accessories');
    die 'request failed' unless $response->{status} == 200;
    say $response->{body};

pair_setup()

Complete SRP pair-setup M1-M6. On success, store the accessory LTPK and return true. On a protocol error, return "undef" and put the TLV error code in "last_error".

pair_verify()

Complete pair-verify M1-M4 and switch the connection to the encrypted session framing. This requires a completed "pair_setup", or an "accessory_ltpk" from the caller.

Send one HTTP request over the session. After pair-verify completes, the session uses the encrypted framing. Return a hash reference with "status", "headers" and "body", or "undef" on a transport error.

The pairings management methods of HAP-Pairing.md section 7.

Wait for an EVENT/1.0 message on the socket, then decrypt and parse it. Return "undef" on timeout. This requires a socket connection, not an injected transport.

last_error()

The TLV error code or message string of the last failed exchange.

Protocol::HAP, Protocol::HAP::Server, Protocol::HAP::SRP, spec/HAP-Pairing.md, spec/HAP-Encryption.md

2026-08-18 OpenBSD