Skip to content

Commit

Permalink
Merge pull request #42 from brutella/hap_config
Browse files Browse the repository at this point in the history
Add config to customise transport ip port, storage path and accessory pin
  • Loading branch information
brutella committed Nov 23, 2015
2 parents 3f37f2a + 2b3f7b8 commit 1325e12
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 25 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func main() {
}
sw := accessory.NewSwitch(info)

t, err := hap.NewIPTransport("00102003", sw.Accessory)
config := hap.Config{Pin: "00102003"}
t, err := hap.NewIPTransport(config, sw.Accessory)
if err != nil {
log.Fatal(err)
}
Expand Down
4 changes: 3 additions & 1 deletion _example/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ func main() {
Name: "Lamp",
}
sw := accessory.NewSwitch(switchInfo)
t, err := hap.NewIPTransport("00102003", sw.Accessory)

config := hap.Config{Pin: "12344321", Port: "12345", StoragePath: "./db"}
t, err := hap.NewIPTransport(config, sw.Accessory)

if err != nil {
log.Fatal(err)
Expand Down
84 changes: 69 additions & 15 deletions hap/ip_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,23 @@ import (
"github.com/gosexy/to"
)

// Config provides basic configuration for an IP transport
type Config struct {
// Path to the storage
// When empty, the tranport stores the data inside a folder named exactly like the accessory
StoragePath string

// Port at which transport is reachable e.g. 12345
// When empty, the transport uses a random port
Port string

// Pin with has to be entered on iOS client to pair with the accessory
// When empty, the pin 00102003 is used
Pin string
}

type ipTransport struct {
config Config
context netio.HAPContext
server server.Server
mutex *sync.Mutex
Expand All @@ -31,31 +47,52 @@ type ipTransport struct {
device netio.SecuredDevice
container *container.Container

// Used to communicate between different parts of the program (e.g. successful pairing with HomeKit)
emitter event.Emitter
}

// NewIPTransport creates a transport to provide accessories over IP.
// The pairing is secured using a 8-numbers pin.
// If more than one accessory is provided, the first becomes a bridge in HomeKit.
// It's fine when the bridge has no explicit services.
//
// All accessory specific data (crypto keys, ids) is stored in a folder named after the first accessory.
// So changing the order of the accessories or renaming the first accessory makes the stored
// data inaccessible to the tranport. In this case new crypto keys are created and the accessory
// appears as a new one to clients.
func NewIPTransport(pin string, a *accessory.Accessory, as ...*accessory.Accessory) (Transport, error) {
// The IP transports stores the crypto keys inside a database, which
// is by default inside a folder at the current working directory.
// The folder is named exactly as the accessory name.
//
// The transports can contain more than one accessory. If this is the
// case, the first accessory acts as the HomeKit bridge.
//
// *Important:* Changing the name of the accessory, or letting multiple
// transports store the data inside the same database lead to
// unexpected behavior – don't do that.
//
// The transport is secured with an 8-digit pin, which must be entered
// by an iOS client to successfully pair with the accessory. If the
// provided transport config does not specify any pin, 00102003 is used.
func NewIPTransport(config Config, a *accessory.Accessory, as ...*accessory.Accessory) (Transport, error) {
// Find transport name which is visible in mDNS
name := a.Name()
if len(name) == 0 {
log.Fatal("Invalid empty name for first accessory")
}

hapPin, err := NewPin(pin)
if err != nil {
return nil, err
default_config := Config{
StoragePath: name,
Pin: "00102003",
Port: "",
}

if dir := config.StoragePath; len(dir) > 0 {
default_config.StoragePath = dir
}

if pin := config.Pin; len(pin) > 0 {
default_config.Pin = pin
}

if port := config.Port; len(port) > 0 {
default_config.Port = ":" + port
}

storage, err := util.NewFileStorage(name)
storage, err := util.NewFileStorage(default_config.StoragePath)
if err != nil {
return nil, err
}
Expand All @@ -64,12 +101,19 @@ func NewIPTransport(pin string, a *accessory.Accessory, as ...*accessory.Accesso
// must be unique and stay the same over time
uuid := transportUUIDInStorage(storage)
database := db.NewDatabaseWithStorage(storage)
device, err := netio.NewSecuredDevice(uuid, hapPin, database)

hap_pin, err := NewPin(default_config.Pin)
if err != nil {
return nil, err
}

device, err := netio.NewSecuredDevice(uuid, hap_pin, database)

t := &ipTransport{
database: database,
name: name,
device: device,
config: default_config,
container: container.NewContainer(),
mutex: &sync.Mutex{},
context: netio.NewContextForSecuredDevice(device),
Expand All @@ -87,15 +131,25 @@ func NewIPTransport(pin string, a *accessory.Accessory, as ...*accessory.Accesso
}

func (t *ipTransport) Start() {
s := server.NewServer(t.context, t.database, t.container, t.device, t.mutex, t.emitter)
config := server.Config{
Port: t.config.Port,
Context: t.context,
Database: t.database,
Container: t.container,
Device: t.device,
Mutex: t.mutex,
Emitter: t.emitter,
}

s := server.NewServer(config)
t.server = s
port := to.Int64(s.Port())

mdns := NewMDNSService(t.name, t.device.Name(), int(port))
t.mdns = mdns

// Paired accessories must not be reachable for other clients since iOS 9
if t.isPaired() {
// Paired accessories must not be reachable for other clients since iOS 9
mdns.SetReachable(false)
}

Expand Down
27 changes: 19 additions & 8 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ type Server interface {
Stop()
}

type Config struct {
Port string
Context netio.HAPContext
Database db.Database
Container *container.Container
Device netio.SecuredDevice
Mutex *sync.Mutex
Emitter event.Emitter
}

type hkServer struct {
context netio.HAPContext
database db.Database
Expand All @@ -44,25 +54,26 @@ type hkServer struct {
}

// NewServer returns a server
func NewServer(ctx netio.HAPContext, d db.Database, c *container.Container, device netio.SecuredDevice, mutex *sync.Mutex, emitter event.Emitter) Server {
func NewServer(c Config) Server {

// os gives us a free Port when Port is ""
ln, err := net.Listen("tcp", "")
ln, err := net.Listen("tcp", c.Port)
if err != nil {
log.Fatal(err)
}

_, port, _ := net.SplitHostPort(ln.Addr().String())

s := hkServer{
context: ctx,
database: d,
container: c,
device: device,
context: c.Context,
database: c.Database,
container: c.Container,
device: c.Device,
mux: http.NewServeMux(),
mutex: mutex,
mutex: c.Mutex,
listener: ln.(*net.TCPListener),
port: port,
emitter: emitter,
emitter: c.Emitter,
}

s.setupEndpoints()
Expand Down

0 comments on commit 1325e12

Please sign in to comment.