From 861632f92b02cda67fc7547db43341ca13f234b4 Mon Sep 17 00:00:00 2001 From: Alexey Khit Date: Tue, 12 Sep 2023 21:03:59 +0300 Subject: [PATCH] Add support events for HomeKit client --- pkg/hap/client.go | 31 +++++++++++++++++++ pkg/hap/client_http.go | 28 +++++++++++++++++ pkg/hap/event_reader.go | 68 ----------------------------------------- pkg/hap/helpers.go | 3 +- 4 files changed, 61 insertions(+), 69 deletions(-) delete mode 100644 pkg/hap/event_reader.go diff --git a/pkg/hap/client.go b/pkg/hap/client.go index e61a018f..8ce8c472 100644 --- a/pkg/hap/client.go +++ b/pkg/hap/client.go @@ -41,6 +41,9 @@ type Client struct { Conn net.Conn reader *bufio.Reader + + res chan *http.Response + err error } func NewClient(rawURL string) (*Client, error) { @@ -216,6 +219,8 @@ func (c *Client) Dial() (err error) { // new reader for new conn c.reader = bufio.NewReaderSize(c.Conn, 32*1024) // 32K like default request body + go c.eventsReader() + return } @@ -228,6 +233,32 @@ func (c *Client) Close() error { return conn.Close() } +func (c *Client) eventsReader() { + c.res = make(chan *http.Response) + + for { + var res *http.Response + if res, c.err = ReadResponse(c.reader, nil); c.err != nil { + break + } + + var body []byte + if body, c.err = io.ReadAll(res.Body); c.err != nil { + break + } + + res.Body = io.NopCloser(bytes.NewReader(body)) + + if res.Proto != ProtoEvent { + c.res <- res + } else if c.OnEvent != nil { + c.OnEvent(res) + } + } + + close(c.res) +} + func (c *Client) GetAccessories() ([]*Accessory, error) { res, err := c.Get(PathAccessories) if err != nil { diff --git a/pkg/hap/client_http.go b/pkg/hap/client_http.go index a6c775a2..360f48bc 100644 --- a/pkg/hap/client_http.go +++ b/pkg/hap/client_http.go @@ -1,6 +1,7 @@ package hap import ( + "bufio" "errors" "io" "net/http" @@ -22,6 +23,9 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { if err := req.Write(c.Conn); err != nil { return nil, err } + if c.res != nil { + return <-c.res, c.err + } return http.ReadResponse(c.reader, req) } @@ -54,3 +58,27 @@ func (c *Client) Post(path, contentType string, body io.Reader) (*http.Response, func (c *Client) Put(path, contentType string, body io.Reader) (*http.Response, error) { return c.Request("PUT", path, contentType, body) } + +const ProtoEvent = "EVENT/1.0" + +func ReadResponse(r *bufio.Reader, req *http.Request) (*http.Response, error) { + b, err := r.Peek(9) + if err != nil { + return nil, err + } + + if string(b) != ProtoEvent { + return http.ReadResponse(r, req) + } + + copy(b, "HTTP/1.1 ") + + res, err := http.ReadResponse(r, req) + if err != nil { + return nil, err + } + + res.Proto = ProtoEvent + + return res, nil +} diff --git a/pkg/hap/event_reader.go b/pkg/hap/event_reader.go deleted file mode 100644 index d2303b6c..00000000 --- a/pkg/hap/event_reader.go +++ /dev/null @@ -1,68 +0,0 @@ -package hap - -import ( - "io" - "os" - "time" -) - -type EventReader struct { - r io.Reader - ch chan []byte - err error - left []byte -} - -func NewEventReader(r io.Reader) *EventReader { - e := &EventReader{r: r, ch: make(chan []byte, 1)} - go e.background() - return e -} - -func (e *EventReader) background() { - b := make([]byte, 32*1024) - for { - n, err := e.r.Read(b) - if err != nil { - e.err = err - return - } - - if n >= 6 && string(b[:6]) == "EVENT " { - panic("TODO") - } - - // copy because will be overwriten - buf := make([]byte, n) - copy(buf, b) - e.ch <- buf - } -} - -func (e *EventReader) Read(p []byte) (n int, err error) { - if e.err != nil { - return 0, e.err - } - - // if something left after previous reading - if e.left != nil { - // if still something left - if n = copy(p, e.left); n < len(e.left) { - e.left = e.left[n:] - } else { - e.left = nil - } - return - } - - select { - case <-time.After(time.Second * 5): - return 0, os.ErrDeadlineExceeded - case b := <-e.ch: - if n = copy(p, b); n < len(b) { - e.left = b[n:] - } - } - - return -} diff --git a/pkg/hap/helpers.go b/pkg/hap/helpers.go index c68dc919..ea5e4059 100644 --- a/pkg/hap/helpers.go +++ b/pkg/hap/helpers.go @@ -66,7 +66,8 @@ type JSONCharacters struct { type JSONCharacter struct { AID uint8 `json:"aid"` IID uint64 `json:"iid"` - Value any `json:"value"` + Value any `json:"value,omitempty"` + Event any `json:"ev,omitempty"` } func SanitizePin(pin string) (string, error) {