UNPKG

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

224 lines 10.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HikvisionApi = void 0; // require('axios-debug-log'); const https_1 = __importDefault(require("https")); const ts_axios_digest_auth_1 = require("@lukesthl/ts-axios-digest-auth"); const axios_1 = __importDefault(require("axios")); const xml2js_1 = __importStar(require("xml2js")); class HikvisionApi { constructor(config, log) { this.connected = false; this._baseURL = `http${config.secure ? 's' : ''}://${config.host}`; this._http = new ts_axios_digest_auth_1.AxiosDigestAuth({ username: config.username, password: config.password, axios: axios_1.default.create({ httpsAgent: new https_1.default.Agent({ rejectUnauthorized: !config.ignoreInsecureTls, }), timeout: 8000, }), }); this._httpStream = new ts_axios_digest_auth_1.AxiosDigestAuth({ username: config.username, password: config.password, axios: axios_1.default.create({ httpsAgent: new https_1.default.Agent({ rejectUnauthorized: !config.ignoreInsecureTls, }), }), }); this._parser = new xml2js_1.Parser({ explicitArray: false }); this.log = log; } /* "DeviceInfo": { "$": { "version": "2.0", "xmlns": "http://www.isapi.org/ver20/XMLSchema" }, "deviceName": "Network Video Recorder", "deviceID": "48443030-3637-3534-3837-f84dfcf8ef1c", "model": "DS-7608NI-I2/8P", "serialNumber": "DS-7608NI-I2/8P0820190316CCRRD00675487WCVU", "macAddress": "f8:4d:fc:f8:ef:1c", "firmwareVersion": "V4.22.005", "firmwareReleasedDate": "build 191208", "encoderVersion": "V5.0", "encoderReleasedDate": "build 191208", "deviceType": "NVR", "telecontrolID": "255" } */ getSystemInfo() { return __awaiter(this, void 0, void 0, function* () { return this._getResponse('/ISAPI/System/deviceInfo'); }); } getCameras() { return __awaiter(this, void 0, void 0, function* () { const channels = yield this._getResponse('/ISAPI/System/Video/inputs/channels'); if (channels.VideoInputChannelList) { for (let i = 0; i < channels.VideoInputChannelList.VideoInputChannel.length; i++) { const channel = channels.VideoInputChannelList.VideoInputChannel[i]; if (channel.resDesc !== 'NO VIDEO') { channel.capabilities = yield this._getResponse(`/ISAPI/ContentMgmt/StreamingProxy/channels/${channel.id}01/capabilities`); } channel.status = { online: channel.resDesc !== 'NO VIDEO' }; } return channels.VideoInputChannelList.VideoInputChannel.filter((camera) => camera.status.online); } else { const channels2 = yield this._getResponse('/ISAPI/ContentMgmt/InputProxy/channels'); for (let i = 0; i < channels2.InputProxyChannelList.InputProxyChannel.length; i++) { const channel = channels2.InputProxyChannelList.InputProxyChannel[i]; if (channel.resDesc !== 'NO VIDEO') { channel.capabilities = yield this._getResponse(`/ISAPI/ContentMgmt/StreamingProxy/channels/${channel.id}01/capabilities`); } channel.status = { online: channel.resDesc !== 'NO VIDEO' }; } return channels2.InputProxyChannelList.InputProxyChannel.filter((camera) => camera.status.online); } }); } startMonitoringEvents(callback) { return __awaiter(this, void 0, void 0, function* () { this.log.info('Starting event monitoring...'); const url = '/ISAPI/Event/notification/alertStream'; const xmlParser = new xml2js_1.default.Parser({ explicitArray: false, }); const startStream = () => __awaiter(this, void 0, void 0, function* () { try { const response = yield this.getStream(url, { responseType: 'stream', headers: { 'Content-Type': 'multipart/form-data' }, }); this.log.info(`Event Monitoring Connection Status ${response === null || response === void 0 ? void 0 : response.status} -> ${response === null || response === void 0 ? void 0 : response.statusText}`); if ((response === null || response === void 0 ? void 0 : response.status) !== 200) { throw new Error(`Failed to start stream, retrying... ${response === null || response === void 0 ? void 0 : response.status} -> ${response === null || response === void 0 ? void 0 : response.statusText}`); } else { const stream = response === null || response === void 0 ? void 0 : response.data; // eslint-disable-next-line @typescript-eslint/no-use-before-define handleStream(stream); } } catch (error) { this.log.error(`Failed to start stream, retrying... ${error}`); setTimeout(startStream, 5000); // Retry after 5 seconds } }); const handleStream = (stream) => __awaiter(this, void 0, void 0, function* () { stream.on('error', (error) => { this.log.error(`Stream error, restarting connection...${error}`); startStream(); }); // stream.on('end', () => { // this.log.info('Stream ended, restarting connection...'); // startStream(); // }); stream.on('close', () => { this.log.info('Stream closed, restarting connection...'); startStream(); }); stream.on('finish', () => { this.log.info('Stream finished, restarting connection...'); startStream(); }); stream.on('pause', () => { this.log.debug('stream PAUSE'); }); stream.on('resume', () => { this.log.debug('stream RESUME'); }); stream.on('unpipe', () => { this.log.debug('stream UNPIPE'); }); stream.on('data', (data) => __awaiter(this, void 0, void 0, function* () { data = data.toString(); // console.log('DATA', data); if (data.includes('<EventNotificationAlert')) { const message = data.slice(data.indexOf('<EventNotificationAlert')); const eventMsg = yield xmlParser.parseStringPromise(message); callback(eventMsg); } })); }); startStream(); }); } getStream(url, config) { return __awaiter(this, void 0, void 0, function* () { try { // this.log.debug('GET', this._baseURL + url, config); return yield this._httpStream.get(this._baseURL + url, config); } catch (e) { this.log.error(`ERROR: getStream ${this._baseURL + url} -> ${config} ${e}`); } }); } _getResponse(path) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; try { // this.log.debug(`_getResponse ${this._baseURL + path}`); const response = yield ((_a = this._http) === null || _a === void 0 ? void 0 : _a.get(this._baseURL + path, { validateStatus: function (status) { if (status !== 401) { return true; // Resolve only if the status code is less than 500 } else { return false; } }, })); const responseJson = yield ((_b = this._parser) === null || _b === void 0 ? void 0 : _b.parseStringPromise(response === null || response === void 0 ? void 0 : response.data)); this.connected = true; return responseJson; } catch (e) { this.log.error(`ERROR: _getResponse ${this._baseURL + path} -> ${e.message}`); } }); } } exports.HikvisionApi = HikvisionApi; //# sourceMappingURL=HikvisionApi.js.map