feat(homekit): add ONVIF motion detection support

- implement ONVIF motion watcher to handle motion events
- add configuration options for motion hold time and ONVIF URL
- remap motion mode from "onvif" to "api" for compatibility
- log ONVIF motion watcher activity for better debugging

feat(onvif): implement event subscription for motion detection
- create PullPoint subscription to receive motion events
- implement methods for pulling messages and renewing subscriptions
- handle event requests and responses specific to motion detection

test(onvif): add unit tests for motion event parsing and subscription
- create tests for parsing various motion event XML responses
- verify correct handling of multiple notifications and edge cases
- test resolving event addresses for ONVIF clients

fix(hksv): improve motion detection logging
- log warnings when accessory or character not found during motion detection
- log number of listeners notified during motion state changes

feat(hap): add listener count method
- introduce method to retrieve the number of listeners for a character

feat(onvif): enhance ONVIF client with event URL handling
- extract event URL from ONVIF device response for subscription management
This commit is contained in:
Sergey Krashevich
2026-03-09 13:06:57 +03:00
parent e3d1085a6d
commit 8a21809f18
10 changed files with 1004 additions and 3 deletions
+32 -1
View File
@@ -7,6 +7,7 @@ import (
"net/http"
"strings"
"sync"
"time"
"github.com/AlexxIT/go2rtc/internal/api"
"github.com/AlexxIT/go2rtc/internal/app"
@@ -36,6 +37,8 @@ func Init() {
HKSV bool `yaml:"hksv"`
Motion string `yaml:"motion"`
MotionThreshold float64 `yaml:"motion_threshold"`
MotionHoldTime float64 `yaml:"motion_hold_time"`
OnvifURL string `yaml:"onvif_url"`
Speaker *bool `yaml:"speaker"`
} `yaml:"homekit"`
}
@@ -71,6 +74,12 @@ func Init() {
proxyURL = url
}
// Remap "onvif" → "api" for hksv.Server; ONVIF watcher drives motion externally.
motionMode := conf.Motion
if motionMode == "onvif" {
motionMode = "api"
}
srv, err := hksv.NewServer(hksv.Config{
StreamName: id,
Pin: conf.Pin,
@@ -81,7 +90,7 @@ func Init() {
Pairings: conf.Pairings,
ProxyURL: proxyURL,
HKSV: conf.HKSV,
MotionMode: conf.Motion,
MotionMode: motionMode,
MotionThreshold: conf.MotionThreshold,
Speaker: conf.Speaker,
UserAgent: app.UserAgent,
@@ -98,6 +107,28 @@ func Init() {
continue
}
// Start ONVIF motion watcher if configured.
if conf.Motion == "onvif" {
onvifURL := conf.OnvifURL
if onvifURL == "" {
sources := stream.Sources()
log.Debug().Str("stream", id).Strs("sources", sources).
Msg("[homekit] onvif motion: searching for ONVIF URL in stream sources")
onvifURL = findOnvifURL(sources)
}
if onvifURL == "" {
log.Warn().Str("stream", id).Msg("[homekit] onvif motion: no ONVIF URL found, set onvif_url or use onvif:// stream source")
} else {
holdTime := time.Duration(conf.MotionHoldTime) * time.Second
if holdTime <= 0 {
holdTime = 30 * time.Second
}
log.Info().Str("stream", id).Str("onvif_url", onvifURL).
Dur("hold_time", holdTime).Msg("[homekit] starting ONVIF motion watcher")
startOnvifMotionWatcher(srv, onvifURL, holdTime, log)
}
}
entry := srv.MDNSEntry()
entries = append(entries, entry)