mirror of
https://github.com/koush/scrypted.git
synced 2026-05-04 21:30:30 +01:00
homekit: update hap
This commit is contained in:
1593
plugins/homekit/package-lock.json
generated
1593
plugins/homekit/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.2.13",
|
||||
"version": "1.2.14",
|
||||
"description": "HomeKit Plugin for Scrypted",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
@@ -36,7 +36,7 @@
|
||||
"dependencies": {
|
||||
"@koush/werift-src": "file:../../external/werift",
|
||||
"check-disk-space": "^3.3.0",
|
||||
"hap-nodejs": "file:../../external/HAP-NodeJS",
|
||||
"hap-nodejs": "^0.11.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mkdirp": "^1.0.4"
|
||||
},
|
||||
|
||||
@@ -8,34 +8,6 @@ import { Categories, EventedHTTPServer, HAPStorage } from './hap';
|
||||
import { randomPinCode } from './pincode';
|
||||
import './types';
|
||||
|
||||
class HAPLocalStorage {
|
||||
initSync() {
|
||||
|
||||
}
|
||||
getItem(key: string): any {
|
||||
const data = localStorage.getItem(key);
|
||||
if (!data)
|
||||
return;
|
||||
return JSON.parse(data);
|
||||
}
|
||||
setItemSync(key: string, value: any) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
removeItemSync(key: string) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
persistSync() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// HAP storage seems to be global?
|
||||
export function initializeHapStorage() {
|
||||
HAPStorage.setStorage(new HAPLocalStorage());
|
||||
}
|
||||
|
||||
|
||||
export function createHAPUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export * from 'hap-nodejs/src/lib/definitions'; // must be loaded before Characteristic and Service class
|
||||
export * from 'hap-nodejs/src/lib/Accessory';
|
||||
export * as uuid from 'hap-nodejs/src/lib/util/uuid';
|
||||
export * from 'hap-nodejs/src/lib/Characteristic';
|
||||
export * from 'hap-nodejs/src/lib/camera';
|
||||
export * from 'hap-nodejs/src/lib/camera/RecordingManagement';
|
||||
export * from 'hap-nodejs/src/lib/model/ControllerStorage';
|
||||
export * from 'hap-nodejs/src/lib/util/eventedhttp';
|
||||
export * from 'hap-nodejs/src/lib/controller/CameraController';
|
||||
export * from 'hap-nodejs/src/lib/datastream/DataStreamServer';
|
||||
export * from 'hap-nodejs/src/lib/Service';
|
||||
export * from 'hap-nodejs/src/types';
|
||||
export * from 'hap-nodejs/src/lib/model/HAPStorage';
|
||||
export * from 'hap-nodejs/src/lib/Bridge';
|
||||
export * from 'hap-nodejs/dist/lib/definitions'; // must be loaded before Characteristic and Service class
|
||||
export * from 'hap-nodejs/dist/lib/Accessory';
|
||||
export * as uuid from 'hap-nodejs/dist/lib/util/uuid';
|
||||
export * from 'hap-nodejs/dist/lib/Characteristic';
|
||||
export * from 'hap-nodejs/dist/lib/camera';
|
||||
export * from 'hap-nodejs/dist/lib/camera/RecordingManagement';
|
||||
export * from 'hap-nodejs/dist/lib/model/ControllerStorage';
|
||||
export * from 'hap-nodejs/dist/lib/util/eventedhttp';
|
||||
export * from 'hap-nodejs/dist/lib/controller/CameraController';
|
||||
export * from 'hap-nodejs/dist/lib/datastream/DataStreamServer';
|
||||
export * from 'hap-nodejs/dist/lib/Service';
|
||||
export * from 'hap-nodejs/dist/types';
|
||||
export * from 'hap-nodejs/dist/lib/model/HAPStorage';
|
||||
export * from 'hap-nodejs/dist/lib/Bridge';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { maybeAddBatteryService } from './battery';
|
||||
import { CameraMixin, canCameraMixin } from './camera-mixin';
|
||||
import { SnapshotThrottle, supportedTypes } from './common';
|
||||
import { Accessory, Bridge, Categories, Characteristic, ControllerStorage, MDNSAdvertiser, PublishInfo, Service } from './hap';
|
||||
import { createHAPUsernameStorageSettingsDict, getHAPUUID, getRandomPort as createRandomPort, initializeHapStorage, logConnections, typeToCategory } from './hap-utils';
|
||||
import { createHAPUsernameStorageSettingsDict, getHAPUUID, getRandomPort as createRandomPort, logConnections, typeToCategory } from './hap-utils';
|
||||
import { HomekitMixin, HOMEKIT_MIXIN } from './homekit-mixin';
|
||||
import { addAccessoryDeviceInfo } from './info';
|
||||
import { randomPinCode } from './pincode';
|
||||
@@ -17,7 +17,6 @@ import { VideoClipsMixinProvider } from './video-clips-provider';
|
||||
|
||||
const { systemManager, deviceManager } = sdk;
|
||||
|
||||
initializeHapStorage();
|
||||
const includeToken = 4;
|
||||
|
||||
export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider, Settings, DeviceProvider {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sdk, { AudioSensor, Camera, Intercom, MotionSensor, ObjectsDetected, OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, VideoCamera, VideoCameraConfiguration } from '@scrypted/sdk';
|
||||
import { defaultObjectDetectionContactSensorTimeout } from '../camera-mixin';
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice, } from '../common';
|
||||
import { AudioRecordingCodec, AudioRecordingCodecType, AudioRecordingSamplerate, AudioStreamingCodec, AudioStreamingCodecType, AudioStreamingSamplerate, CameraController, CameraRecordingDelegate, CameraRecordingOptions, CameraStreamingOptions, Characteristic, CharacteristicEventTypes, DataStreamConnection, H264Level, H264Profile, OccupancySensor, RecordingManagement, Service, SRTPCryptoSuites, VideoCodecType, WithUUID } from '../hap';
|
||||
import { AudioRecordingCodec, AudioRecordingCodecType, AudioRecordingSamplerate, AudioStreamingCodec, AudioStreamingCodecType, AudioStreamingSamplerate, CameraController, CameraRecordingDelegate, CameraRecordingOptions, CameraStreamingOptions, Characteristic, CharacteristicEventTypes, DataStreamConnection, H264Level, H264Profile, MediaContainerType, OccupancySensor, RecordingManagement, Service, SRTPCryptoSuites, VideoCodecType, WithUUID } from '../hap';
|
||||
import { handleFragmentsRequests, iframeIntervalSeconds } from './camera/camera-recording';
|
||||
import { createCameraStreamingDelegate } from './camera/camera-streaming';
|
||||
import { FORCE_OPUS } from './camera/camera-utils';
|
||||
@@ -62,7 +62,6 @@ addSupportedType({
|
||||
const streamingOptions: CameraStreamingOptions = {
|
||||
video: {
|
||||
codec: {
|
||||
type: VideoCodecType.H264,
|
||||
levels: [H264Level.LEVEL3_1, H264Level.LEVEL3_2, H264Level.LEVEL4_0],
|
||||
profiles: [H264Profile.MAIN],
|
||||
},
|
||||
@@ -146,23 +145,17 @@ addSupportedType({
|
||||
// ensureHasWidthResolution(recordingResolutions, 1280, 720);
|
||||
// ensureHasWidthResolution(recordingResolutions, 1920, 1080);
|
||||
|
||||
const h265Support = storage.getItem('h265Support') === 'true';
|
||||
const codecType = h265Support ? VideoCodecType.H265 : VideoCodecType.H264
|
||||
|
||||
recordingOptions = {
|
||||
motionService: true,
|
||||
prebufferLength: numberPrebufferSegments * iframeIntervalSeconds * 1000,
|
||||
eventTriggerOptions: 0x01,
|
||||
mediaContainerConfigurations: [
|
||||
mediaContainerConfiguration: [
|
||||
{
|
||||
type: 0,
|
||||
type: MediaContainerType.FRAGMENTED_MP4,
|
||||
fragmentLength: iframeIntervalSeconds * 1000,
|
||||
}
|
||||
],
|
||||
|
||||
video: {
|
||||
codec: {
|
||||
type: codecType,
|
||||
type: VideoCodecType.H264,
|
||||
parameters: {
|
||||
levels: [H264Level.LEVEL3_1, H264Level.LEVEL3_2, H264Level.LEVEL4_0],
|
||||
profiles: [H264Profile.BASELINE, H264Profile.MAIN, H264Profile.HIGH],
|
||||
},
|
||||
@@ -186,7 +179,10 @@ addSupportedType({
|
||||
recording: {
|
||||
options: recordingOptions,
|
||||
delegate: recordingDelegate,
|
||||
}
|
||||
},
|
||||
sensors: {
|
||||
motion: true,
|
||||
},
|
||||
});
|
||||
|
||||
accessory.configureController(controller);
|
||||
@@ -220,20 +216,20 @@ addSupportedType({
|
||||
});
|
||||
}
|
||||
|
||||
persistBooleanCharacteristic(recordingManagement.getService(), Characteristic.Active);
|
||||
persistBooleanCharacteristic(recordingManagement.getService(), Characteristic.RecordingAudioActive);
|
||||
persistBooleanCharacteristic(controller.cameraOperatingModeService, Characteristic.EventSnapshotsActive);
|
||||
persistBooleanCharacteristic(controller.cameraOperatingModeService, Characteristic.HomeKitCameraActive);
|
||||
persistBooleanCharacteristic(controller.cameraOperatingModeService, Characteristic.PeriodicSnapshotsActive);
|
||||
persistBooleanCharacteristic(recordingManagement.recordingManagementService, Characteristic.Active);
|
||||
persistBooleanCharacteristic(recordingManagement.recordingManagementService, Characteristic.RecordingAudioActive);
|
||||
persistBooleanCharacteristic(recordingManagement.operatingModeService, Characteristic.EventSnapshotsActive);
|
||||
persistBooleanCharacteristic(recordingManagement.operatingModeService, Characteristic.HomeKitCameraActive);
|
||||
persistBooleanCharacteristic(recordingManagement.operatingModeService, Characteristic.PeriodicSnapshotsActive);
|
||||
|
||||
if (!device.interfaces.includes(ScryptedInterface.OnOff)) {
|
||||
persistBooleanCharacteristic(controller.cameraOperatingModeService, Characteristic.CameraOperatingModeIndicator);
|
||||
persistBooleanCharacteristic(recordingManagement.operatingModeService, Characteristic.CameraOperatingModeIndicator);
|
||||
}
|
||||
else {
|
||||
const indicator = controller.cameraOperatingModeService.getCharacteristic(Characteristic.CameraOperatingModeIndicator);
|
||||
const indicator = recordingManagement.operatingModeService.getCharacteristic(Characteristic.CameraOperatingModeIndicator);
|
||||
const linkStatusIndicator = storage.getItem('statusIndicator') === 'true';
|
||||
const property = `characteristic-v2-${Characteristic.CameraOperatingModeIndicator.UUID}`
|
||||
bindCharacteristic(device, ScryptedInterface.OnOff, controller.cameraOperatingModeService, Characteristic.CameraOperatingModeIndicator, () => {
|
||||
bindCharacteristic(device, ScryptedInterface.OnOff, recordingManagement.operatingModeService, Characteristic.CameraOperatingModeIndicator, () => {
|
||||
if (!linkStatusIndicator)
|
||||
return storage.getItem(property) === 'true' ? 1 : 0;
|
||||
|
||||
@@ -251,7 +247,7 @@ addSupportedType({
|
||||
});
|
||||
}
|
||||
|
||||
recordingManagement.getService().getCharacteristic(Characteristic.SelectedCameraRecordingConfiguration)
|
||||
recordingManagement.recordingManagementService.getCharacteristic(Characteristic.SelectedCameraRecordingConfiguration)
|
||||
.on(CharacteristicEventTypes.GET, callback => {
|
||||
callback(null, storage.getItem(storageKeySelectedRecordingConfiguration) || '');
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import mkdirp from 'mkdirp';
|
||||
import net from 'net';
|
||||
import { Duplex, Readable, Writable } from 'stream';
|
||||
import { } from '../../common';
|
||||
import { AudioRecordingCodecType, AudioRecordingSamplerateValues, CameraRecordingConfiguration, DataStreamConnection } from '../../hap';
|
||||
import { AudioRecordingCodecType, CameraRecordingConfiguration, DataStreamConnection } from '../../hap';
|
||||
import type { HomeKitPlugin } from "../../main";
|
||||
import { getCameraRecordingFiles, HksvVideoClip, VIDEO_CLIPS_NATIVE_ID } from './camera-recording-files';
|
||||
import { checkCompatibleCodec, FORCE_OPUS, transcodingDebugModeWarning } from './camera-utils';
|
||||
@@ -33,6 +33,14 @@ const allowedNaluTypes = [
|
||||
NAL_TYPE_DELIMITER,
|
||||
];
|
||||
|
||||
const AudioRecordingSamplerateValues = {
|
||||
0: 8,
|
||||
1: 16,
|
||||
2: 24,
|
||||
3: 32,
|
||||
4: 44.1,
|
||||
5: 48,
|
||||
};
|
||||
|
||||
async function checkMp4StartsWithKeyFrame(console: Console, mp4: Buffer) {
|
||||
const cp = child_process.spawn(await mediaManager.getFFmpegPath(), [
|
||||
@@ -102,7 +110,7 @@ export async function* handleFragmentsRequests(connection: DataStreamConnection,
|
||||
const saveRecordings = debugMode.recording;
|
||||
|
||||
// request more than needed, and determine what to do with the fragments after receiving them.
|
||||
const prebuffer = configuration.mediaContainerConfiguration.prebufferLength * 2.5;
|
||||
const prebuffer = configuration.prebufferLength * 2.5;
|
||||
|
||||
const media = await device.getVideoStream({
|
||||
destination: 'remote-recorder',
|
||||
@@ -153,7 +161,7 @@ export async function* handleFragmentsRequests(connection: DataStreamConnection,
|
||||
width: configuration.videoCodec.resolution[0],
|
||||
height: configuration.videoCodec.resolution[1],
|
||||
fps: configuration.videoCodec.resolution[2],
|
||||
max_bit_rate: configuration.videoCodec.bitrate,
|
||||
max_bit_rate: configuration.videoCodec.parameters.bitRate,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +218,7 @@ export async function* handleFragmentsRequests(connection: DataStreamConnection,
|
||||
const videoRecordingFilter = `scale=w='min(${configuration.videoCodec.resolution[0]},iw)':h=-2`;
|
||||
addVideoFilterArguments(videoArgs, videoRecordingFilter);
|
||||
videoArgs.push(
|
||||
'-b:v', `${configuration.videoCodec.bitrate}k`,
|
||||
'-b:v', `${configuration.videoCodec.parameters.bitRate}k`,
|
||||
"-bufsize", (2 * request.video.max_bit_rate).toString() + "k",
|
||||
"-maxrate", request.video.max_bit_rate.toString() + "k",
|
||||
// used to use this but switched to group of picture (gop) instead.
|
||||
|
||||
@@ -158,33 +158,33 @@ export function createCameraStreamingDelegate(device: ScryptedDevice & VideoCame
|
||||
// may not be reachable.
|
||||
// Return the incoming address, assuming the sanity checks pass. Otherwise, fall through
|
||||
// to the HAP-NodeJS implementation.
|
||||
let check: string;
|
||||
if (request.addressVersion === 'ipv4') {
|
||||
const localAddress = request.connection.localAddress;
|
||||
if (v4Regex.exec(localAddress)) {
|
||||
check = localAddress;
|
||||
}
|
||||
else if (v4v6Regex.exec(localAddress)) {
|
||||
// if this is a v4 over v6 address, parse it out.
|
||||
check = localAddress.substring('::ffff:'.length);
|
||||
}
|
||||
}
|
||||
else if (request.addressVersion === 'ipv6' && !v4Regex.exec(request.connection.localAddress)) {
|
||||
check = request.connection.localAddress;
|
||||
}
|
||||
// let check: string;
|
||||
// if (request.addressVersion === 'ipv4') {
|
||||
// const localAddress = request.connection.localAddress;
|
||||
// if (v4Regex.exec(localAddress)) {
|
||||
// check = localAddress;
|
||||
// }
|
||||
// else if (v4v6Regex.exec(localAddress)) {
|
||||
// // if this is a v4 over v6 address, parse it out.
|
||||
// check = localAddress.substring('::ffff:'.length);
|
||||
// }
|
||||
// }
|
||||
// else if (request.addressVersion === 'ipv6' && !v4Regex.exec(request.connection.localAddress)) {
|
||||
// check = request.connection.localAddress;
|
||||
// }
|
||||
|
||||
// ignore the IP if it is APIPA (Automatic Private IP Addressing)
|
||||
if (check?.startsWith('169.')) {
|
||||
check = undefined;
|
||||
}
|
||||
// // ignore the IP if it is APIPA (Automatic Private IP Addressing)
|
||||
// if (check?.startsWith('169.')) {
|
||||
// check = undefined;
|
||||
// }
|
||||
|
||||
// sanity check this address.
|
||||
if (check) {
|
||||
const infos = os.networkInterfaces()[request.connection.networkInterface];
|
||||
if (infos && infos.find(info => info.address === check)) {
|
||||
response.addressOverride = check;
|
||||
}
|
||||
}
|
||||
// // sanity check this address.
|
||||
// if (check) {
|
||||
// const infos = os.networkInterfaces()[request.connection.networkInterface];
|
||||
// if (infos && infos.find(info => info.address === check)) {
|
||||
// response.addressOverride = check;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
console.log('source address', response.addressOverride, videoPort, audioPort);
|
||||
|
||||
Reference in New Issue
Block a user