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
129 lines • 8.29 kB
JavaScript
import { HikVisionCamera } from './HikVisionCamera.js';
import { HikvisionApi } from './HikvisionApi.js';
import { HIKVISION_PLUGIN_NAME } from './index.js';
import { Log } from './lib/logger.js';
export class HikVisionNVR {
constructor(log, config, api) {
this.log = new Log(log, config.debug);
this.homebridgeApi = api;
this.hikVisionApi = new HikvisionApi(config, this.log);
this.config = config;
this.cameras = [];
this.log.info('Initialising accessories for HikVision...');
this.homebridgeApi.on('didFinishLaunching', this.loadAccessories.bind(this));
setInterval(this.loadAccessories.bind(this), (this.config.refresh ? this.config.refresh * 60 * 60 * 1000 : 12 * 60 * 60 * 1000));
}
async loadAccessories() {
this.log.info(`Connecting to NVR system @ ${this.hikVisionApi._baseURL}`);
const systemInformation = await this.hikVisionApi.getSystemInfo();
if (this.hikVisionApi.connected) {
this.log.info(`Connected to NVR system: @ ${this.hikVisionApi._baseURL} -> '${systemInformation.DeviceInfo.deviceName}' ${systemInformation.DeviceInfo.model}`);
this.log.info('Loading cameras...');
const apiCameras = await this.hikVisionApi.getCameras();
this.log.debug(`Found cameras: ${JSON.stringify(apiCameras, null, 2)}`);
apiCameras.map((channel) => {
if (!channel.capabilities.StreamingChannel) {
this.log.error(`Failed to connect to NVR system, incomplete config @ ${this.hikVisionApi._baseURL}`);
return;
}
if (this.config.maxWidth || this.config.maxWidth) {
// eslint-disable-next-line max-len
this.log.debug(`Overriding supplied camera config ${channel.name} - ${channel.capabilities.StreamingChannel.Video?.videoResolutionWidth?.['#text']}x${channel.capabilities.StreamingChannel.Video?.videoResolutionHeight?.['#text']} with ${this.config.maxWidth}x${this.config.maxHeight}`);
}
const cameraConfig = {
accessory: 'camera',
name: (this.config.test ? 'Test ' : '') + channel.name,
channelId: channel.id,
hasAudio: channel.capabilities.StreamingChannel.Audio ? String(channel.capabilities.StreamingChannel.Audio.enabled['#text']) === 'true' : false,
doorbell: (this.config?.doorbells ? this.config?.doorbells.includes(channel.name) : false),
model: channel.sourceInputPortDescriptor?.model,
maxFPS: channel.capabilities.StreamingChannel.Video?.maxFrameRate?.['#text'] / 100,
maxBitrate: channel.capabilities.StreamingChannel.Video?.vbrUpperCap?.['#text'],
maxWidth: (this.config.maxWidth ? this.config.maxWidth : channel.capabilities.StreamingChannel.Video?.videoResolutionWidth?.['#text']),
maxHeight: (this.config.maxHeight ? this.config.maxHeight : channel.capabilities.StreamingChannel.Video?.videoResolutionHeight?.['#text']),
};
const cameraUUID = this.homebridgeApi.hap.uuid.generate((this.config.test ? 'Test ' : '') + HIKVISION_PLUGIN_NAME + systemInformation.DeviceInfo.deviceID + cameraConfig.channelId);
let accessoryType = 17 /* this.homebridgeApi.hap.Accessory.Categories.CAMERA */;
if (cameraConfig.doorbell) {
accessoryType = 18 /* this.homebridgeApi.hap.Accessory.Categories.VIDEO_DOORBELL */;
}
const accessory = new this.homebridgeApi.platformAccessory(cameraConfig.name, cameraUUID, accessoryType);
accessory.context = cameraConfig;
// Only add new cameras that are not cached
if (!this.cameras.find((x) => x.UUID === accessory.UUID)) {
this.configureAccessory(accessory); // abusing the configureAccessory here
this.homebridgeApi.publishExternalAccessories(HIKVISION_PLUGIN_NAME, [accessory]);
}
return accessory;
});
this.log.info('Registering cameras with homebridge');
this.startMonitoring();
}
else {
this.log.error(`Failed to connect to NVR system @ ${this.hikVisionApi._baseURL}`);
}
// var camerasToRemove: any[] = [];
// // Remove cameras that were not in previous call
// this.cameras.forEach((camera: any) => {
// if (!newAccessories.find((x: PlatformAccessory) => x.UUID === camera.UUID)) {
// this.log(`Unregistering missing camera: ${camera.UUID}`)
// camerasToRemove.push(camera.accessory);
// }
// });
// this.homebridgeApi.unregisterPlatformAccessories(HIKVISION_PLUGIN_NAME, HIKVISION_PLATFORM_NAME, camerasToRemove);
}
async configureAccessory(accessory) {
accessory.context = Object.assign(accessory.context, this.config);
const camera = new HikVisionCamera(this.log, this.homebridgeApi, accessory, this.config);
const cameraAccessoryInfo = camera.getService(this.homebridgeApi.hap.Service.AccessoryInformation);
cameraAccessoryInfo.setCharacteristic(this.homebridgeApi.hap.Characteristic.Manufacturer, 'HikVision');
cameraAccessoryInfo.setCharacteristic(this.homebridgeApi.hap.Characteristic.Model, accessory.context.model);
// cameraAccessoryInfo!.setCharacteristic(this.homebridgeApi.hap.Characteristic.SerialNumber, systemInformation.DeviceInfo.serialNumber);
// cameraAccessoryInfo!.setCharacteristic(this.homebridgeApi.hap.Characteristic.FirmwareRevision, systemInformation.DeviceInfo.firmwareVersion);
this.cameras.push(camera);
}
processHikVisionEvent(event) {
try {
switch (event.EventNotificationAlert.eventType) {
case 'videoloss':
this.log.debug('videoloss, nothing to do...');
break;
case 'fielddetection':
case 'linedetection':
case 'shelteralarm':
case 'VMD':
const motionDetected = event.EventNotificationAlert.eventState === 'active';
const channelId = (event.EventNotificationAlert.channelID ? event.EventNotificationAlert.channelID : event.EventNotificationAlert.dynChannelID);
const camera = this.cameras.find((data) => data.accessory.context.channelId === channelId);
if (!camera) {
return this.log.warn(`Could not find camera for event ${event}`);
}
this.log.info(`Motion detected on camera, triggering motion for ${camera.displayName}`);
if (motionDetected !== camera.motionDetected) {
camera.motionDetected = motionDetected;
const motionService = camera.getService(this.homebridgeApi.hap.Service.MotionSensor);
motionService?.setCharacteristic(this.homebridgeApi.hap.Characteristic.MotionDetected, motionDetected);
setTimeout(() => {
this.log.debug(`Disabling motion detection on camera ${camera.displayName}`);
camera.motionDetected = !motionDetected;
camera
.getService(this.homebridgeApi.hap.Service.MotionSensor)
?.setCharacteristic(this.homebridgeApi.hap.Characteristic.MotionDetected, !motionDetected);
}, 10000);
}
break;
default:
this.log.debug(`event ${JSON.stringify(event)}`);
}
}
catch (err) {
this.log.error(`Error processing HikVision event: ${err instanceof Error ? err.message : String(err)}`);
}
}
startMonitoring() {
if (this.hikVisionApi.connected) {
this.hikVisionApi.startMonitoringEvents(this.processHikVisionEvent.bind(this));
}
}
}
//# sourceMappingURL=HikVisionNVR.js.map