homebridge-hikvision-local
Version:
Homebridge plugin that connects to your HikVision DVR via a local connection and exposes your cameras in Homebridge. The plugin is heavily based on excellent [homebridge-camera-ffmpeg](https://github.com/Sunoo/homebridge-camera-ffmpeg) and the various hom
141 lines • 7.46 kB
JavaScript
// We borrow, rather cheekly from the homebridge-camera-ffmpeg plugin.
// TODO: probably rethink and do something like https://github.com/homebridge/homebridge-examples/tree/master/bridged-camera-example-typescript.
import { Logger } from '@homebridge-plugins/homebridge-camera-ffmpeg/dist/logger.js';
import { StreamingDelegate } from '@homebridge-plugins/homebridge-camera-ffmpeg/dist/streamingDelegate.js';
export class HikVisionCamera {
constructor(log, homebridgeApi, accessory, config) {
this.motionDetected = false;
this.log = log;
this.homebridgeApi = homebridgeApi;
this.accessory = accessory;
this.displayName = this.accessory.displayName;
this.UUID = accessory.UUID;
this.config = config;
this.configure(this.accessory);
}
getService(...args) {
return this.accessory.getService(...args);
}
configureController(...args) {
return this.accessory.configureController(...args);
}
addService(...args) {
return this.accessory.addService(...args);
}
removeService(...args) {
return this.accessory.removeService(...args);
}
on(...args) {
this.accessory.on(...args);
}
getServiceByUUIDAndSubType(uuid, subType) {
return undefined;
}
configure(accessory) {
this.log.info(
// eslint-disable-next-line max-len
`Configuring ${(this.config.doorbells && this.config.doorbells.includes(accessory.displayName) ? 'doorbell' : 'camera')} accessory: ${accessory.displayName} - ${JSON.stringify(accessory.context)}`);
accessory.on('identify', () => {
this.log(`${accessory.displayName} identified!`);
});
let motionSensor = accessory.getService(this.homebridgeApi.hap.Service.MotionSensor);
if (motionSensor) {
this.log.info('Re-creating motion sensor');
accessory.removeService(motionSensor);
}
else {
// this.log.warn('There was no motion sensor set up!');
}
motionSensor = new this.homebridgeApi.hap.Service.MotionSensor(accessory.displayName);
accessory.addService(motionSensor);
if (this.config.doorbells && this.config.doorbells.includes(accessory.displayName)) {
this.log.info(`Creating Doorbell Trigger: ${accessory.displayName} Doorbell Trigger`);
const doorbellService = new this.homebridgeApi.hap.Service.Doorbell(accessory.displayName + ' Doorbell');
accessory.addService(doorbellService);
const switchService = new this.homebridgeApi.hap.Service.Switch(accessory.displayName + ' Doorbell Trigger', 'DoorbellTrigger');
switchService.getCharacteristic(this.homebridgeApi.hap.Characteristic.On)
.on('set', (state, callback) => {
this.log.info(`Doorbell trigger for ${accessory.displayName} - ${state}`);
if (state) {
doorbellService.getCharacteristic(this.homebridgeApi.hap.Characteristic.ProgrammableSwitchEvent).setValue(this.homebridgeApi.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
setTimeout(() => {
switchService.getCharacteristic(this.homebridgeApi.hap.Characteristic.On).updateValue(false);
}, 5000);
}
callback(null);
});
accessory.addService(switchService);
}
// doorbell.updateCharacteristic(hap.Characteristic.ProgrammableSwitchEvent, hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
const channelId = accessory.context.channelId;
const cameraConfig = {
name: accessory.displayName,
videoConfig: {
source: `-rtsp_transport tcp -i rtsp://${accessory.context.username}:${accessory.context.password}@${accessory.context.host}/Streaming/Channels/${channelId}01`,
stillImageSource: `-i http${accessory.context.secure ? 's' : ''}://${accessory.context.username}:${accessory.context.password}@${accessory.context.host}/ISAPI/Streaming/channels/${channelId}01/picture?videoResolutionWidth=720`,
maxFPS: (accessory.context.maxFPS ? accessory.context.maxFPS : 30),
maxBitrate: (accessory.context.maxBitrate ? accessory.context.maxBitrate : 16384),
maxWidth: (accessory.context.maxWidth ? accessory.context.maxWidth : 1920),
maxHeight: (accessory.context.maxHeight ? accessory.context.maxHeight : 1080),
vcodec: 'libx264',
audio: accessory.context.hasAudio,
debug: Boolean(accessory.context.debugFfmpeg),
},
};
const cameraLogger = new Logger(this.log);
// Use the homebridge-camera-ffmpeg StreamingDelegate.
const streamingDelegate = new StreamingDelegate(cameraLogger, cameraConfig, this.homebridgeApi, this.homebridgeApi.hap, accessory);
const cameraControllerOptions = {
cameraStreamCount: 5, // HomeKit requires at least 2 streams, but 1 is also just fine
delegate: streamingDelegate,
streamingOptions: {
supportedCryptoSuites: [
0 /* this.homebridgeApi.hap.SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80 */,
],
video: {
resolutions: [
// TODO: put in the max framerates & resolutions from the camera config.
[320, 180, 30],
[320, 240, 15], // Apple Watch requires this configuration
[320, 240, 30],
[480, 270, 30],
[480, 360, 30],
[640, 360, 30],
[640, 480, 30],
[1280, 720, 30],
[1280, 960, 30],
[1920, 1080, 30],
[1600, 1200, 30],
],
codec: {
profiles: [
0 /* this.homebridgeApi.hap.H264Profile.BASELINE */,
1 /* this.homebridgeApi.hap.H264Profile.MAIN */,
2 /* this.homebridgeApi.hap.H264Profile.HIGH */,
],
levels: [
0 /* this.homebridgeApi.hap.H264Level.LEVEL3_1 */,
1 /* this.homebridgeApi.hap.H264Level.LEVEL3_2 */,
2 /* this.homebridgeApi.hap.H264Level.LEVEL4_0 */,
],
},
},
audio: {
codecs: [
{
type: "OPUS" /* AudioStreamingCodecType.OPUS */,
samplerate: 24 /* AudioStreamingSamplerate.KHZ_24 */,
},
{
type: "AAC-eld" /* AudioStreamingCodecType.AAC_ELD */,
samplerate: 16 /* AudioStreamingSamplerate.KHZ_16 */,
},
],
},
},
};
const cameraController = new this.homebridgeApi.hap.CameraController(cameraControllerOptions, true);
accessory.configureController(cameraController);
}
}
//# sourceMappingURL=HikVisionCamera.js.map