hap

package module
v0.0.31 Latest Latest
Warning

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

Go to latest
Published: Mar 5, 2024 License: Apache-2.0 Imports: 37 Imported by: 66

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.
  • You can create your own persistent storage by implementing the Store interface.
  • Setting the value of a characteristic can now fail. Fixes hc#163
  • You can define custom http handlers. Fixes 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
)

Status codes for json communication.

View Source
const (
	TlvErrorUnknown        = 0x1
	TlvErrorInvalidRequest = 0x2
	TlvErrorAuthentication = 0x2
	TlvErrorBackoff        = 0x3
	TlvErrorMaxPeers       = 0x4
	TlvErrorUnknownPeer    = 0x4
	TlvErrorMaxTries       = 0x5
	TlvErrorUnavailable    = 0x6
	TlvErrorBusy           = 0x7
)

Error codes for TLV8 communication.

View Source
const (
	// HTTPContentTypePairingTLV8 is the HTTP content type for tlv8 data
	HTTPContentTypePairingTLV8 = "application/pairing+tlv8"

	// HTTPContentTypeHAPJson is the HTTP content type for json data
	HTTPContentTypeHAPJson = "application/hap+json"
)
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 is the user permission for a paired controller.
	PermissionUser byte = 0x0
	// PermissionAdmin is the administrator permission for a paired controller.
	PermissionAdmin byte = 0x1
)
View Source
const (
	M1 byte = 0x1
	M2 byte = 0x2
	M3 byte = 0x3
	M4 byte = 0x4
	M5 byte = 0x5
	M6 byte = 0x6
)

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 JsonError added in v0.0.10

func JsonError(res http.ResponseWriter, status int) error

JsonErrors sends an HTTP 500 (bad request) response including the status in the body.

func JsonMultiStatus added in v0.0.10

func JsonMultiStatus(res http.ResponseWriter, body interface{}) error

JsonMultiStatus sends an HTTP 207 (multi status) response.

func JsonOK added in v0.0.10

func JsonOK(res http.ResponseWriter, body interface{}) error

JsonOK sends an HTTP 200 (ok) response.

func NewChunkedWriter

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

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

Types

type KeyPair

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

KeyPair holds public and private key.

type Pairing

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

Pairing is the pairing of a controller with the server.

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)
	// Mount attaches another http.Handler along ./pattern/*
	Mount(pattern string, handler http.Handler)
}

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

	// Ifaces specifies at which interface the
	// associated dnssd service is announced.
	Ifaces []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 given a store (to persist data) and accessories. If more than one accessory is added to the server, *a* acts as a bridge.

func (*Server) DelTimedWrite added in v0.0.27

func (s *Server) DelTimedWrite(request *http.Request)

func (*Server) IsAuthorized added in v0.0.10

func (s *Server) IsAuthorized(request *http.Request) bool

IsAuthorized returns true if the provided request is authorized to access accessory data.

func (*Server) IsPaired added in v0.0.22

func (s *Server) IsPaired() bool

IsPaired returns true if the server is paired with a client (iOS).

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.

func (*Server) SetTimedWrite added in v0.0.27

func (s *Server) SetTimedWrite(ttl, pid uint64, request *http.Request)

func (*Server) TimedWrite added in v0.0.27

func (s *Server) TimedWrite(request *http.Request) *TimedWrite

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.

func NewFsStore

func NewFsStore(dir string) Store

func NewMemStore

func NewMemStore() Store

type TimedWrite added in v0.0.27

type TimedWrite struct {
	// contains filtered or unexported fields
}

Directories

Path Synopsis
THIS FILE IS AUTO-GENERATED
THIS FILE IS AUTO-GENERATED
Package characteristic implements the HomeKit characteristics.
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

Jump to

Keyboard shortcuts

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