hap

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 21, 2022 License: Apache-2.0 Imports: 39 Imported by: 69

README

hap

GoDoc Widget Travis Widget

hap (previously hc) is a lightweight library to develop HomeKit accessories in Go. It abstracts the HomeKit Accessory Protocol (HAP) and makes it easy to work with services and characteristics.

hap handles the underlying communication between HomeKit accessories and clients. You can focus on implementing the business logic for your accessory, without having to worry about the protocol.

Here are some projects which use hap.

What is HomeKit?

HomeKit is a set of protocols and libraries from Apple. It is used by Apple's platforms to communicate with smart home appliances. A non-commercial version of the documentation is now available on the HomeKit developer website.

HomeKit is fully integrated into iOS since iOS 8. Developers can use HomeKit.framework to communicate with accessories using high-level APIs.

I've developed the Home+ app to control HomeKit accessories from iPhone, iPad, and Apple Watch. If you want to support hap, please purchase Home from the App Store. That would be awesome. ❤️

Migrate from hc

This library is a rewrite of hc. If you want to migrate from hc, consider the following changes.

  • Instead of hc.NewIPTransport(...) you now call hap.NewServer(...) to create a server.
  • With the server you can define your own http handlers. hc#212
server.ServeMux().HandleFunc("/ping", func(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("pong"))
})
  • You can define your own public and private key (just in case) by setting the Key field of the server. Otherwise those keys are generate and stored on disk for you.
server.Key = hap.KeyPair{
	Public:  []byte{...},
	Private: []byte{...},
}

Features

Usage

In a following example a simple on/off switch is created. It can be paired with HomeKit using the Apple Home app – use the pin code 00102003.

package main

import (
	"github.com/brutella/hap"
	"github.com/brutella/hap/accessory"

	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	// Create the switch accessory.
	a := accessory.NewSwitch(accessory.Info{
		Name: "Lamp",
	})

	// Store the data in the "./db" directory.
	fs := hap.NewFsStore("./db")

	// Create the hap server.
	server, err := hap.NewServer(fs, a.A)
	if err != nil {
		// stop if an error happens
		log.Panic(err)
	}

	// Setup a listener for interrupts and SIGTERM signals
	// to stop the server.
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)

	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		<-c
		// Stop delivering signals.
		signal.Stop(c)
		// Cancel the context to stop the server.
		cancel() 
	}()

	// Run the server.
	server.ListenAndServe(ctx)
}
Events

The library provides callback functions, which let you know when a client updates a characteristic value. The following example shows how to get notified when the On characteristic value changes.

a.Switch.On.OnValueRemoteUpdate(func(on bool) {
    if on == true {
        log.Println("Switch is on")
    } else {
        log.Println("Switch is off")
    }
})

If you want to change the state of a switch programmatically, you call SetValue(...).

a.Switch.On.SetValue(true)

The library takes care of the rest and notifies all connected clients that the state has changed.

Multiple Accessories

When you create a server you can specify multiple accessories like this.

var a1, a2, a3 *accessory.A
s, err := hap.NewServer(fs, a1, a2, a3)

By doing so, the first accessory a1 appears as a bridge in HomeKit. When adding the accessories to HomeKit, iOS only shows the bridge accessory. Once the bridge was added, the other accessories appear automatically.

HomeKit requires that every accessory has a unique id, which must not change between system restarts. hap automatically assigns the ids for you based on the order in which the accessories are added to the server.

The best would be to specify the unique id for every accessory yourself, like this

a1.Id = 1
a2.Id = 2

Accessory Architecture

HomeKit uses a hierarchical architecture to define accessories, services and characeristics. At the root level there is an accessory. Every accessory contains services. And every service contains characteristics.

For example a lightbulb accessory contains a lightbulb service. This service contains the on characteristic.

There are predefined accessories, services and characteristics available in HomeKit. Those types are defined in the packages accessory, service, characteristic.

Contact

Matthias Hochgatterer

Website: https://hochgatterer.me

Github: https://github.com/brutella

Twitter: https://twitter.com/brutella

License

hap is available under the Apache License 2.0 license. See the LICENSE file for more info.

Documentation

Index

Constants

View Source
const (
	JsonStatusSuccess                     = 0
	JsonStatusInsufficientPrivileges      = -70401
	JsonStatusServiceCommunicationFailure = -70402
	JsonStatusResourceBusy                = -70403
	JsonStatusReadOnlyCharacteristic      = -70404
	JsonStatusWriteOnlyCharacteristic     = -70405
	JsonStatusNotificationNotSupported    = -70406
	JsonStatusOutOfResource               = -70407
	JsonStatusOperationTimedOut           = -70408
	JsonStatusResourceDoesNotExist        = -70409
	JsonStatusInvalidValueInRequest       = -70410
)
View Source
const (
	TlvErrorUnknown        = 0x1
	TlvErrorInvalidRequest = 0x2
	TlvErrorAuthentication = 0x2
	TlvErrorBackoff        = 0x3
	TlvErrorMaxPeers       = 0x4
	TlvErrorUnknownPeer    = 0x4
	TlvErrorMaxTries       = 0x5
	TlvErrorUnavailable    = 0x6
	TlvErrorBusy           = 0x7
)
View Source
const (
	// HTTPContentTypePairingTLV8 is the HTTP content type for pairing
	HTTPContentTypePairingTLV8 = "application/pairing+tlv8"

	// HTTPContentTypeHAPJson is the HTTP content type for json data
	HTTPContentTypeHAPJson = "application/hap+json"
)
View Source
const (
	Step1 byte = 0x1
	Step2 byte = 0x2
	Step3 byte = 0x3
	Step4 byte = 0x4
	Step5 byte = 0x5
	Step6 byte = 0x6
)
View Source
const (
	MethodPair          byte = 0x0 // pair
	MethodPairMFi       byte = 0x1 // MFi compliant accessory
	MethodVerifyPair    byte = 0x2 // verify a pairing
	MethodAddPairing    byte = 0x3 // add client through secure connection
	MethodDeletePairing byte = 0x4 // delete pairing through secure connection
	MethodListPairings  byte = 0x5
)
View Source
const (
	PermissionUser  byte = 0x0
	PermissionAdmin byte = 0x1
)
View Source
const (
	// PacketLengthMax is the max length of encrypted packets
	PacketLengthMax = 0x400
)
View Source
const (
	SRPGroup = "rfc5054.3072" // N (modulo) => 384 byte
)

Main SRP algorithm is described in http://srp.stanford.edu/design.html The HAP uses the SRP-6a Stanford implementation with the following characteristics

x = H(s | H(I | ":" | P)) -> called the key derivative function
M1 = H(H(N) xor H(g), H(I), s, A, B, K)

Variables

View Source
var InvalidPins = map[string]bool{
	"00000000": true,
	"11111111": true,
	"22222222": true,
	"33333333": true,
	"44444444": true,
	"55555555": true,
	"66666666": true,
	"77777777": true,
	"88888888": true,
	"99999999": true,
	"12345678": true,
	"87654321": true,
}

Functions

func Conns

func Conns() map[string]*Conn

func NewChunkedWriter

func NewChunkedWriter(wr io.Writer, chunk int) io.Writer

NewChunkedWriter returns a writer which writes bytes in chunkes of specified size.

func NewFsStore

func NewFsStore(dir string) *fsStore

func NewMemStore

func NewMemStore() memStore

func SetConn

func SetConn(addr string, conn *Conn)

Types

type AccessoriesPayload

type AccessoriesPayload struct {
	Accessories []*accessory.A `json:"accessories"`
}

type Conn

type Conn struct {
	net.Conn
	// contains filtered or unexported fields
}

func GetConn

func GetConn(req *http.Request) *Conn

func (*Conn) Read

func (conn *Conn) Read(b []byte) (int, error)

Read reads bytes from the connection. The read bytes are decrypted when possible.

func (*Conn) Upgrade

func (conn *Conn) Upgrade(s *Session)

func (*Conn) Write

func (conn *Conn) Write(b []byte) (int, error)

Write writes bytes to the connection. The written bytes are encrypted when possible.

type KeyPair

type KeyPair struct {
	Public  []byte
	Private []byte
}

type Listener

type Listener struct {
	*net.TCPListener
}

func (*Listener) Accept

func (ln *Listener) Accept() (con net.Conn, err error)

func (*Listener) Addr

func (ln *Listener) Addr() net.Addr

func (*Listener) Close

func (ln *Listener) Close() error

type PairSetupSession

type PairSetupSession struct {
	Identifier    []byte
	Salt          []byte   // s
	PublicKey     []byte   // A
	PrivateKey    []byte   // S
	EncryptionKey [32]byte // K
	// contains filtered or unexported fields
}

func NewPairSetupSession

func NewPairSetupSession(id, pin string) (*PairSetupSession, error)

NewPairSetupSession return a new setup server session.

func (*PairSetupSession) ProofFromClientProof

func (p *PairSetupSession) ProofFromClientProof(clientProof []byte) ([]byte, error)

ProofFromClientProof validates client proof (`M1`) and returns authenticator or error if proof is not valid.

func (*PairSetupSession) SetupEncryptionKey

func (p *PairSetupSession) SetupEncryptionKey(salt []byte, info []byte) error

SetupEncryptionKey calculates and internally sets encryption key `K` based on salt and info

Only 32 bytes are used from HKDF-SHA512

func (*PairSetupSession) SetupPrivateKeyFromClientPublicKey

func (p *PairSetupSession) SetupPrivateKeyFromClientPublicKey(key []byte) error

SetupPrivateKeyFromClientPublicKey calculates and internally sets secret key `S` based on client public key `A`

type Pairing

type Pairing struct {
	Name       string
	PublicKey  []byte
	Permission byte
}

type ServeMux

type ServeMux interface {
	// Handle registers the handler for the given pattern.
	Handle(pattern string, handler http.Handler)
	// HandleFuncs registers the handler function for the given pattern.
	HandleFunc(pattern string, handler http.HandlerFunc)
}

A ServeMux lets you attach handlers to http url paths.

type Server

type Server struct {
	// Pin specifies the pincode used to pair
	// with the accessory.
	Pin string

	// Addr specifies the tcp address for the server
	// to listen to in form of "host:port".
	// If empty, a random port is used.
	Addr string

	MfiCompliant bool   // default false
	Protocol     string // default "1.0"
	SetupId      string
	Key          KeyPair // public and private key (generated and stored on disk)
	// contains filtered or unexported fields
}

A server handles incoming HTTP request for an accessory. The server uses dnssd to announce the accessory on the local network.

func NewServer

func NewServer(store Store, a *accessory.A, as ...*accessory.A) (*Server, error)

NewServer returns a new server give a store (to persist data) and accessories. If more than one accessory is added to the server, *a* acts as a bridge.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context) error

ListenAndServe starts the server.

func (*Server) ServeMux

func (s *Server) ServeMux() ServeMux

ServeMux returns the http handler.

type Session

type Session struct {
	Pairing Pairing
	// contains filtered or unexported fields
}

func NewSession

func NewSession(shared [32]byte, p Pairing) (*Session, error)

func (*Session) Decrypt

func (s *Session) Decrypt(r io.Reader) (io.Reader, error)

Decrypt returns the decrypted data

func (*Session) Encrypt

func (s *Session) Encrypt(r io.Reader) (io.Reader, error)

Encrypt return the encrypted data by splitting it into packets [ length (2 bytes)] [ data ] [ auth (16 bytes)]

type Store

type Store interface {

	// Set sets the value for the given key.
	Set(key string, value []byte) error

	// Get returns the value for the given key.
	Get(key string) ([]byte, error)

	// Delete deletes the value for the given key.
	Delete(key string) error

	// KeysWithSuffix returns a list keys with the give suffix.
	KeysWithSuffix(suffix string) ([]string, error)
}

A Store lets you store key-value pairs.

Directories

Path Synopsis
THIS FILE IS AUTO-GENERATED
THIS FILE IS AUTO-GENERATED
THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED Package characteristic implements the HomeKit characteristics.
THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED Package characteristic implements the HomeKit characteristics.
cmd
switch
This example show an example of a switch accessory which periodically changes it's state between on and off.
This example show an example of a switch accessory which periodically changes it's state between on and off.
gen
THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED
THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED THIS FILE IS AUTO-GENERATED

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL