mirror of
https://github.com/koush/scrypted.git
synced 2026-03-06 11:21:58 +00:00
homekit: fix qr code accessory race condition. add slow connection setting to add all home hubs.
This commit is contained in:
4
plugins/homekit/package-lock.json
generated
4
plugins/homekit/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.1.9",
|
||||
"version": "1.1.12",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.1.9",
|
||||
"version": "1.1.12",
|
||||
"dependencies": {
|
||||
"@koush/qrcode-terminal": "^0.12.0",
|
||||
"@koush/werift-src": "file:../../external/werift",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.1.9",
|
||||
"version": "1.1.12",
|
||||
"description": "HomeKit Plugin for Scrypted",
|
||||
"scripts": {
|
||||
"prepublishOnly": "NODE_ENV=production scrypted-webpack",
|
||||
|
||||
@@ -111,11 +111,12 @@ export function createHAPUsernameStorageSettingsDict(): StorageSettingsDict<'mac
|
||||
}
|
||||
}
|
||||
|
||||
export function logConnections(console: Console, accessory: any) {
|
||||
export function logConnections(console: Console, accessory: any, seenConnections: Set<string>) {
|
||||
const server: EventedHTTPServer = accessory._server.httpServer;
|
||||
server.on('connection-opened', connection => {
|
||||
connection.on('authenticated', () => {
|
||||
console.log('HomeKit Connection', connection.remoteAddress);
|
||||
seenConnections.add(connection.remoteAddress);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import qrcode from '@koush/qrcode-terminal';
|
||||
import { StorageSettings } from '@scrypted/common/src/settings';
|
||||
import { SettingsMixinDeviceOptions } from '@scrypted/common/src/settings-mixin';
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
import sdk, { DeviceProvider, MixinProvider, Online, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting, Settings } from '@scrypted/sdk';
|
||||
import crypto from 'crypto';
|
||||
import packageJson from "../package.json";
|
||||
import { maybeAddBatteryService } from './battery';
|
||||
import { CameraMixin, canCameraMixin } from './camera-mixin';
|
||||
import { SnapshotThrottle, supportedTypes } from './common';
|
||||
import { Category, Accessory, Bridge, Categories, Characteristic, ControllerStorage, EventedHTTPServer, MDNSAdvertiser, PublishInfo, Service } from './hap';
|
||||
import { Accessory, Bridge, Categories, Characteristic, ControllerStorage, MDNSAdvertiser, PublishInfo, Service } from './hap';
|
||||
import { createHAPUsernameStorageSettingsDict, getAddresses, getHAPUUID, getRandomPort as createRandomPort, initializeHapStorage, logConnections, typeToCategory } from './hap-utils';
|
||||
import { HomekitMixin, HOMEKIT_MIXIN } from './homekit-mixin';
|
||||
import { randomPinCode } from './pincode';
|
||||
import './types';
|
||||
import { VIDEO_CLIPS_NATIVE_ID } from './types/camera/camera-recording-files';
|
||||
import { VideoClipsMixinProvider } from './video-clips-provider';
|
||||
import crypto from 'crypto';
|
||||
import { access } from 'fs';
|
||||
|
||||
const { systemManager, deviceManager } = sdk;
|
||||
|
||||
@@ -23,6 +21,7 @@ initializeHapStorage();
|
||||
const includeToken = 4;
|
||||
|
||||
export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider, Settings, DeviceProvider {
|
||||
seenConnections = new Set<string>();
|
||||
bridge = new Bridge('Scrypted', getHAPUUID(this.storage));
|
||||
snapshotThrottles = new Map<string, SnapshotThrottle>();
|
||||
standalones = new Map<string, Accessory>();
|
||||
@@ -91,10 +90,23 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
choices: [MDNSAdvertiser.BONJOUR, MDNSAdvertiser.CIAO],
|
||||
defaultValue: MDNSAdvertiser.CIAO,
|
||||
},
|
||||
slowConnections: {
|
||||
group: 'Network',
|
||||
title: 'Slow Mode Addresses',
|
||||
description: 'The addressesses of Home Hubs and iOS clients that will always be served remote/medium streams.',
|
||||
type: 'string',
|
||||
multiple: true,
|
||||
combobox: true,
|
||||
onGet: async () => {
|
||||
return {
|
||||
choices: [...this.seenConnections],
|
||||
}
|
||||
}
|
||||
},
|
||||
lastKnownHomeHub: {
|
||||
hide: true,
|
||||
description: 'The last home hub to request a recording. Internally used to determine if a streaming request is coming from remote wifi.',
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
constructor() {
|
||||
@@ -222,7 +234,7 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
this.publishAccessory(accessory, storageSettings.values.mac, standaloneCategory);
|
||||
if (!hasPublished) {
|
||||
hasPublished = true;
|
||||
logConnections(mixinConsole, accessory);
|
||||
logConnections(mixinConsole, accessory, this.seenConnections);
|
||||
|
||||
qrcode.generate(accessory.setupURI(), { small: true }, (code: string) => {
|
||||
mixinConsole.log('Pairing QR Code:')
|
||||
@@ -256,7 +268,7 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
updateDeviceAdvertisement();
|
||||
if (!published)
|
||||
mixinConsole.warn('Device is in accessory mode and was offline during HomeKit startup. Device will not be started until it comes back online. Disable accessory mode if this is in error.');
|
||||
|
||||
|
||||
// throttle this in case the device comes back online very quickly.
|
||||
device.listen(ScryptedInterface.Online, () => {
|
||||
const isOnline = !device.interfaces.includes(ScryptedInterface.Online) || device.online;
|
||||
@@ -294,7 +306,7 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
};
|
||||
|
||||
this.bridge.publish(publishInfo, true);
|
||||
logConnections(this.console, this.bridge);
|
||||
logConnections(this.console, this.bridge, this.seenConnections);
|
||||
|
||||
qrcode.generate(this.bridge.setupURI(), { small: true }, (code: string) => {
|
||||
this.console.log('Pairing QR Code:')
|
||||
@@ -378,8 +390,12 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
ret = new HomekitMixin(options);
|
||||
}
|
||||
|
||||
const accessory = this.standalones.get(mixinDeviceState.id);
|
||||
ret.storageSettings.settings.qrCode.onPut = () => {
|
||||
const accessory = this.standalones.get(mixinDeviceState.id);
|
||||
if (!accessory) {
|
||||
ret.console.error('Accessory not found. Try reloading the HomeKit plugin?');
|
||||
return;
|
||||
}
|
||||
if (!accessory._setupID) {
|
||||
ret.console.warn('This accessory is currently unpublished since it is offline. The accessory will be published now to generate the QR Code and allow pairing. The device may not respond to commands.');
|
||||
const standaloneCategory = typeToCategory(ret.type);
|
||||
|
||||
@@ -9,7 +9,7 @@ import fs from 'fs';
|
||||
import mkdirp from 'mkdirp';
|
||||
import net from 'net';
|
||||
import { Duplex, Readable, Writable } from 'stream';
|
||||
import { } from '../../common';
|
||||
import { } from '../../common';
|
||||
import { DataStreamConnection, AudioRecordingCodecType, AudioRecordingSamplerateValues, CameraRecordingConfiguration } from '../../hap';
|
||||
import { getCameraRecordingFiles, HksvVideoClip, VIDEO_CLIPS_NATIVE_ID } from './camera-recording-files';
|
||||
import { checkCompatibleCodec, FORCE_OPUS, transcodingDebugModeWarning } from './camera-utils';
|
||||
@@ -122,14 +122,19 @@ export async function* handleFragmentsRequests(connection: DataStreamConnection,
|
||||
const isDefinitelyNotAAC = !audioCodec || audioCodec.toLowerCase().indexOf('aac') === -1;
|
||||
const transcodingDebugMode = storage.getItem('transcodingDebugMode') === 'true';
|
||||
const transcodeRecording = !!ffmpegInput.h264EncoderArguments?.length;
|
||||
const incompatibleStream = noAudio || transcodeRecording || isDefinitelyNotAAC;
|
||||
const needsFFmpeg = transcodingDebugMode
|
||||
|| !ffmpegInput.url.startsWith('tcp://')
|
||||
|| !!ffmpegInput.h264EncoderArguments?.length
|
||||
|| !!ffmpegInput.h264FilterArguments?.length
|
||||
|| ffmpegInput.container !== 'mp4'
|
||||
|| noAudio;
|
||||
|
||||
if (transcodingDebugMode)
|
||||
transcodingDebugModeWarning();
|
||||
|
||||
let session: FFmpegFragmentedMP4Session & { socket?: Duplex };
|
||||
|
||||
if (ffmpegInput.container === 'mp4' && ffmpegInput.url.startsWith('tcp://') && !incompatibleStream) {
|
||||
if (!needsFFmpeg) {
|
||||
console.log('prebuffer is tcp/mp4/h264/aac compatible. using direct tcp.');
|
||||
const socketUrl = new URL(ffmpegInput.url);
|
||||
const socket = net.connect(parseInt(socketUrl.port), socketUrl.hostname);
|
||||
@@ -218,6 +223,10 @@ export async function* handleFragmentsRequests(connection: DataStreamConnection,
|
||||
];
|
||||
}
|
||||
|
||||
if (ffmpegInput.h264FilterArguments?.length) {
|
||||
videoArgs.push(...ffmpegInput.h264FilterArguments);
|
||||
}
|
||||
|
||||
console.log(`motion recording starting`);
|
||||
session = await startFFMPegFragmentedMP4Session(inputArguments, audioArgs, videoArgs, console);
|
||||
}
|
||||
|
||||
@@ -194,26 +194,40 @@ export function createCameraStreamingDelegate(device: ScryptedDevice & VideoCame
|
||||
|
||||
session.startRequest = request as StartStreamRequest;
|
||||
|
||||
const isHomeHub = homekitPlugin.storageSettings.values.lastKnownHomeHub?.includes(session.prepareRequest.targetAddress);
|
||||
// ios is seemingly forcing all connections through the home hub on ios 15.5. this is test code to force low bandwidth.
|
||||
// remote wifi connections request the same audio packet time as local wifi connections.
|
||||
// so there's no way to differentiate between remote and local wifi. with low bandwidth forcing off,
|
||||
// it will always select the local stream. with it on, it always selects the remote stream.
|
||||
if (isHomeHub)
|
||||
console.log('Streaming request is coming from the active HomeHub. Medium resolution stream will be selected in case this is a remote wifi connection or a wireless HomeHub. Using Accessory Mode is recommended.');
|
||||
let forceSlowConnection = false;
|
||||
try {
|
||||
for (const address of homekitPlugin.storageSettings.values.slowConnections) {
|
||||
if (address.includes(session.prepareRequest.targetAddress))
|
||||
forceSlowConnection = true;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
if (forceSlowConnection) {
|
||||
console.log('Streaming request is coming from a device in the slow mode connection list. Medium resolution stream will be selected.');
|
||||
}
|
||||
else {
|
||||
// ios is seemingly forcing all connections through the home hub on ios 15.5. this is test code to force low bandwidth.
|
||||
// remote wifi connections request the same audio packet time as local wifi connections.
|
||||
// so there's no way to differentiate between remote and local wifi. with low bandwidth forcing off,
|
||||
// it will always select the local stream. with it on, it always selects the remote stream.
|
||||
forceSlowConnection = homekitPlugin.storageSettings.values.slowConnections?.includes(session.prepareRequest.targetAddress);
|
||||
if (forceSlowConnection)
|
||||
console.log('Streaming request is coming from the active HomeHub. Medium resolution stream will be selected in case this is a remote wifi connection or a wireless HomeHub. Using Accessory Mode is recommended if not already in use.');
|
||||
}
|
||||
|
||||
const {
|
||||
destination,
|
||||
dynamicBitrate,
|
||||
isLowBandwidth,
|
||||
isWatch,
|
||||
} = await getStreamingConfiguration(device, isHomeHub, storage, request)
|
||||
} = await getStreamingConfiguration(device, forceSlowConnection, storage, request)
|
||||
|
||||
const hasHomeHub = !!homekitPlugin.storageSettings.values.lastKnownHomeHub;
|
||||
const waitRtcp = isHomeHub || isLowBandwidth || !hasHomeHub;
|
||||
const waitRtcp = forceSlowConnection || isLowBandwidth || !hasHomeHub;
|
||||
if (waitRtcp) {
|
||||
console.log('Will wait for initial RTCP packet.', {
|
||||
isHomeHub,
|
||||
isHomeHub: forceSlowConnection,
|
||||
isLowBandwidth,
|
||||
hasHomeHub,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user