Add support HomeKit server

This commit is contained in:
Alexey Khit
2023-09-02 06:35:04 +03:00
parent a101387b26
commit f00e646612
18 changed files with 1988 additions and 53 deletions

View File

@@ -1,6 +1,9 @@
package homekit
import (
"io"
"net"
"net/http"
"strings"
"github.com/AlexxIT/go2rtc/internal/api"
@@ -8,24 +11,168 @@ import (
"github.com/AlexxIT/go2rtc/internal/srtp"
"github.com/AlexxIT/go2rtc/internal/streams"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/hap"
"github.com/AlexxIT/go2rtc/pkg/hap/camera"
"github.com/AlexxIT/go2rtc/pkg/homekit"
"github.com/AlexxIT/go2rtc/pkg/mdns"
"github.com/rs/zerolog"
)
func Init() {
var cfg struct {
Mod map[string]struct {
Pin string `json:"pin"`
Name string `json:"name"`
DeviceID string `json:"device_id"`
DevicePrivate string `json:"device_private"`
Pairings []string `json:"pairings"`
//Listen string `json:"listen"`
} `yaml:"homekit"`
}
app.LoadConfig(&cfg)
log = app.GetLogger("homekit")
streams.HandleFunc("homekit", streamHandler)
api.HandleFunc("api/homekit", apiHandler)
if cfg.Mod == nil {
return
}
servers = map[string]*server{}
var entries []*mdns.ServiceEntry
for id, conf := range cfg.Mod {
stream := streams.Get(id)
if stream == nil {
log.Warn().Msgf("[homekit] missing stream: %s", id)
continue
}
if conf.Pin == "" {
conf.Pin = "19841984" // default PIN
}
pin, err := hap.SanitizePin(conf.Pin)
if err != nil {
log.Error().Err(err).Caller().Send()
continue
}
deviceID := calcDeviceID(conf.DeviceID, id) // random MAC-address
name := calcName(conf.Name, deviceID)
srv := &server{
stream: id,
srtp: srtp.Server,
pairings: conf.Pairings,
}
srv.hap = &hap.Server{
Pin: pin,
DeviceID: deviceID,
DevicePrivate: calcDevicePrivate(conf.DevicePrivate, id),
GetPair: srv.GetPair,
AddPair: srv.AddPair,
Handler: homekit.ServerHandler(srv),
}
if url := findHomeKitURL(stream); url != "" {
// 1. Act as transparent proxy for HomeKit camera
dial := func() (net.Conn, error) {
client, err := homekit.Dial(url, srtp.Server)
if err != nil {
return nil, err
}
return client.Conn(), nil
}
srv.hap.Handler = homekit.ProxyHandler(srv, dial)
} else {
// 2. Act as basic HomeKit camera
srv.accessory = camera.NewAccessory("AlexxIT", "go2rtc", name, "-", app.Version)
srv.hap.Handler = homekit.ServerHandler(srv)
}
entry := &mdns.ServiceEntry{
Name: name,
Port: uint16(api.Port()),
Info: map[string]string{
hap.TXTConfigNumber: "1",
hap.TXTFeatureFlags: "0",
hap.TXTDeviceID: deviceID,
hap.TXTModel: app.UserAgent,
hap.TXTProtoVersion: "1.1",
hap.TXTStateNumber: "1",
hap.TXTStatusFlags: hap.StatusNotPaired,
hap.TXTCategory: hap.CategoryCamera,
hap.TXTSetupHash: srv.hap.SetupHash(),
},
}
entries = append(entries, entry)
host := entry.Host(mdns.ServiceHAP)
servers[host] = srv
}
api.HandleFunc(hap.PathPairSetup, hapPairSetup)
api.HandleFunc(hap.PathPairVerify, hapPairVerify)
log.Trace().Msgf("[homekit] mnds: %s", entries)
go func() {
if err := mdns.Serve(mdns.ServiceHAP, entries); err != nil {
log.Error().Err(err).Caller().Send()
}
}()
}
var log zerolog.Logger
var servers map[string]*server
func streamHandler(url string) (core.Producer, error) {
return homekit.Dial(url, srtp.Server)
}
func hapPairSetup(w http.ResponseWriter, r *http.Request) {
srv, ok := servers[r.Host]
if !ok {
log.Error().Msg("[homekit] unknown host: " + r.Host)
return
}
conn, rw, err := w.(http.Hijacker).Hijack()
if err != nil {
return
}
defer conn.Close()
if err = srv.hap.PairSetup(r, rw, conn); err != nil {
log.Error().Err(err).Caller().Send()
}
}
func hapPairVerify(w http.ResponseWriter, r *http.Request) {
srv, ok := servers[r.Host]
if !ok {
log.Error().Msg("[homekit] unknown host: " + r.Host)
return
}
conn, rw, err := w.(http.Hijacker).Hijack()
if err != nil {
return
}
defer conn.Close()
if err = srv.hap.PairVerify(r, rw, conn); err != nil && err != io.EOF {
log.Error().Err(err).Caller().Send()
}
}
func findHomeKitURL(stream *streams.Stream) string {
sources := stream.Sources()
if len(sources) == 0 {