feat(homekit): add HKSV support with motion detection and doorbell functionality

- Introduced HKSV configuration options in homekit.go, allowing for motion detection and doorbell features.
- Implemented API endpoints for triggering motion detection and doorbell events.
- Enhanced server.go to handle HKSV sessions and manage motion detection states.
- Created new accessory types for HKSV and doorbell in accessory.go.
- Added support for audio recording configurations in ch207.go.
- Defined new services for motion detection and doorbell in services_hksv.go.
- Implemented opack encoding/decoding for HDS protocol in opack.go and protocol.go.
- Updated OpenAPI documentation to reflect new endpoints and features.
- Extended schema.json to include HKSV configuration options.
This commit is contained in:
Sergey Krashevich
2026-03-04 13:50:50 +03:00
parent 01c7451556
commit ab27a042c1
12 changed files with 1551 additions and 5 deletions
+60
View File
@@ -19,6 +19,66 @@ func NewAccessory(manuf, model, name, serial, firmware string) *hap.Accessory {
return acc
}
func NewHKSVAccessory(manuf, model, name, serial, firmware string) *hap.Accessory {
rtpStream := ServiceCameraRTPStreamManagement()
motionSensor := ServiceMotionSensor()
operatingMode := ServiceCameraOperatingMode()
recordingMgmt := ServiceCameraEventRecordingManagement()
dataStreamMgmt := ServiceDataStreamManagement()
acc := &hap.Accessory{
AID: hap.DeviceAID,
Services: []*hap.Service{
hap.ServiceAccessoryInformation(manuf, model, name, serial, firmware),
rtpStream,
ServiceMicrophone(),
motionSensor,
operatingMode,
recordingMgmt,
dataStreamMgmt,
},
}
acc.InitIID()
// CameraOperatingMode links to RTPStreamManagement and RecordingManagement
operatingMode.Linked = []int{int(rtpStream.IID), int(recordingMgmt.IID)}
// CameraEventRecordingManagement links to DataStreamManagement and MotionSensor
recordingMgmt.Linked = []int{int(dataStreamMgmt.IID), int(motionSensor.IID)}
return acc
}
func NewHKSVDoorbellAccessory(manuf, model, name, serial, firmware string) *hap.Accessory {
rtpStream := ServiceCameraRTPStreamManagement()
motionSensor := ServiceMotionSensor()
operatingMode := ServiceCameraOperatingMode()
recordingMgmt := ServiceCameraEventRecordingManagement()
dataStreamMgmt := ServiceDataStreamManagement()
doorbell := ServiceDoorbell()
acc := &hap.Accessory{
AID: hap.DeviceAID,
Services: []*hap.Service{
hap.ServiceAccessoryInformation(manuf, model, name, serial, firmware),
rtpStream,
ServiceMicrophone(),
motionSensor,
operatingMode,
recordingMgmt,
dataStreamMgmt,
doorbell,
},
}
acc.InitIID()
// CameraOperatingMode links to RTPStreamManagement and RecordingManagement
operatingMode.Linked = []int{int(rtpStream.IID), int(recordingMgmt.IID)}
// CameraEventRecordingManagement links to DataStreamManagement, MotionSensor, and Doorbell
recordingMgmt.Linked = []int{int(dataStreamMgmt.IID), int(motionSensor.IID), int(doorbell.IID)}
return acc
}
func ServiceMicrophone() *hap.Service {
return &hap.Service{
Type: "112", // 'Microphone'
+13
View File
@@ -2,6 +2,19 @@ package camera
const TypeSupportedAudioRecordingConfiguration = "207"
//goland:noinspection ALL
const (
AudioRecordingCodecTypeAACELD = 2
AudioRecordingCodecTypeAACLC = 3
AudioRecordingSampleRate8Khz = 0
AudioRecordingSampleRate16Khz = 1
AudioRecordingSampleRate24Khz = 2
AudioRecordingSampleRate32Khz = 3
AudioRecordingSampleRate44Khz = 4
AudioRecordingSampleRate48Khz = 5
)
type SupportedAudioRecordingConfiguration struct {
CodecConfigs []AudioRecordingCodecConfiguration `tlv8:"1"`
}
+194
View File
@@ -0,0 +1,194 @@
package camera
import (
"github.com/AlexxIT/go2rtc/pkg/hap"
"github.com/AlexxIT/go2rtc/pkg/hap/tlv8"
)
func ServiceMotionSensor() *hap.Service {
return &hap.Service{
Type: "85",
Characters: []*hap.Character{
{
Type: "22",
Format: hap.FormatBool,
Value: false,
Perms: hap.EVPR,
},
{
Type: "75",
Format: hap.FormatBool,
Value: true,
Perms: hap.EVPR,
},
},
}
}
func ServiceCameraOperatingMode() *hap.Service {
return &hap.Service{
Type: "21A",
Characters: []*hap.Character{
{
Type: "21B",
Format: hap.FormatBool,
Value: true,
Perms: hap.EVPRPW,
},
{
Type: "223",
Format: hap.FormatBool,
Value: true,
Perms: hap.EVPRPW,
},
{
Type: "225",
Format: hap.FormatBool,
Value: true,
Perms: hap.EVPRPW,
},
},
}
}
func ServiceCameraEventRecordingManagement() *hap.Service {
val205, _ := tlv8.MarshalBase64(SupportedCameraRecordingConfiguration{
PrebufferLength: 4000,
EventTriggerOptions: 0x01, // motion
MediaContainerConfigurations: MediaContainerConfigurations{
MediaContainerType: 0, // fragmented MP4
MediaContainerParameters: MediaContainerParameters{
FragmentLength: 4000,
},
},
})
val206, _ := tlv8.MarshalBase64(SupportedVideoRecordingConfiguration{
CodecConfigs: []VideoRecordingCodecConfiguration{
{
CodecType: VideoCodecTypeH264,
CodecParams: VideoRecordingCodecParameters{
ProfileID: VideoCodecProfileHigh,
Level: VideoCodecLevel40,
Bitrate: 2000,
IFrameInterval: 4000,
},
CodecAttrs: VideoCodecAttributes{Width: 1920, Height: 1080, Framerate: 30},
},
{
CodecType: VideoCodecTypeH264,
CodecParams: VideoRecordingCodecParameters{
ProfileID: VideoCodecProfileMain,
Level: VideoCodecLevel31,
Bitrate: 1000,
IFrameInterval: 4000,
},
CodecAttrs: VideoCodecAttributes{Width: 1280, Height: 720, Framerate: 30},
},
},
})
val207, _ := tlv8.MarshalBase64(SupportedAudioRecordingConfiguration{
CodecConfigs: []AudioRecordingCodecConfiguration{
{
CodecType: AudioRecordingCodecTypeAACLC,
CodecParams: []AudioRecordingCodecParameters{
{
Channels: 1,
BitrateMode: []byte{AudioCodecBitrateVariable},
SampleRate: []byte{AudioRecordingSampleRate24Khz, AudioRecordingSampleRate32Khz, AudioRecordingSampleRate48Khz},
MaxAudioBitrate: []uint32{64},
},
},
},
},
})
return &hap.Service{
Type: "204",
Characters: []*hap.Character{
{
Type: "B0",
Format: hap.FormatUInt8,
Value: 0,
Perms: hap.EVPRPW,
},
{
Type: TypeSupportedCameraRecordingConfiguration,
Format: hap.FormatTLV8,
Value: val205,
Perms: hap.EVPR,
},
{
Type: TypeSupportedVideoRecordingConfiguration,
Format: hap.FormatTLV8,
Value: val206,
Perms: hap.EVPR,
},
{
Type: TypeSupportedAudioRecordingConfiguration,
Format: hap.FormatTLV8,
Value: val207,
Perms: hap.EVPR,
},
{
Type: TypeSelectedCameraRecordingConfiguration,
Format: hap.FormatTLV8,
Value: "",
Perms: hap.EVPRPW,
},
{
Type: "226",
Format: hap.FormatUInt8,
Value: 0,
Perms: hap.EVPRPW,
},
},
}
}
func ServiceDataStreamManagement() *hap.Service {
val130, _ := tlv8.MarshalBase64(SupportedDataStreamTransportConfiguration{
Configs: []TransferTransportConfiguration{
{TransportType: 0}, // TCP
},
})
return &hap.Service{
Type: "129",
Characters: []*hap.Character{
{
Type: TypeSupportedDataStreamTransportConfiguration,
Format: hap.FormatTLV8,
Value: val130,
Perms: hap.PR,
},
{
Type: TypeSetupDataStreamTransport,
Format: hap.FormatTLV8,
Value: "",
Perms: []string{"pr", "pw", "wr"},
},
{
Type: "37",
Format: hap.FormatString,
Value: "1.0",
Perms: hap.PR,
},
},
}
}
func ServiceDoorbell() *hap.Service {
return &hap.Service{
Type: "121",
Characters: []*hap.Character{
{
Type: "73",
Format: hap.FormatUInt8,
Value: nil,
Perms: hap.EVPR,
},
},
}
}