Adds support HomeKit cameras!
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package homekit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/brutella/hap/characteristic"
|
||||
"github.com/brutella/hap/tlv8"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Character struct {
|
||||
AID int `json:"aid,omitempty"`
|
||||
IID int `json:"iid"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
Event interface{} `json:"ev,omitempty"`
|
||||
Perms []string `json:"perms,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
//MaxDataLen int `json:"maxDataLen"`
|
||||
|
||||
listeners map[io.Writer]bool
|
||||
}
|
||||
|
||||
func (c *Character) AddListener(w io.Writer) {
|
||||
// TODO: sync.Mutex
|
||||
if c.listeners == nil {
|
||||
c.listeners = map[io.Writer]bool{}
|
||||
}
|
||||
c.listeners[w] = true
|
||||
}
|
||||
|
||||
func (c *Character) RemoveListener(w io.Writer) {
|
||||
delete(c.listeners, w)
|
||||
|
||||
if len(c.listeners) == 0 {
|
||||
c.listeners = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Character) NotifyListeners(ignore io.Writer) error {
|
||||
if c.listeners == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := c.GenerateEvent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for w, _ := range c.listeners {
|
||||
if w == ignore {
|
||||
continue
|
||||
}
|
||||
if _, err = w.Write(data); err != nil {
|
||||
// error not a problem - just remove listener
|
||||
c.RemoveListener(w)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateEvent with raw HTTP headers
|
||||
func (c *Character) GenerateEvent() (data []byte, err error) {
|
||||
chars := Characters{
|
||||
Characters: []*Character{{AID: DeviceAID, IID: c.IID, Value: c.Value}},
|
||||
}
|
||||
if data, err = json.Marshal(chars); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res := http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 0,
|
||||
Header: http.Header{"Content-Type": []string{MimeJSON}},
|
||||
ContentLength: int64(len(data)),
|
||||
Body: io.NopCloser(bytes.NewReader(data)),
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer([]byte{0})
|
||||
if err = res.Write(buf); err != nil {
|
||||
return
|
||||
}
|
||||
copy(buf.Bytes(), "EVENT")
|
||||
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// Set new value and NotifyListeners
|
||||
func (c *Character) Set(v interface{}) (err error) {
|
||||
if err = c.Write(v); err != nil {
|
||||
return
|
||||
}
|
||||
return c.NotifyListeners(nil)
|
||||
}
|
||||
|
||||
// Write new value with right format
|
||||
func (c *Character) Write(v interface{}) (err error) {
|
||||
switch c.Format {
|
||||
case characteristic.FormatTLV8:
|
||||
var data []byte
|
||||
if data, err = tlv8.Marshal(v); err != nil {
|
||||
return
|
||||
}
|
||||
c.Value = base64.StdEncoding.EncodeToString(data)
|
||||
|
||||
case characteristic.FormatBool:
|
||||
switch v.(type) {
|
||||
case bool:
|
||||
c.Value = v.(bool)
|
||||
case float64:
|
||||
c.Value = v.(float64) != 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReadTLV8 value to right struct
|
||||
func (c *Character) ReadTLV8(v interface{}) (err error) {
|
||||
var data []byte
|
||||
if data, err = base64.StdEncoding.DecodeString(c.Value.(string)); err != nil {
|
||||
return
|
||||
}
|
||||
return tlv8.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (c *Character) ReadBool() bool {
|
||||
return c.Value.(bool)
|
||||
}
|
||||
Reference in New Issue
Block a user