diff --git a/cmd/homekit/api.go b/cmd/homekit/api.go index 0c74b3b8..a5b3743d 100644 --- a/cmd/homekit/api.go +++ b/cmd/homekit/api.go @@ -54,7 +54,7 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { items = append(items, device) } - _= json.NewEncoder(w).Encode(items) + _ = json.NewEncoder(w).Encode(items) case "POST": // TODO: post params... @@ -62,10 +62,10 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") pin := r.URL.Query().Get("pin") - client, err := homekit.Pair(id, pin) + conn, err := homekit.Pair(id, pin) if err != nil { // log error - log.Error().Err(err).Msg("[api.homekit] pair") + log.Error().Err(err).Caller().Send() // response error _, err = w.Write([]byte(err.Error())) return @@ -73,15 +73,15 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { name := r.URL.Query().Get("name") dict := store.GetDict("streams") - dict[name] = client.URL() + dict[name] = conn.URL() if err = store.Set("streams", dict); err != nil { // log error - log.Error().Err(err).Msg("[api.homekit] save to store") + log.Error().Err(err).Caller().Send() // response error _, err = w.Write([]byte(err.Error())) } - streams.New(name, client.URL()) + streams.New(name, conn.URL()) case "DELETE": src := r.URL.Query().Get("src") @@ -91,36 +91,32 @@ func apiHandler(w http.ResponseWriter, r *http.Request) { continue } - client, err := homekit.NewClient(rawURL.(string)) + conn, err := homekit.Dial(rawURL.(string)) if err != nil { // log error - log.Error().Err(err).Msg("[api.homekit] new client") + log.Error().Err(err).Caller().Send() // response error _, err = w.Write([]byte(err.Error())) return } - if err = client.Dial(); err != nil { + go func() { + if err = conn.Handle(); err != nil { + log.Warn().Err(err).Caller().Send() + } + }() + + if err = conn.ListPairings(); err != nil { // log error - log.Error().Err(err).Msg("[api.homekit] client dial") + log.Error().Err(err).Caller().Send() // response error _, err = w.Write([]byte(err.Error())) return } - go client.Handle() - - if err = client.ListPairings(); err != nil { + if err = conn.DeletePairing(conn.ClientID); err != nil { // log error - log.Error().Err(err).Msg("[api.homekit] unpair") - // response error - _, err = w.Write([]byte(err.Error())) - return - } - - if err = client.DeletePairing(client.ClientID); err != nil { - // log error - log.Error().Err(err).Msg("[api.homekit] unpair") + log.Error().Err(err).Caller().Send() // response error _, err = w.Write([]byte(err.Error())) } diff --git a/cmd/homekit/producer.go b/cmd/homekit/client.go similarity index 88% rename from cmd/homekit/producer.go rename to cmd/homekit/client.go index 2b819ee8..5e5216ac 100644 --- a/cmd/homekit/producer.go +++ b/cmd/homekit/client.go @@ -14,17 +14,18 @@ import ( "strconv" ) -type Producer struct { +type Client struct { streamer.Element - client *homekit.Client + conn *homekit.Conn + exit chan error medias []*streamer.Media tracks []*streamer.Track sessions []*pkg.Session } -func (c *Producer) GetMedias() []*streamer.Media { +func (c *Client) GetMedias() []*streamer.Media { if c.medias == nil { c.medias = c.getMedias() } @@ -32,7 +33,7 @@ func (c *Producer) GetMedias() []*streamer.Media { return c.medias } -func (c *Producer) GetTrack(media *streamer.Media, codec *streamer.Codec) *streamer.Track { +func (c *Client) GetTrack(media *streamer.Media, codec *streamer.Codec) *streamer.Track { for _, track := range c.tracks { if track.Codec == codec { return track @@ -44,13 +45,13 @@ func (c *Producer) GetTrack(media *streamer.Media, codec *streamer.Codec) *strea return track } -func (c *Producer) Start() error { +func (c *Client) Start() error { if c.tracks == nil { return errors.New("producer without tracks") } // get our server local IP-address - host, _, err := net.SplitHostPort(c.client.LocalAddr()) + host, _, err := net.SplitHostPort(c.conn.LocalAddr()) if err != nil { return err } @@ -66,7 +67,7 @@ func (c *Producer) Start() error { hkSession.SetLocalEndpoint(host, uint16(port)) // create client for processing camera accessory - cam := camera.NewClient(c.client) + cam := camera.NewClient(c.conn) // try to start HomeKit stream if err = cam.StartStream2(hkSession); err != nil { panic(err) // TODO: fixme @@ -103,11 +104,11 @@ func (c *Producer) Start() error { c.sessions = []*pkg.Session{vs, as} - return nil + return <-c.exit } -func (c *Producer) Stop() error { - err := c.client.Close() +func (c *Client) Stop() error { + err := c.conn.Close() for _, session := range c.sessions { srtp.RemoveSession(session) @@ -116,10 +117,10 @@ func (c *Producer) Stop() error { return err } -func (c *Producer) getMedias() []*streamer.Media { +func (c *Client) getMedias() []*streamer.Media { var medias []*streamer.Media - accs, err := c.client.GetAccessories() + accs, err := c.conn.GetAccessories() acc := accs[0] if err != nil { panic(err) diff --git a/cmd/homekit/homekit.go b/cmd/homekit/homekit.go index 3084122d..2cceff55 100644 --- a/cmd/homekit/homekit.go +++ b/cmd/homekit/homekit.go @@ -20,20 +20,14 @@ func Init() { var log zerolog.Logger func streamHandler(url string) (streamer.Producer, error) { - client, err := homekit.NewClient(url) + conn, err := homekit.Dial(url) if err != nil { return nil, err } - if err = client.Dial(); err != nil { - return nil, err - } - - // start gorutine for reading responses from camera + exit := make(chan error) go func() { - if err = client.Handle(); err != nil { - log.Warn().Err(err).Msg("[homekit] client") - } + //start goroutine for reading responses from camera + exit <- conn.Handle() }() - - return &Producer{client: client}, nil + return &Client{conn: conn, exit: exit}, nil } diff --git a/pkg/homekit/camera/client.go b/pkg/homekit/camera/client.go index 22a7fa7d..905c3e94 100644 --- a/pkg/homekit/camera/client.go +++ b/pkg/homekit/camera/client.go @@ -8,10 +8,10 @@ import ( ) type Client struct { - client *homekit.Client + client *homekit.Conn } -func NewClient(client *homekit.Client) *Client { +func NewClient(client *homekit.Conn) *Client { return &Client{client: client} } diff --git a/pkg/homekit/client.go b/pkg/homekit/conn.go similarity index 90% rename from pkg/homekit/client.go rename to pkg/homekit/conn.go index 302b8423..a1417f57 100644 --- a/pkg/homekit/client.go +++ b/pkg/homekit/conn.go @@ -21,10 +21,11 @@ import ( "net/http" "net/url" "strings" + "time" ) -// Client for HomeKit. DevicePublic can be null. -type Client struct { +// Conn for HomeKit. DevicePublic can be null. +type Conn struct { streamer.Element DeviceAddress string // including port @@ -41,14 +42,14 @@ type Client struct { httpResponse chan *bufio.Reader } -func NewClient(rawURL string) (*Client, error) { +func Dial(rawURL string) (*Conn, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } query := u.Query() - c := &Client{ + c := &Conn{ DeviceAddress: u.Host, DeviceID: query.Get("device_id"), DevicePublic: DecodeKey(query.Get("device_public")), @@ -56,16 +57,38 @@ func NewClient(rawURL string) (*Client, error) { ClientPrivate: DecodeKey(query.Get("client_private")), } + if err = c.Dial(); err != nil { + return nil, err + } + return c, nil } -func Pair(deviceID, pin string) (*Client, error) { +//func NewConn(rawURL string) (*Conn, error) { +// u, err := url.Parse(rawURL) +// if err != nil { +// return nil, err +// } +// +// query := u.Query() +// c := &Conn{ +// DeviceAddress: u.Host, +// DeviceID: query.Get("device_id"), +// DevicePublic: DecodeKey(query.Get("device_public")), +// ClientID: query.Get("client_id"), +// ClientPrivate: DecodeKey(query.Get("client_private")), +// } +// +// return c, nil +//} + +func Pair(deviceID, pin string) (*Conn, error) { entry := mdns.GetEntry(deviceID) if entry == nil { return nil, errors.New("can't find device via mDNS") } - c := &Client{ + c := &Conn{ DeviceAddress: fmt.Sprintf("%s:%d", entry.AddrV4.String(), entry.Port), DeviceID: deviceID, ClientID: GenerateUUID(), @@ -85,32 +108,32 @@ func Pair(deviceID, pin string) (*Client, error) { return c, c.Pair(mfi, pin) } -func (c *Client) ClientPublic() []byte { +func (c *Conn) ClientPublic() []byte { return c.ClientPrivate[32:] } -func (c *Client) URL() string { +func (c *Conn) URL() string { return fmt.Sprintf( "homekit://%s?device_id=%s&device_public=%16x&client_id=%s&client_private=%32x", c.DeviceAddress, c.DeviceID, c.DevicePublic, c.ClientID, c.ClientPrivate, ) } -func (c *Client) DialAndServe() error { +func (c *Conn) DialAndServe() error { if err := c.Dial(); err != nil { return err } return c.Handle() } -func (c *Client) Dial() error { +func (c *Conn) Dial() error { // update device host before dial if host := mdns.GetAddress(c.DeviceID); host != "" { c.DeviceAddress = host } var err error - c.conn, err = net.Dial("tcp", c.DeviceAddress) + c.conn, err = net.DialTimeout("tcp", c.DeviceAddress, time.Second*5) if err != nil { return err } @@ -254,7 +277,7 @@ func (c *Client) Dial() error { } // https://github.com/apple/HomeKitADK/blob/master/HAP/HAPPairingPairSetup.c -func (c *Client) Pair(mfi bool, pin string) (err error) { +func (c *Conn) Pair(mfi bool, pin string) (err error) { pin = strings.ReplaceAll(pin, "-", "") if len(pin) != 8 { return fmt.Errorf("wrong PIN format: %s", pin) @@ -489,7 +512,7 @@ func (c *Client) Pair(mfi bool, pin string) (err error) { return nil } -func (c *Client) Close() error { +func (c *Conn) Close() error { if c.conn == nil { return nil } @@ -498,7 +521,7 @@ func (c *Client) Close() error { return conn.Close() } -func (c *Client) GetAccessories() ([]*Accessory, error) { +func (c *Conn) GetAccessories() ([]*Accessory, error) { res, err := c.Get("/accessories") if err != nil { return nil, err @@ -525,7 +548,7 @@ func (c *Client) GetAccessories() ([]*Accessory, error) { return p.Accessories, nil } -func (c *Client) GetCharacters(query string) ([]*Character, error) { +func (c *Conn) GetCharacters(query string) ([]*Character, error) { res, err := c.Get("/characteristics?id=" + query) if err != nil { return nil, err @@ -543,7 +566,7 @@ func (c *Client) GetCharacters(query string) ([]*Character, error) { return ch.Characters, nil } -func (c *Client) GetCharacter(char *Character) error { +func (c *Conn) GetCharacter(char *Character) error { query := fmt.Sprintf("%d.%d", char.AID, char.IID) chars, err := c.GetCharacters(query) if err != nil { @@ -553,7 +576,7 @@ func (c *Client) GetCharacter(char *Character) error { return nil } -func (c *Client) PutCharacters(characters ...*Character) (err error) { +func (c *Conn) PutCharacters(characters ...*Character) (err error) { for i, char := range characters { if char.Event != nil { char = &Character{AID: char.AID, IID: char.IID, Event: char.Event} @@ -579,7 +602,7 @@ func (c *Client) PutCharacters(characters ...*Character) (err error) { return } -func (c *Client) GetImage(width, height int) ([]byte, error) { +func (c *Conn) GetImage(width, height int) ([]byte, error) { res, err := c.Post( "/resource", []byte(fmt.Sprintf( `{"image-width":%d,"image-height":%d,"resource-type":"image","reason":0}`, @@ -609,7 +632,7 @@ func (c *Client) GetImage(width, height int) ([]byte, error) { // return nil //} -func (c *Client) ListPairings() error { +func (c *Conn) ListPairings() error { pReq := struct { Method byte `tlv8:"0"` State byte `tlv8:"6"` @@ -642,7 +665,7 @@ func (c *Client) ListPairings() error { return nil } -func (c *Client) PairingsAdd(clientID string, clientPublic []byte, admin bool) error { +func (c *Conn) PairingsAdd(clientID string, clientPublic []byte, admin bool) error { pReq := struct { Method byte `tlv8:"0"` Identifier string `tlv8:"1"` @@ -682,7 +705,7 @@ func (c *Client) PairingsAdd(clientID string, clientPublic []byte, admin bool) e return nil } -func (c *Client) DeletePairing(id string) error { +func (c *Conn) DeletePairing(id string) error { reqM1 := struct { State byte `tlv8:"6"` Method byte `tlv8:"0"` @@ -716,7 +739,7 @@ func (c *Client) DeletePairing(id string) error { return nil } -func (c *Client) LocalAddr() string { +func (c *Conn) LocalAddr() string { return c.conn.LocalAddr().String() } diff --git a/pkg/homekit/http.go b/pkg/homekit/http.go index de0c21ff..1297861b 100644 --- a/pkg/homekit/http.go +++ b/pkg/homekit/http.go @@ -23,7 +23,7 @@ const ( UriResource = "/resource" ) -func (c *Client) Write(p []byte) (r io.Reader, err error) { +func (c *Conn) Write(p []byte) (r io.Reader, err error) { if c.secure == nil { if _, err = c.conn.Write(p); err == nil { r = bufio.NewReader(c.conn) @@ -36,7 +36,7 @@ func (c *Client) Write(p []byte) (r io.Reader, err error) { return } -func (c *Client) Do(req *http.Request) (*http.Response, error) { +func (c *Conn) Do(req *http.Request) (*http.Response, error) { if c.secure == nil { // insecure requests if err := req.Write(c.conn); err != nil { @@ -56,7 +56,7 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { return http.ReadResponse(buf, req) } -func (c *Client) Get(uri string) (*http.Response, error) { +func (c *Conn) Get(uri string) (*http.Response, error) { req, err := http.NewRequest( "GET", "http://"+c.DeviceAddress+uri, nil, ) @@ -66,7 +66,7 @@ func (c *Client) Get(uri string) (*http.Response, error) { return c.Do(req) } -func (c *Client) Post(uri string, data []byte) (*http.Response, error) { +func (c *Conn) Post(uri string, data []byte) (*http.Response, error) { req, err := http.NewRequest( "POST", "http://"+c.DeviceAddress+uri, bytes.NewReader(data), @@ -85,7 +85,7 @@ func (c *Client) Post(uri string, data []byte) (*http.Response, error) { return c.Do(req) } -func (c *Client) Put(uri string, data []byte) (*http.Response, error) { +func (c *Conn) Put(uri string, data []byte) (*http.Response, error) { req, err := http.NewRequest( "PUT", "http://"+c.DeviceAddress+uri, bytes.NewReader(data), @@ -102,7 +102,7 @@ func (c *Client) Put(uri string, data []byte) (*http.Response, error) { return c.Do(req) } -func (c *Client) Handle() (err error) { +func (c *Conn) Handle() (err error) { defer func() { if c.conn == nil { err = nil