From 3d928eb032ea52124a5860e727c381b7fc4bf0bb Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Fri, 6 Jan 2023 20:00:17 +0100 Subject: [PATCH 01/24] * Fix an issues in SIP.js where the ACK and BYE replies didn't go to the correct uri --- plugins/bticino/src/main.ts | 2 +- plugins/sip/src/main.ts | 2 +- plugins/sip/src/sip-call.ts | 61 +++++++++++++++++++--------------- plugins/sip/src/sip-session.ts | 1 - 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 5e5e0d5824..328570c99e 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -326,7 +326,7 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider } } - async releaseDevice(id: string, nativeId: string, device: any): Promise { + async releaseDevice(id: string, nativeId: string): Promise { } async createDevice(settings: DeviceCreatorSettings): Promise { diff --git a/plugins/sip/src/main.ts b/plugins/sip/src/main.ts index 1ab32a9869..703b021442 100644 --- a/plugins/sip/src/main.ts +++ b/plugins/sip/src/main.ts @@ -437,7 +437,7 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider } } - async releaseDevice(id: string, nativeId: string, device: any): Promise { + async releaseDevice(id: string, nativeId: string): Promise { } async createDevice(settings: DeviceCreatorSettings): Promise { diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index 3002b71a29..155525dcc8 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -3,6 +3,7 @@ import { randomInteger, randomString } from './util' import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' import { decodeSrtpOptions } from '@homebridge/camera-utils' import { stringify } from 'sip/sip' +import { timeoutPromise } from '@scrypted/common/src/promise-utils'; const sip = require('sip'), sdp = require('sdp') @@ -15,7 +16,7 @@ export interface SipOptions { localIp: string localPort: number debugSip?: boolean - messageHandler?: SipMessageHandler + messageHandler?: SipMessageHandler shouldRegister?: boolean } @@ -178,10 +179,14 @@ export class SipCall { ws: false, logger: { recv: function(m, remote) { + if( m.status == '200' && m.reason =='Ok' && m.headers.contact ) { + // ACK for INVITE and BYE must use the registrar contact uri + this.registrarContact = m.headers.contact[0].uri; + } if( sipOptions.debugSip ) { console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") console.log(stringify( m )); - console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") } }, send: function(m, remote) { @@ -195,30 +200,31 @@ export class SipCall { // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() - if( m.method == 'REGISTER' || m.method == 'INVITE' ) { + if( m.method == 'REGISTER' ) { m.uri = "sip:" + sipOptions.domain } else if( m.method == 'INVITE' ) { m.uri = toWithDomain + } else if( m.method == 'ACK' || m.method == 'BYE' ) { + m.uri = this.registrarContact } else { throw new Error("Error: Method construct for uri not implemented: " + m.method) } - + m.headers.to.uri = toWithDomain m.headers.from.uri = fromWithDomain - if( m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { + if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); } - } - } + if( sipOptions.debugSip ) { console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); console.log(stringify( m )); - console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } - }, - }, + }, + }, }, (request: SipRequest) => { if (request.method === 'BYE') { @@ -368,24 +374,27 @@ export class SipCall { /** * Register the user agent with a Registrar - */ + */ async register() { - const { from } = this.sipOptions, - inviteResponse = await this.request({ - method: 'REGISTER', - headers: { - //supported: 'replaces, outbound', - allow: - 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'application/sdp', - contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], - }, - }); + const { from } = this.sipOptions; + await timeoutPromise( 500, + this.request({ + method: 'REGISTER', + headers: { + //supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + 'content-type': 'application/sdp', + contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], + }, + }).catch(() => { + // Don't care if we get an exception here. + })); } /** * Send a message to the current call contact - */ + */ async message( content: string ) { const { from } = this.sipOptions, inviteResponse = await this.request({ @@ -399,13 +408,13 @@ export class SipCall { }, content: content }); - } + } async sendBye() { this.console.log('Sending BYE...') - return this.request({ method: 'BYE' }).catch(() => { + return await timeoutPromise( 3000, this.request({ method: 'BYE' }).catch(() => { // Don't care if we get an exception here. - }) + })); } destroy() { diff --git a/plugins/sip/src/sip-session.ts b/plugins/sip/src/sip-session.ts index 9f7c110189..a2a8cb43af 100644 --- a/plugins/sip/src/sip-session.ts +++ b/plugins/sip/src/sip-session.ts @@ -149,7 +149,6 @@ export class SipSession extends Subscribed { return rtpDescription } catch (e) { - this.callEnded(true) throw e } From db45f0d9b2b3d13087aa681ff33eab7a470ebeb6 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Mon, 16 Jan 2023 19:38:03 +0100 Subject: [PATCH 02/24] * Implemented outgoing SIP MESSAGE sending * Adding voice mail check * Adding a lock for a bticino doorbell --- plugins/bticino/package-lock.json | 434 ++++++++---------- plugins/bticino/package.json | 3 +- plugins/bticino/src/bticino-camera.ts | 294 ++++++++++++ plugins/bticino/src/bticino-lock.ts | 28 ++ .../bticino/src/bticino-voicemailHandler.ts | 61 +++ plugins/bticino/src/main.ts | 409 +++-------------- plugins/bticino/src/sip-helper.ts | 43 ++ plugins/bticino/src/storage-settings.ts | 60 +++ plugins/sip/package-lock.json | 430 +++++++---------- plugins/sip/src/compositeSipMessageHandler.ts | 14 + plugins/sip/src/sip-call.ts | 32 +- plugins/sip/src/sip-session.ts | 4 +- 12 files changed, 942 insertions(+), 870 deletions(-) create mode 100644 plugins/bticino/src/bticino-camera.ts create mode 100644 plugins/bticino/src/bticino-lock.ts create mode 100644 plugins/bticino/src/bticino-voicemailHandler.ts create mode 100644 plugins/bticino/src/sip-helper.ts create mode 100644 plugins/bticino/src/storage-settings.ts create mode 100644 plugins/sip/src/compositeSipMessageHandler.ts diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 847b37f50a..3167d64865 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,15 +1,15 @@ { "name": "@scrypted/bticino", - "version": "0.0.5", + "version": "0.0.6-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.5", + "version": "0.0.6-beta.1", "dependencies": { "@homebridge/camera-utils": "^2.0.4", - "rxjs": "^7.5.5", + "rxjs": "^7.5.6", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", @@ -39,97 +39,25 @@ "@types/node": "^16.9.0" } }, - "../../external/ring-client-api": { - "version": "11.0.4", - "extraneous": true, - "funding": [ - { - "type": "paypal", - "url": "https://www.paypal.me/dustingreif" - }, - { - "type": "github", - "url": "https://github.com/sponsors/dgreif" - } - ], - "license": "MIT", - "workspaces": [ - "homebridge-ui" - ], - "dependencies": { - "@eneris/push-receiver": "../push-receiver", - "@homebridge/camera-utils": "^2.1.1", - "@homebridge/plugin-ui-utils": "^0.0.19", - "@types/socket.io-client": "1.4.36", - "colors": "^1.4.0", - "debug": "^4.3.4", - "got": "^11.8.3", - "rxjs": "^7.5.5", - "sdp": "^3.0.3", - "socket.io-client": "^2.4.0", - "stun": "^2.1.0", - "systeminformation": "^5.11.15", - "uuid": "^8.3.2", - "werift": "0.15.4", - "ws": "^8.7.0" - }, - "bin": { - "ring-auth-cli": "ring-auth-cli.js", - "ring-device-data-cli": "ring-device-data-cli.js" - }, - "devDependencies": { - "@swc-node/register": "^1.5.1", - "@types/debug": "4.1.7", - "@types/jest": "27.5.1", - "@types/node": "17.0.36", - "@types/uuid": "8.3.4", - "@types/ws": "^8.5.3", - "@typescript-eslint/eslint-plugin": "5.26.0", - "@typescript-eslint/parser": "5.26.0", - "concurrently": "^7.2.1", - "conventional-github-releaser": "3.1.5", - "dotenv": "16.0.1", - "esbuild": "^0.14.42", - "eslint": "8.16.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-jest": "26.4.2", - "eslint-plugin-prettier": "4.0.0", - "express": "4.18.1", - "homebridge": "1.4.1", - "jest": "28.1.0", - "prettier": "2.6.2", - "reflect-metadata": "0.1.13", - "rimraf": "3.0.2", - "standard-version": "9.5.0", - "ts-jest": "28.0.3", - "typescript": "4.7.2" - }, - "engines": { - "homebridge": ">=1.4.0", - "node": "^16" - } - }, - "../../external/ring-client/api": { - "extraneous": true - }, "../../sdk": { "name": "@scrypted/sdk", - "version": "0.2.39", + "version": "0.2.55", "dev": true, "license": "ISC", "dependencies": { - "@babel/preset-typescript": "^7.16.7", + "@babel/preset-typescript": "^7.18.6", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", "tmp": "^0.2.1", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" }, "bin": { @@ -142,16 +70,13 @@ "scrypted-webpack": "bin/scrypted-webpack.js" }, "devDependencies": { - "@types/node": "^18.11.9", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "stringify-object": "^3.3.0", "ts-node": "^10.4.0", "typedoc": "^0.23.21" } }, - "../sdk": { - "extraneous": true - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -226,9 +151,9 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, "node_modules/@types/node": { - "version": "16.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", - "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==" + "version": "16.18.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==" }, "node_modules/@types/uuid": { "version": "8.3.4", @@ -334,14 +259,6 @@ "node": ">=4" } }, - "node_modules/camelcase-keys/node_modules/quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", - "engines": { - "node": ">=4" - } - }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -379,6 +296,22 @@ "node": ">=0.10.0" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -432,17 +365,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -539,6 +461,17 @@ "node": ">= 8" } }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -810,6 +743,17 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -823,9 +767,9 @@ } }, "node_modules/minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", "dependencies": { "yallist": "^4.0.0" }, @@ -845,6 +789,17 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -856,6 +811,11 @@ "node": ">=10" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -900,7 +860,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -1023,27 +983,6 @@ "debug": "^4.3.1" } }, - "node_modules/pick-port/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/pick-port/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -1088,6 +1027,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", + "engines": { + "node": ">=4" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1142,9 +1089,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dependencies": { "tslib": "^2.1.0" } @@ -1272,14 +1219,6 @@ "node": ">=0.2.2" } }, - "node_modules/sip/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1371,27 +1310,6 @@ "node": ">=8.3" } }, - "node_modules/stun/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/stun/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1404,9 +1322,9 @@ } }, "node_modules/systeminformation": { - "version": "5.12.15", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.12.15.tgz", - "integrity": "sha512-LMctTV27bGqWMBsuhzvNTH3roKOQonTN730F9v0x9YtoYducXcobs0rg3QKNnWDyHJyWIgKY6FiHlFcXJYclTQ==", + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", + "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==", "os": [ "darwin", "linux", @@ -1429,13 +1347,13 @@ } }, "node_modules/tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -1508,9 +1426,9 @@ } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "peer": true, "bin": { "tsc": "bin/tsc", @@ -1567,7 +1485,15 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } }, "node_modules/yallist": { "version": "4.0.0", @@ -1645,12 +1571,12 @@ "@scrypted/sdk": { "version": "file:../../sdk", "requires": { - "@babel/preset-typescript": "^7.16.7", - "@types/node": "^18.11.9", + "@babel/preset-typescript": "^7.18.6", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", @@ -1658,10 +1584,11 @@ "rimraf": "^3.0.2", "stringify-object": "^3.3.0", "tmp": "^0.2.1", + "ts-loader": "^9.4.2", "ts-node": "^10.4.0", "typedoc": "^0.23.21", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" } }, @@ -1686,9 +1613,9 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" }, "@types/node": { - "version": "16.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", - "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==" + "version": "16.18.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==" }, "@types/uuid": { "version": "8.3.4", @@ -1765,13 +1692,6 @@ "camelcase": "^4.1.0", "map-obj": "^2.0.0", "quick-lru": "^1.0.0" - }, - "dependencies": { - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" - } } }, "chownr": { @@ -1802,6 +1722,14 @@ "array-find-index": "^1.0.1" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1834,13 +1762,6 @@ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "requires": { "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } } }, "detect-libc": { @@ -1912,6 +1833,16 @@ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "function-bind": { @@ -2121,6 +2052,11 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, "minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -2131,9 +2067,9 @@ } }, "minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", "requires": { "yallist": "^4.0.0" } @@ -2145,6 +2081,16 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "mkdirp": { @@ -2152,6 +2098,11 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -2184,7 +2135,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -2278,21 +2229,6 @@ "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", "requires": { "debug": "^4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "pify": { @@ -2324,6 +2260,11 @@ "strict-uri-encode": "^2.0.0" } }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -2363,9 +2304,9 @@ } }, "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "requires": { "tslib": "^2.1.0" } @@ -2434,16 +2375,6 @@ "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", "requires": { "ws": "^6.1.0" - }, - "dependencies": { - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "requires": { - "async-limiter": "~1.0.0" - } - } } }, "spdx-correct": { @@ -2514,21 +2445,6 @@ "parse-url": "^5.0.1", "turbo-crc32": "^1.0.0", "universalify": "^0.1.2" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "supports-preserve-symlinks-flag": { @@ -2537,18 +2453,18 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "systeminformation": { - "version": "5.12.15", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.12.15.tgz", - "integrity": "sha512-LMctTV27bGqWMBsuhzvNTH3roKOQonTN730F9v0x9YtoYducXcobs0rg3QKNnWDyHJyWIgKY6FiHlFcXJYclTQ==" + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", + "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==" }, "tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -2590,9 +2506,9 @@ "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "peer": true }, "universalify": { @@ -2630,7 +2546,15 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } }, "yallist": { "version": "4.0.0", diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index b8c6b5edef..e553bb03c8 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.5", + "version": "0.0.6-beta.1", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", @@ -34,7 +34,6 @@ }, "dependencies": { "@homebridge/camera-utils": "^2.0.4", - "rxjs": "^7.5.5", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts new file mode 100644 index 0000000000..0e5dd4cfba --- /dev/null +++ b/plugins/bticino/src/bticino-camera.ts @@ -0,0 +1,294 @@ +import { listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; +import { RtspServer } from '@scrypted/common/src/rtsp-server'; +import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; +import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import { SipSession } from '../../sip/src/sip-session'; +import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType } from '../../sip/src/rtp-utils'; +import { VoicemailHandler } from './bticino-voicemailHandler'; +import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; +import { sleep } from '@scrypted/common/src/sleep'; +import { SipHelper } from './sip-helper'; +import { BticinoStorageSettings } from './storage-settings'; +import { BticinoSipPlugin } from './main'; +import { BticinoSipLock } from './bticino-lock'; + +const STREAM_TIMEOUT = 50000; + +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { + session: SipSession + currentMedia: FFmpegInput | MediaStreamUrl + currentMediaMimeType: string + refreshTimeout: NodeJS.Timeout + messageHandler: CompositeSipMessageHandler + settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) + + constructor(nativeId: string, public provider: BticinoSipPlugin) { + super(nativeId) + this.messageHandler = new CompositeSipMessageHandler() + this.messageHandler.add( new VoicemailHandler( this ) ) + } + + sipUnlock(): Promise { + this.log.i("unlocking C300X door ") + return SipHelper.sipSession( this ) + .then( ( sip ) => { + sip.sipCall.register() + .then( () => + sip.sipCall.message( '*8*19*20##' ) + .then( () => + sleep(1000) + .then( () => sip.sipCall.message( '*8*20*20##' ) ) + ) + .catch( () => {} ) + .finally( () => sip.sipCall.destroy() ) + ) + .catch( e => this.console.error() ) + } ) + .catch( e => this.console.error(e) ) + } + + getAswmStatus() : Promise { + return SipHelper.sipSession( this ) + .then( ( sip ) => { + sip.sipCall.register() + .then( () => sip.sipCall.message( "GetAswmStatus!") ) + .catch( () => {}) + .finally( () => sip.sipCall.destroy() ) + } ) + .catch( e => this.console.error(e) ) + } + + async takePicture(option?: PictureOptions): Promise { + throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL."); + } + + async getPictureOptions(): Promise { + return; + } + + getSettings(): Promise { + return this.settingsStorage.getSettings(); + } + + putSetting(key: string, value: SettingValue): Promise { + return this.settingsStorage.putSetting(key, value); + } + + async startIntercom(media: MediaObject): Promise { + this.log.d( "TODO: startIntercom" + media ); + } + + async stopIntercom(): Promise { + this.log.d( "TODO: stopIntercom" ); + } + + resetStreamTimeout() { + this.log.d('starting/refreshing stream'); + clearTimeout(this.refreshTimeout); + this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT); + } + + stopSession() { + if (this.session) { + this.log.d('ending sip session'); + this.session.stop(); + this.session = undefined; + } + } + + async getVideoStream(options?: ResponseMediaStreamOptions): Promise { + if( !SipHelper.sipOptions( this ) ) { + // Bail out fast when no options are set and someone enables prebuffering + throw new Error('Please configure from/to/domain settings') + } + + if (options?.metadata?.refreshAt) { + if (!this.currentMedia?.mediaStreamOptions) + throw new Error("no stream to refresh"); + + const currentMedia = this.currentMedia; + currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT; + currentMedia.mediaStreamOptions.metadata = { + refreshAt: currentMedia.mediaStreamOptions.refreshAt + }; + this.resetStreamTimeout(); + return sdk.mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType); + } + + this.stopSession(); + + + const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient(); + + const playbackUrl = `rtsp://127.0.0.1:${playbackPort}`; + + playbackPromise.then(async (client) => { + client.setKeepAlive(true, 10000); + let sip: SipSession; + try { + let rtsp: RtspServer; + const cleanup = () => { + client.destroy(); + if (this.session === sip) + this.session = undefined; + try { + this.log.d('cleanup(): stopping sip session.'); + sip.stop(); + } + catch (e) { + } + rtsp?.destroy(); + } + + client.on('close', cleanup); + client.on('error', cleanup); + + sip = await SipHelper.sipSession( this ) + // Validate this sooner + if( !sip ) return Promise.reject("Cannot create session"); + + sip.onCallEnded.subscribe(cleanup); + + // Call the C300X + let remoteRtpDescription = await sip.call( + ( audio ) => { + return [ + 'a=DEVADDR:20', // Needed for bt_answering_machine (bticino specific) + `m=audio ${audio.port} RTP/SAVP 97`, + `a=rtpmap:97 speex/8000`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, + ] + }, ( video ) => { + return [ + `m=video ${video.port} RTP/SAVP 97`, + `a=rtpmap:97 H264/90000`, + `a=fmtp:97 profile-level-id=42801F`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, + 'a=recvonly' + ] + } ); + if( sip.sipOptions.debugSip ) + this.log.d('SIP: Received remote SDP:\n' + remoteRtpDescription.sdp) + + let sdp: string = replacePorts( remoteRtpDescription.sdp, 0, 0 ); + sdp = addTrackControls(sdp); + sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n'); + if( sip.sipOptions.debugSip ) + this.log.d('SIP: Updated SDP:\n' + sdp); + + let vseq = 0; + let vseen = 0; + let vlost = 0; + let aseq = 0; + let aseen = 0; + let alost = 0; + + rtsp = new RtspServer(client, sdp, true); + const parsedSdp = parseSdp(rtsp.sdp); + const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control; + const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control; + if( sip.sipOptions.debugSip ) { + rtsp.console = this.console; + } + + await rtsp.handlePlayback(); + sip.videoSplitter.on('message', message => { + if (!isStunMessage(message)) { + const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); + if (!isRtpMessage) + return; + vseen++; + rtsp.sendTrack(videoTrack, message, !isRtpMessage); + const seq = getSequenceNumber(message); + if (seq !== (vseq + 1) % 0x0FFFF) + vlost++; + vseq = seq; + } + }); + + sip.videoRtcpSplitter.on('message', message => { + rtsp.sendTrack(videoTrack, message, true); + }); + + sip.audioSplitter.on('message', message => { + if (!isStunMessage(message)) { + const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); + if (!isRtpMessage) + return; + aseen++; + rtsp.sendTrack(audioTrack, message, !isRtpMessage); + const seq = getSequenceNumber(message); + if (seq !== (aseq + 1) % 0x0FFFF) + alost++; + aseq = seq; + } + }); + + sip.audioRtcpSplitter.on('message', message => { + rtsp.sendTrack(audioTrack, message, true); + }); + + this.session = sip; + + try { + await rtsp.handleTeardown(); + this.log.d('rtsp client ended'); + } + catch (e) { + this.log.e('rtsp client ended ungracefully' + e); + } + finally { + cleanup(); + } + } + catch (e) { + sip?.stop(); + throw e; + } + }); + + this.resetStreamTimeout(); + + const mediaStreamOptions = Object.assign(this.getSipMediaStreamOptions(), { + refreshAt: Date.now() + STREAM_TIMEOUT, + }); + + const mediaStreamUrl: MediaStreamUrl = { + url: playbackUrl, + mediaStreamOptions, + }; + this.currentMedia = mediaStreamUrl; + this.currentMediaMimeType = ScryptedMimeTypes.MediaStreamUrl; + + return sdk.mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl); + } + + getSipMediaStreamOptions(): ResponseMediaStreamOptions { + return { + id: 'sip', + name: 'SIP', + // this stream is NOT scrypted blessed due to wackiness in the h264 stream. + // tool: "scrypted", + container: 'sdp', + audio: { + // this is a hint to let homekit, et al, know that it's speex audio and needs transcoding. + codec: 'speex', + }, + source: 'cloud', // to disable prebuffering + userConfigurable: false, + }; + } + + async getVideoStreamOptions(): Promise { + return [ + this.getSipMediaStreamOptions(), + ] + } + + async getDevice(nativeId: string) : Promise { + return new BticinoSipLock(this); + } + + async releaseDevice(id: string, nativeId: string): Promise { + } +} \ No newline at end of file diff --git a/plugins/bticino/src/bticino-lock.ts b/plugins/bticino/src/bticino-lock.ts new file mode 100644 index 0000000000..d4d9d6798d --- /dev/null +++ b/plugins/bticino/src/bticino-lock.ts @@ -0,0 +1,28 @@ +import sdk, { ScryptedDeviceBase, Lock, LockState, DeviceCreator, DeviceCreatorSettings, Setting } from "@scrypted/sdk"; +import { BticinoSipCamera } from "./bticino-camera"; + +export class BticinoSipLock extends ScryptedDeviceBase implements Lock { + private timeout : NodeJS.Timeout + + constructor(public camera: BticinoSipCamera) { + super( camera.nativeId + "-lock") + } + + lock(): Promise { + if( !this.timeout ) { + this.timeout = setTimeout(() => { + this.lockState = LockState.Locked + this.timeout = undefined + } , 3000); + } else { + this.console.log("Still attempting previous locking ...") + } + return + } + + unlock(): Promise { + this.lockState = LockState.Unlocked + this.lock() + return this.camera.sipUnlock() + } +} \ No newline at end of file diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts new file mode 100644 index 0000000000..0633736f88 --- /dev/null +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -0,0 +1,61 @@ +import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call"; +import { BticinoSipCamera } from "./bticino-camera"; + +export class VoicemailHandler extends SipMessageHandler { + private sipCamera : BticinoSipCamera + + constructor( sipCamera : BticinoSipCamera ) { + super() + this.sipCamera = sipCamera + this.checkVoicemail() + } + + checkVoicemail() { + if( !this.sipCamera ) + return + if( this.isEnabled() ) { + this.sipCamera.console.info("Checking answering machine.") + this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) ) + } else { + this.sipCamera.console.info("Answering machine check not enabled.") + } + //TODO: make interval customizable, now every 5 minutes + setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) + } + + handle(request: SipRequest) { + if( this.isEnabled() ) { + const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1 + const message : string = request.content.toString() + if( message.startsWith('*#8**40*0*0*1176*0*2##') ) { + this.sipCamera.console.debug("Handling incoming answering machine reply") + const messages : string[] = message.split(';') + let lastMessageTimestamp : number = 0 + let countNewMessages : number = 0 + messages.forEach( (message, index) => { + if( index > 0 ) { + const parts = message.split('|') + if( parts.length == 4 ) { + let messageTimestamp = Number.parseInt( parts[2] ) + if( messageTimestamp > lastVoicemailMessageTimestamp ) + countNewMessages++ + if( index == messages.length-2 ) + lastMessageTimestamp = messageTimestamp + } + } + } ) + if( (lastVoicemailMessageTimestamp == null && lastMessageTimestamp > 0) || + ( lastVoicemailMessageTimestamp != null && lastMessageTimestamp > lastVoicemailMessageTimestamp ) ) { + this.sipCamera.log.a(`You have ${countNewMessages} new voicemail messages.`) + this.sipCamera.storage.setItem('lastVoicemailMessageTimestamp', lastMessageTimestamp.toString()) + } else { + this.sipCamera.console.debug("No new messages since: " + lastVoicemailMessageTimestamp + " lastMessage: " + lastMessageTimestamp) + } + } + } + } + + isEnabled() : boolean { + return this.sipCamera?.storage?.getItem('notifyVoicemail')?.toLocaleLowerCase() === 'true' || false + } +} \ No newline at end of file diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 328570c99e..274a430357 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -1,379 +1,100 @@ -import { listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; -import { SipMessageHandler, SipCall, SipOptions, SipRequest } from '../../sip/src/sip-call'; -import { RtspServer } from '@scrypted/common/src/rtsp-server'; -import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import { StorageSettings } from '@scrypted/sdk/storage-settings'; -import sdk, { BinarySensor, Camera, DeviceCreator, DeviceCreatorSettings, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; -import { SipSession } from '../../sip/src/sip-session'; -import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType } from '../../sip/src/rtp-utils'; +import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting } from '@scrypted/sdk'; import { randomBytes } from 'crypto'; +import { BticinoSipCamera } from './bticino-camera'; -const STREAM_TIMEOUT = 50000; -const SIP_EXPIRATION_DEFAULT = 3600; -const { deviceManager, mediaManager } = sdk; +export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { -export class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCamera, Settings, BinarySensor { - session: SipSession; - currentMedia: FFmpegInput | MediaStreamUrl; - currentMediaMimeType: string; - refreshTimeout: NodeJS.Timeout; - messageHandler: SipMessageHandler; + devices = new Map(); - constructor(nativeId: string, public provider: SipCamProvider) { - super(nativeId); - let logger = this.log; - this.messageHandler = new class extends SipMessageHandler { - handle( request: SipRequest ) { - // TODO: implement netatmo.onPresence handling? - // {"jsonrpc":"2.0","method":"netatmo.onPresence","params":[{"persons":[]}]} - logger.d("remote message: " + request.content ); - } - }() - } - - async takePicture(option?: PictureOptions): Promise { - throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL."); - } - - async getPictureOptions(): Promise { - return; - } - - settingsStorage = new StorageSettings(this, { - sipfrom: { - title: 'SIP From: URI', - type: 'string', - value: this.storage.getItem('sipfrom'), - description: 'SIP URI From field: Using the IP address of your server you will be calling from. Also the user and IP you added in /etc/flexisip/users/route_ext.conf on the intercom.', - placeholder: 'user@192.168.0.111', - multiple: false, - }, - sipto: { - title: 'SIP To: URI', - type: 'string', - description: 'SIP URI To field: Must look like c300x@IP;transport=udp;rport and UDP transport is the only one supported right now.', - placeholder: 'c300x@192.168.0.2[:5060];transport=udp;rport', - }, - sipdomain: { - title: 'SIP domain', - type: 'string', - description: 'SIP domain: The internal BTicino domain, usually has the following format: 2048362.bs.iotleg.com', - placeholder: '2048362.bs.iotleg.com', - }, - sipexpiration: { - title: 'SIP UA expiration', - type: 'number', - range: [60, SIP_EXPIRATION_DEFAULT], - description: 'SIP UA expiration: How long the UA should remain active before expiring. Use 3600.', - placeholder: '3600', - }, - sipdebug: { - title: 'SIP debug logging', - type: 'boolean', - description: 'Enable SIP debugging', - placeholder: 'true or false', - }, - }); - - getSettings(): Promise { - return this.settingsStorage.getSettings(); - } - - putSetting(key: string, value: SettingValue): Promise { - return this.settingsStorage.putSetting(key, value); - } - - async startIntercom(media: MediaObject): Promise { - this.log.d( "TODO: startIntercom" + media ); - } - - async stopIntercom(): Promise { - this.log.d( "TODO: stopIntercom" ); - } - - resetStreamTimeout() { - this.log.d('starting/refreshing stream'); - clearTimeout(this.refreshTimeout); - this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT); - } - - stopSession() { - if (this.session) { - this.log.d('ending sip session'); - this.session.stop(); - this.session = undefined; - } - } - - async getVideoStream(options?: ResponseMediaStreamOptions): Promise { - if (options?.metadata?.refreshAt) { - if (!this.currentMedia?.mediaStreamOptions) - throw new Error("no stream to refresh"); - - const currentMedia = this.currentMedia; - currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT; - currentMedia.mediaStreamOptions.metadata = { - refreshAt: currentMedia.mediaStreamOptions.refreshAt - }; - this.resetStreamTimeout(); - return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType); - } - - this.stopSession(); - - - const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient(); - - const playbackUrl = `rtsp://127.0.0.1:${playbackPort}`; - - playbackPromise.then(async (client) => { - client.setKeepAlive(true, 10000); - let sip: SipSession; - try { - let rtsp: RtspServer; - const cleanup = () => { - client.destroy(); - if (this.session === sip) - this.session = undefined; - try { - this.log.d('cleanup(): stopping sip session.'); - sip.stop(); - } - catch (e) { - } - rtsp?.destroy(); - } - - client.on('close', cleanup); - client.on('error', cleanup); - - const from = this.storage.getItem('sipfrom')?.trim(); - const to = this.storage.getItem('sipto')?.trim(); - const localIp = from?.split(':')[0].split('@')[1]; - const localPort = parseInt(from?.split(':')[1]) || 5060; - const domain = this.storage.getItem('sipdomain')?.trim(); - const expiration : string = this.storage.getItem('sipuaexpiration')?.trim() || '3600'; - const sipdebug : boolean = this.storage.getItem('sipdebug')?.toLocaleLowerCase() === 'true' || false; - - if (!from || !to || !localIp || !localPort || !domain || !expiration ) { - this.log.e('Error: SIP From/To/Domain URIs not specified!'); - return; - } - - //TODO settings - let sipOptions : SipOptions = { - from: "sip:" + from, - to: "sip:" + to, - domain: domain, - expire: Number.parseInt( expiration ), - localIp, - localPort, - shouldRegister: true, - debugSip: sipdebug, - messageHandler: this.messageHandler - }; - sip = await SipSession.createSipSession(console, "Bticino", sipOptions); - - sip.onCallEnded.subscribe(cleanup); - - // Call the C300X - let remoteRtpDescription = await sip.call( - ( audio ) => { - return [ - 'a=DEVADDR:20', // Needed for bt_answering_machine (bticino specific) - `m=audio ${audio.port} RTP/SAVP 97`, - `a=rtpmap:97 speex/8000`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, - ] - }, ( video ) => { - return [ - `m=video ${video.port} RTP/SAVP 97`, - `a=rtpmap:97 H264/90000`, - `a=fmtp:97 profile-level-id=42801F`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, - 'a=recvonly' - ] - } ); - if( sipOptions.debugSip ) - this.log.d('SIP: Received remote SDP:\n' + remoteRtpDescription.sdp) - - let sdp: string = replacePorts( remoteRtpDescription.sdp, 0, 0 ); - sdp = addTrackControls(sdp); - sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n'); - if( sipOptions.debugSip ) - this.log.d('SIP: Updated SDP:\n' + sdp); - - let vseq = 0; - let vseen = 0; - let vlost = 0; - let aseq = 0; - let aseen = 0; - let alost = 0; - - rtsp = new RtspServer(client, sdp, true); - const parsedSdp = parseSdp(rtsp.sdp); - const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control; - const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control; - if( sipOptions.debugSip ) { - rtsp.console = this.console; - } - - await rtsp.handlePlayback(); - sip.videoSplitter.on('message', message => { - if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); - if (!isRtpMessage) - return; - vseen++; - rtsp.sendTrack(videoTrack, message, !isRtpMessage); - const seq = getSequenceNumber(message); - if (seq !== (vseq + 1) % 0x0FFFF) - vlost++; - vseq = seq; - } - }); - - sip.videoRtcpSplitter.on('message', message => { - rtsp.sendTrack(videoTrack, message, true); - }); - - sip.audioSplitter.on('message', message => { - if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); - if (!isRtpMessage) - return; - aseen++; - rtsp.sendTrack(audioTrack, message, !isRtpMessage); - const seq = getSequenceNumber(message); - if (seq !== (aseq + 1) % 0x0FFFF) - alost++; - aseq = seq; - } - }); - - sip.audioRtcpSplitter.on('message', message => { - rtsp.sendTrack(audioTrack, message, true); - }); - - this.session = sip; - - try { - await rtsp.handleTeardown(); - this.log.d('rtsp client ended'); - } - catch (e) { - this.log.e('rtsp client ended ungracefully' + e); - } - finally { - cleanup(); - } - } - catch (e) { - sip?.stop(); - throw e; + async getCreateDeviceSettings(): Promise { + return [ + { + key: 'newCamera', + title: 'Add Camera', + placeholder: 'Camera name, e.g.: Back Yard Camera, Baby Camera, etc', } - }); - - this.resetStreamTimeout(); - - const mediaStreamOptions = Object.assign(this.getSipMediaStreamOptions(), { - refreshAt: Date.now() + STREAM_TIMEOUT, - }); - - const mediaStreamUrl: MediaStreamUrl = { - url: playbackUrl, - mediaStreamOptions, - }; - this.currentMedia = mediaStreamUrl; - this.currentMediaMimeType = ScryptedMimeTypes.MediaStreamUrl; - - return mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl); + ] } - getSipMediaStreamOptions(): ResponseMediaStreamOptions { - return { - id: 'sip', - name: 'SIP', - // this stream is NOT scrypted blessed due to wackiness in the h264 stream. - // tool: "scrypted", - container: 'sdp', - audio: { - // this is a hint to let homekit, et al, know that it's speex audio and needs transcoding. - codec: 'speex', + async createDevice(settings: DeviceCreatorSettings): Promise { + this.console.log("main::createDevice") + const nativeId = randomBytes(4).toString('hex') + const name = settings.newCamera?.toString() + const camera = await this.updateDevice(nativeId, name); + + this.console.log("main::createDevice: " + camera ) + + const device: Device = { + providerNativeId: nativeId, + info: { + //model: `${camera.model} (${camera.data.kind})`, + manufacturer: 'BticinoPlugin', + //firmware: camera.data.firmware_version, + //serialNumber: camera.data.device_id }, - source: 'local', - userConfigurable: false, + nativeId: nativeId + '-lock', + name: name + ' Lock', + type: ScryptedDeviceType.Lock, + interfaces: [ScryptedInterface.Lock], }; - } - - async getVideoStreamOptions(): Promise { - return [ - this.getSipMediaStreamOptions(), - ] - } -} -export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { + const ret = await sdk.deviceManager.onDevicesChanged({ + providerNativeId: nativeId, + devices: [device], + }); - devices = new Map(); + let x : BticinoSipCamera = await this.getDevice(nativeId) - constructor(nativeId?: string) { - super(nativeId); + let foo : BticinoSipCamera = sdk.systemManager.getDeviceById(x.id) + + this.console.log("main::getDevice: " + x ) + this.console.log("main::" + this.devices.size ) + this.devices.forEach( e => this.console.log("main::device::" + e ) ) + + this.console.log("main::getDevice::done" ) - for (const camId of deviceManager.getNativeIds()) { - if (camId) - this.getDevice(camId); - } - } + let y = await x.getDevice(undefined) + y.lockState = LockState.Locked + //foo.getDevice() - async releaseDevice(id: string, nativeId: string): Promise { - } + //let y = await foo. - async createDevice(settings: DeviceCreatorSettings): Promise { - const nativeId = randomBytes(4).toString('hex'); - const name = settings.newCamera.toString(); - await this.updateDevice(nativeId, name); return nativeId; } - async getCreateDeviceSettings(): Promise { - return [ - { - key: 'newCamera', - title: 'Add Camera', - placeholder: 'Camera name, e.g.: Back Yard Camera, Baby Camera, etc', - } - ] - } - updateDevice(nativeId: string, name: string) { - return deviceManager.onDeviceDiscovered({ + return sdk.deviceManager.onDeviceDiscovered({ nativeId, + info: { + //model: `${camera.model} (${camera.data.kind})`, + manufacturer: 'BticinoSipPlugin', + //firmware: camera.data.firmware_version, + //serialNumber: camera.data.device_id + }, name, interfaces: [ ScryptedInterface.Camera, ScryptedInterface.VideoCamera, ScryptedInterface.Settings, ScryptedInterface.Intercom, - ScryptedInterface.BinarySensor + ScryptedInterface.BinarySensor, + ScryptedDeviceType.DeviceProvider ], type: ScryptedDeviceType.Doorbell, - }); - } + }) + } - getDevice(nativeId: string) { - let ret = this.devices.get(nativeId); - if (!ret) { - ret = this.createCamera(nativeId); - if (ret) - this.devices.set(nativeId, ret); + async getDevice(nativeId: string): Promise { + if (!this.devices.has(nativeId)) { + const camera = new BticinoSipCamera(nativeId, this); + this.devices.set(nativeId, camera); } - return ret; + return this.devices.get(nativeId); } - createCamera(nativeId: string): SipCamera { - return new SipCamera(nativeId, this); + async releaseDevice(id: string, nativeId: string): Promise { + this.console.log('release') } } -export default new SipCamProvider(); +export default new BticinoSipPlugin() \ No newline at end of file diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts new file mode 100644 index 0000000000..be42436b22 --- /dev/null +++ b/plugins/bticino/src/sip-helper.ts @@ -0,0 +1,43 @@ +import { SipOptions } from "../../sip/src/sip-call"; +import { SipSession } from "../../sip/src/sip-session"; +import { BticinoSipCamera } from "./bticino-camera"; + +export class SipHelper { + public static sipOptions( camera : BticinoSipCamera ) : SipOptions { + //Might be removed soon + if( camera.storage.getItem('sipto') && camera.storage.getItem('sipto').toString().indexOf(';') > 0 ) { + camera.storage.setItem('sipto', camera.storage.getItem('sipto').toString().split(';')[0] ) + } + const from = camera.storage.getItem('sipfrom')?.trim() + const to = camera.storage.getItem('sipto')?.trim() + const localIp = from?.split(':')[0].split('@')[1] + const localPort = parseInt(from?.split(':')[1]) || 5060 + const domain = camera.storage.getItem('sipdomain')?.trim() + const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '3600' + const sipdebug : boolean = camera.storage.getItem('sipdebug')?.toLocaleLowerCase() === 'true' || false + + if (!from || !to || !localIp || !localPort || !domain || !expiration ) { + camera.log.e('Error: SIP From/To/Domain URIs not specified!') + throw new Error('SIP From/To/Domain URIs not specified!') + } + + let sipOptions : SipOptions = { + from: "sip:" + from, + //TCP is more reliable for large messages, also see useTcp=true below + to: "sip:" + to + ";transport=tcp", + domain: domain, + expire: Number.parseInt( expiration ), + localIp, + localPort, + shouldRegister: true, + debugSip: sipdebug, + useTcp: true, + messageHandler: camera.messageHandler + }; + return sipOptions + } + + public static sipSession( camera : BticinoSipCamera ) : Promise { + return SipSession.createSipSession(console, "Bticino", this.sipOptions( camera ) ) + } +} \ No newline at end of file diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts new file mode 100644 index 0000000000..49963e2372 --- /dev/null +++ b/plugins/bticino/src/storage-settings.ts @@ -0,0 +1,60 @@ +import { ScryptedInterface, Setting, SettingValue } from '@scrypted/sdk'; +import { StorageSettings } from '@scrypted/sdk/storage-settings'; +import { BticinoSipCamera } from './bticino-camera'; + +export class BticinoStorageSettings { + private storageSettings + constructor(camera : BticinoSipCamera) { + + this.storageSettings = new StorageSettings( camera, { + sipfrom: { + title: 'SIP From: URI', + type: 'string', + value: camera.storage.getItem('sipfrom'), + description: 'SIP URI From field: Using the IP address of your server you will be calling from.', + placeholder: 'user@192.168.0.111', + multiple: false, + }, + sipto: { + title: 'SIP To: URI', + type: 'string', + description: 'SIP URI To field: Must look like c300x@192.168.0.2', + placeholder: 'c300x@192.168.0.2', + }, + sipdomain: { + title: 'SIP domain', + type: 'string', + description: 'SIP domain - tshe internal BTicino domain, usually has the following format: 2048362.bs.iotleg.com', + placeholder: '2048362.bs.iotleg.com', + }, + sipexpiration: { + title: 'SIP UA expiration', + type: 'number', + range: [60, 3600], + description: 'How long the UA should remain active before expiring and having to re-register (in seconds)', + defaultValue: 600, + placeholder: '600', + }, + sipdebug: { + title: 'SIP debug logging', + type: 'boolean', + description: 'Enable SIP debugging', + placeholder: 'true or false', + }, + notifyVoicemail: { + title: 'Notify on new voicemail messages', + type: 'boolean', + description: 'Enable voicemail alerts', + placeholder: 'true or false', + }, + }); + } + + getSettings(): Promise { + return this.storageSettings.getSettings(); + } + + putSetting(key: string, value: SettingValue): Promise { + return this.storageSettings.putSetting(key, value); + } +} \ No newline at end of file diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 630a20327a..5c2b6e0045 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -23,7 +23,6 @@ } }, "../../common": { - "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -38,97 +37,24 @@ "@types/node": "^16.9.0" } }, - "../../external/ring-client-api": { - "version": "11.0.4", - "extraneous": true, - "funding": [ - { - "type": "paypal", - "url": "https://www.paypal.me/dustingreif" - }, - { - "type": "github", - "url": "https://github.com/sponsors/dgreif" - } - ], - "license": "MIT", - "workspaces": [ - "homebridge-ui" - ], - "dependencies": { - "@eneris/push-receiver": "../push-receiver", - "@homebridge/camera-utils": "^2.1.1", - "@homebridge/plugin-ui-utils": "^0.0.19", - "@types/socket.io-client": "1.4.36", - "colors": "^1.4.0", - "debug": "^4.3.4", - "got": "^11.8.3", - "rxjs": "^7.5.5", - "sdp": "^3.0.3", - "socket.io-client": "^2.4.0", - "stun": "^2.1.0", - "systeminformation": "^5.11.15", - "uuid": "^8.3.2", - "werift": "0.15.4", - "ws": "^8.7.0" - }, - "bin": { - "ring-auth-cli": "ring-auth-cli.js", - "ring-device-data-cli": "ring-device-data-cli.js" - }, - "devDependencies": { - "@swc-node/register": "^1.5.1", - "@types/debug": "4.1.7", - "@types/jest": "27.5.1", - "@types/node": "17.0.36", - "@types/uuid": "8.3.4", - "@types/ws": "^8.5.3", - "@typescript-eslint/eslint-plugin": "5.26.0", - "@typescript-eslint/parser": "5.26.0", - "concurrently": "^7.2.1", - "conventional-github-releaser": "3.1.5", - "dotenv": "16.0.1", - "esbuild": "^0.14.42", - "eslint": "8.16.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-jest": "26.4.2", - "eslint-plugin-prettier": "4.0.0", - "express": "4.18.1", - "homebridge": "1.4.1", - "jest": "28.1.0", - "prettier": "2.6.2", - "reflect-metadata": "0.1.13", - "rimraf": "3.0.2", - "standard-version": "9.5.0", - "ts-jest": "28.0.3", - "typescript": "4.7.2" - }, - "engines": { - "homebridge": ">=1.4.0", - "node": "^16" - } - }, - "../../external/ring-client/api": { - "extraneous": true - }, "../../sdk": { - "name": "@scrypted/sdk", - "version": "0.2.22", + "version": "0.2.55", "dev": true, "license": "ISC", "dependencies": { - "@babel/preset-typescript": "^7.16.7", + "@babel/preset-typescript": "^7.18.6", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", "raw-loader": "^4.0.2", "rimraf": "^3.0.2", "tmp": "^0.2.1", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" }, "bin": { @@ -141,16 +67,13 @@ "scrypted-webpack": "bin/scrypted-webpack.js" }, "devDependencies": { - "@types/node": "^18.11.9", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "stringify-object": "^3.3.0", "ts-node": "^10.4.0", "typedoc": "^0.23.21" } }, - "../sdk": { - "extraneous": true - }, "node_modules/@homebridge/camera-utils": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", @@ -172,9 +95,9 @@ "link": true }, "node_modules/@types/node": { - "version": "16.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", - "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==", + "version": "16.18.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", "dev": true }, "node_modules/@types/uuid": { @@ -257,14 +180,6 @@ "node": ">=4" } }, - "node_modules/camelcase-keys/node_modules/quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", - "engines": { - "node": ">=4" - } - }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -297,6 +212,22 @@ "node": ">=0.10.0" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -329,9 +260,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -350,17 +281,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decompress-response/node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", @@ -449,6 +369,17 @@ "node": ">= 8" } }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -715,6 +646,17 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -728,9 +670,9 @@ } }, "node_modules/minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", "dependencies": { "yallist": "^4.0.0" }, @@ -750,6 +692,17 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -761,6 +714,11 @@ "node": ">=10" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -805,7 +763,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -928,27 +886,6 @@ "debug": "^4.3.1" } }, - "node_modules/pick-port/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/pick-port/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -993,6 +930,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", + "engines": { + "node": ">=4" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1047,9 +992,9 @@ } }, "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dependencies": { "tslib": "^2.1.0" } @@ -1177,14 +1122,6 @@ "node": ">=0.2.2" } }, - "node_modules/sip/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1276,27 +1213,6 @@ "node": ">=8.3" } }, - "node_modules/stun/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/stun/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1309,9 +1225,9 @@ } }, "node_modules/systeminformation": { - "version": "5.12.15", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.12.15.tgz", - "integrity": "sha512-LMctTV27bGqWMBsuhzvNTH3roKOQonTN730F9v0x9YtoYducXcobs0rg3QKNnWDyHJyWIgKY6FiHlFcXJYclTQ==", + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", + "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==", "os": [ "darwin", "linux", @@ -1334,13 +1250,13 @@ } }, "node_modules/tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -1412,7 +1328,15 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dependencies": { + "async-limiter": "~1.0.0" + } }, "node_modules/yallist": { "version": "4.0.0", @@ -1455,12 +1379,12 @@ "@scrypted/sdk": { "version": "file:../../sdk", "requires": { - "@babel/preset-typescript": "^7.16.7", - "@types/node": "^18.11.9", + "@babel/preset-typescript": "^7.18.6", + "@types/node": "^18.11.18", "@types/stringify-object": "^4.0.0", "adm-zip": "^0.4.13", "axios": "^0.21.4", - "babel-loader": "^8.2.3", + "babel-loader": "^9.1.0", "babel-plugin-const-enum": "^1.1.0", "esbuild": "^0.15.9", "ncp": "^2.0.0", @@ -1468,17 +1392,18 @@ "rimraf": "^3.0.2", "stringify-object": "^3.3.0", "tmp": "^0.2.1", + "ts-loader": "^9.4.2", "ts-node": "^10.4.0", "typedoc": "^0.23.21", - "typescript": "^4.9.3", - "webpack": "^5.74.0", + "typescript": "^4.9.4", + "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.5.0" } }, "@types/node": { - "version": "16.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", - "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==", + "version": "16.18.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", "dev": true }, "@types/uuid": { @@ -1541,13 +1466,6 @@ "camelcase": "^4.1.0", "map-obj": "^2.0.0", "quick-lru": "^1.0.0" - }, - "dependencies": { - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" - } } }, "chownr": { @@ -1573,6 +1491,14 @@ "array-find-index": "^1.0.1" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -1595,9 +1521,9 @@ } }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "decompress-response": { "version": "6.0.0", @@ -1605,13 +1531,6 @@ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "requires": { "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } } }, "detect-libc": { @@ -1678,6 +1597,16 @@ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "requires": { "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "function-bind": { @@ -1882,6 +1811,11 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, "minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -1892,9 +1826,9 @@ } }, "minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", "requires": { "yallist": "^4.0.0" } @@ -1906,6 +1840,16 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } } }, "mkdirp": { @@ -1913,6 +1857,11 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1945,7 +1894,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -2039,21 +1988,6 @@ "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", "requires": { "debug": "^4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "pify": { @@ -2085,6 +2019,11 @@ "strict-uri-encode": "^2.0.0" } }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -2124,9 +2063,9 @@ } }, "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "requires": { "tslib": "^2.1.0" } @@ -2195,16 +2134,6 @@ "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", "requires": { "ws": "^6.1.0" - }, - "dependencies": { - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "requires": { - "async-limiter": "~1.0.0" - } - } } }, "spdx-correct": { @@ -2275,21 +2204,6 @@ "parse-url": "^5.0.1", "turbo-crc32": "^1.0.0", "universalify": "^0.1.2" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "supports-preserve-symlinks-flag": { @@ -2298,18 +2212,18 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "systeminformation": { - "version": "5.12.15", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.12.15.tgz", - "integrity": "sha512-LMctTV27bGqWMBsuhzvNTH3roKOQonTN730F9v0x9YtoYducXcobs0rg3QKNnWDyHJyWIgKY6FiHlFcXJYclTQ==" + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", + "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==" }, "tar": { - "version": "6.1.12", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", - "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" @@ -2360,7 +2274,15 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "requires": { + "async-limiter": "~1.0.0" + } }, "yallist": { "version": "4.0.0", diff --git a/plugins/sip/src/compositeSipMessageHandler.ts b/plugins/sip/src/compositeSipMessageHandler.ts new file mode 100644 index 0000000000..e0a055cf4c --- /dev/null +++ b/plugins/sip/src/compositeSipMessageHandler.ts @@ -0,0 +1,14 @@ +import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call"; + +export class CompositeSipMessageHandler extends SipMessageHandler { + private handlers : SipMessageHandler[] = [] + constructor() { + super() + } + handle(request: SipRequest) { + this.handlers.forEach( (handler) => handler.handle( request ) ) + } + add( handler : SipMessageHandler ) { + this.handlers.push( handler ) + } +} \ No newline at end of file diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index 155525dcc8..8119618f2b 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -16,6 +16,7 @@ export interface SipOptions { localIp: string localPort: number debugSip?: boolean + useTcp?: boolean messageHandler?: SipMessageHandler shouldRegister?: boolean } @@ -169,8 +170,8 @@ export class SipCall { host, hostname: host, port: port, - udp: true, - tcp: false, + udp: !this.sipOptions.useTcp, + tcp: this.sipOptions.useTcp, tls: false, // tls_port: tlsPort, // tls: { @@ -203,8 +204,11 @@ export class SipCall { if( m.method == 'REGISTER' ) { m.uri = "sip:" + sipOptions.domain - } else if( m.method == 'INVITE' ) { + } else if( m.method == 'INVITE' || m.method == 'MESSAGE' ) { m.uri = toWithDomain + if( m.method == 'MESSAGE' && m.headers.to ) { + m.headers.to.params = null; + } } else if( m.method == 'ACK' || m.method == 'BYE' ) { m.uri = this.registrarContact } else { @@ -215,6 +219,10 @@ export class SipCall { m.headers.from.uri = fromWithDomain if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); + // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) + if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { + m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + } } } @@ -342,7 +350,7 @@ export class SipCall { /** * Initiate a call by sending a SIP INVITE request */ - async invite( audioSection, videoSection? ) { + async invite( audioSection, videoSection? ) : Promise { let ssrc = randomInteger() let audio = audioSection ? audioSection( this.rtpOptions.audio, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.audio.rtcpPort}`] ) : [] let video = videoSection ? videoSection( this.rtpOptions.video, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.video.rtcpPort}`] ) : [] @@ -375,7 +383,7 @@ export class SipCall { /** * Register the user agent with a Registrar */ - async register() { + async register() : Promise { const { from } = this.sipOptions; await timeoutPromise( 500, this.request({ @@ -384,33 +392,31 @@ export class SipCall { //supported: 'replaces, outbound', allow: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'application/sdp', contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], }, - }).catch(() => { - // Don't care if we get an exception here. - })); + }).catch(noop)); } /** * Send a message to the current call contact */ - async message( content: string ) { + async message( content: string ) : Promise { const { from } = this.sipOptions, - inviteResponse = await this.request({ + messageResponse = await this.request({ method: 'MESSAGE', headers: { //supported: 'replaces, outbound', allow: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'application/sdp', + 'content-type': 'text/plain', contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], }, content: content }); + return messageResponse; } - async sendBye() { + async sendBye() : Promise { this.console.log('Sending BYE...') return await timeoutPromise( 3000, this.request({ method: 'BYE' }).catch(() => { // Don't care if we get an exception here. diff --git a/plugins/sip/src/sip-session.ts b/plugins/sip/src/sip-session.ts index a2a8cb43af..e146a1e056 100644 --- a/plugins/sip/src/sip-session.ts +++ b/plugins/sip/src/sip-session.ts @@ -10,7 +10,7 @@ export class SipSession extends Subscribed { private hasStarted = false private hasCallEnded = false private onCallEndedSubject = new ReplaySubject(1) - private sipCall: SipCall + public sipCall: SipCall onCallEnded = this.onCallEndedSubject.asObservable() constructor( @@ -90,7 +90,7 @@ export class SipSession extends Subscribed { try { if( this.sipOptions.shouldRegister ) await this.sipCall.register() - const rtpDescription = await this.sipCall.invite( audioSection, videoSection ), + const rtpDescription : RtpDescription = await this.sipCall.invite( audioSection, videoSection ), sendStunRequests = () => { sendStunBindingRequest({ rtpSplitter: this.audioSplitter, From f950f604f5f3217efc35d9439b171d6330512980 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 11:56:40 +0100 Subject: [PATCH 03/24] Cleanup dependencies, code in sip, bticino plugins --- plugins/bticino/package-lock.json | 804 +++--------------------- plugins/bticino/package.json | 5 +- plugins/bticino/src/bticino-camera.ts | 8 +- plugins/bticino/src/bticino-lock.ts | 4 +- plugins/bticino/src/main.ts | 27 +- plugins/bticino/src/storage-settings.ts | 2 +- plugins/sip/package-lock.json | 19 +- plugins/sip/package.json | 5 +- plugins/sip/src/sip-call.ts | 3 +- 9 files changed, 102 insertions(+), 775 deletions(-) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 3167d64865..29f8209a84 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -8,23 +8,20 @@ "name": "@scrypted/bticino", "version": "0.0.6-beta.1", "dependencies": { - "@homebridge/camera-utils": "^2.0.4", - "rxjs": "^7.5.6", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", - "ts-node": "^10.9.1", "uuid": "^8.3.2" }, "devDependencies": { "@scrypted/common": "file:../../common", "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4" + "@types/uuid": "^8.3.4", + "ts-node": "^10.9.1" } }, "../../common": { - "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -40,7 +37,6 @@ } }, "../../sdk": { - "name": "@scrypted/sdk", "version": "0.2.55", "dev": true, "license": "ISC", @@ -81,6 +77,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -88,22 +85,11 @@ "node": ">=12" } }, - "node_modules/@homebridge/camera-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", - "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", - "dependencies": { - "execa": "^5.1.1", - "ffmpeg-for-homebridge": "^0.1.4", - "pick-port": "^1.0.1", - "rxjs": "^7.5.6", - "systeminformation": "^5.12.1" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -111,12 +97,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -133,27 +121,32 @@ "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true }, "node_modules/@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==" + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", + "dev": true }, "node_modules/@types/uuid": { "version": "8.3.4", @@ -165,6 +158,7 @@ "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -176,6 +170,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -183,7 +178,8 @@ "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/array-find-index": { "version": "1.0.2", @@ -259,31 +255,11 @@ "node": ">=4" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "node_modules/currently-unhandled": { "version": "0.4.1", @@ -351,44 +327,15 @@ "node": ">=0.10" } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, "engines": { "node": ">=0.3.1" } }, - "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -397,40 +344,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/ffmpeg-for-homebridge": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", - "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", - "hasInstallScript": true, - "dependencies": { - "detect-libc": "^2.0.1", - "dotenv": "^16.0.1", - "simple-get": "^4.0.1", - "tar": "^6.1.11" - } - }, "node_modules/filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", @@ -450,28 +363,6 @@ "node": ">=4" } }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -498,17 +389,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -541,14 +421,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", @@ -623,17 +495,6 @@ "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-stun": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", @@ -642,11 +503,6 @@ "node": ">=4" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -701,7 +557,8 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/map-obj": { "version": "2.0.0", @@ -730,30 +587,6 @@ "node": ">=6" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -766,51 +599,6 @@ "node": ">= 4" } }, - "node_modules/minipass": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", - "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -838,47 +626,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -951,14 +706,6 @@ "node": ">=4" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -975,14 +722,6 @@ "node": ">=4" } }, - "node_modules/pick-port": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", - "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", - "dependencies": { - "debug": "^4.3.1" - } - }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -1088,14 +827,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1128,25 +859,6 @@ "semver": "bin/semver" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1165,49 +877,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/sip": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", @@ -1271,14 +940,6 @@ "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, "node_modules/strip-indent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", @@ -1321,47 +982,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/systeminformation": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", - "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==", - "os": [ - "darwin", - "linux", - "win32", - "freebsd", - "openbsd", - "netbsd", - "sunos", - "android" - ], - "bin": { - "systeminformation": "lib/cli.js" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "Buy me a coffee", - "url": "https://www.buymeacoffee.com/systeminfo" - } - }, - "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -1374,6 +994,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -1412,11 +1033,6 @@ } } }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, "node_modules/turbo-crc32": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", @@ -1429,6 +1045,7 @@ "version": "4.9.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, "peer": true, "bin": { "tsc": "bin/tsc", @@ -1457,7 +1074,8 @@ "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -1468,25 +1086,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, "node_modules/ws": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", @@ -1495,11 +1094,6 @@ "async-limiter": "~1.0.0" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", @@ -1512,6 +1106,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, "engines": { "node": ">=6" } @@ -1522,36 +1117,28 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" } }, - "@homebridge/camera-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", - "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", - "requires": { - "execa": "^5.1.1", - "ffmpeg-for-homebridge": "^0.1.4", - "pick-port": "^1.0.1", - "rxjs": "^7.5.6", - "systeminformation": "^5.12.1" - } - }, "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true }, "@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1595,27 +1182,32 @@ "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "@tsconfig/node16": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true }, "@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==" + "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", + "dev": true }, "@types/uuid": { "version": "8.3.4", @@ -1626,17 +1218,20 @@ "acorn": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "array-find-index": { "version": "1.0.2", @@ -1694,25 +1289,11 @@ "quick-lru": "^1.0.0" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "currently-unhandled": { "version": "0.4.1", @@ -1756,28 +1337,11 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - } - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true }, "error-ex": { "version": "1.3.2", @@ -1787,33 +1351,6 @@ "is-arrayish": "^0.2.1" } }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "ffmpeg-for-homebridge": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", - "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", - "requires": { - "detect-libc": "^2.0.1", - "dotenv": "^16.0.1", - "simple-get": "^4.0.1", - "tar": "^6.1.11" - } - }, "filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", @@ -1827,24 +1364,6 @@ "locate-path": "^2.0.0" } }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1868,11 +1387,6 @@ "has-symbols": "^1.0.3" } }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -1896,11 +1410,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, "indent-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", @@ -1962,21 +1471,11 @@ } } }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, "is-stun": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -2019,7 +1518,8 @@ "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "map-obj": { "version": "2.0.0", @@ -2042,21 +1542,6 @@ "yargs-parser": "^10.0.0" } }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - }, "minimist-options": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", @@ -2066,38 +1551,6 @@ "is-plain-obj": "^1.1.0" } }, - "minipass": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", - "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2119,34 +1572,10 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, "p-limit": { "version": "1.3.0", @@ -2205,11 +1634,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2223,14 +1647,6 @@ "pify": "^3.0.0" } }, - "pick-port": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", - "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", - "requires": { - "debug": "^4.3.1" - } - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -2303,14 +1719,6 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "requires": { - "tslib": "^2.1.0" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2326,19 +1734,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -2354,21 +1749,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "sip": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", @@ -2420,11 +1800,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, "strip-indent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", @@ -2452,24 +1827,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, - "systeminformation": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", - "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==" - }, - "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, "trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -2479,6 +1836,7 @@ "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -2495,11 +1853,6 @@ "yn": "3.1.1" } }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, "turbo-crc32": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", @@ -2509,6 +1862,7 @@ "version": "4.9.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, "peer": true }, "universalify": { @@ -2524,7 +1878,8 @@ "v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -2535,19 +1890,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, "ws": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", @@ -2556,11 +1898,6 @@ "async-limiter": "~1.0.0" } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "yargs-parser": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", @@ -2572,7 +1909,8 @@ "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index e553bb03c8..a252cfaff5 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -33,17 +33,16 @@ ] }, "dependencies": { - "@homebridge/camera-utils": "^2.0.4", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", - "ts-node": "^10.9.1", "uuid": "^8.3.2" }, "devDependencies": { "@scrypted/common": "file:../../common", "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4" + "@types/uuid": "^8.3.4", + "ts-node": "^10.9.1" } } diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 0e5dd4cfba..dcd8a14699 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -1,7 +1,7 @@ import { listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; import { SipSession } from '../../sip/src/sip-session'; import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType } from '../../sip/src/rtp-utils'; import { VoicemailHandler } from './bticino-voicemailHandler'; @@ -9,7 +9,7 @@ import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHan import { sleep } from '@scrypted/common/src/sleep'; import { SipHelper } from './sip-helper'; import { BticinoStorageSettings } from './storage-settings'; -import { BticinoSipPlugin } from './main'; +import mediaManager, { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; const STREAM_TIMEOUT = 50000; @@ -112,7 +112,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid refreshAt: currentMedia.mediaStreamOptions.refreshAt }; this.resetStreamTimeout(); - return sdk.mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType); + return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType); } this.stopSession(); @@ -260,7 +260,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.currentMedia = mediaStreamUrl; this.currentMediaMimeType = ScryptedMimeTypes.MediaStreamUrl; - return sdk.mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl); + return mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl); } getSipMediaStreamOptions(): ResponseMediaStreamOptions { diff --git a/plugins/bticino/src/bticino-lock.ts b/plugins/bticino/src/bticino-lock.ts index d4d9d6798d..65eb6658ba 100644 --- a/plugins/bticino/src/bticino-lock.ts +++ b/plugins/bticino/src/bticino-lock.ts @@ -1,7 +1,7 @@ -import sdk, { ScryptedDeviceBase, Lock, LockState, DeviceCreator, DeviceCreatorSettings, Setting } from "@scrypted/sdk"; +import { ScryptedDeviceBase, Lock, LockState } from "@scrypted/sdk"; import { BticinoSipCamera } from "./bticino-camera"; -export class BticinoSipLock extends ScryptedDeviceBase implements Lock { +export class BticinoSipLock extends ScryptedDeviceBase implements Lock { private timeout : NodeJS.Timeout constructor(public camera: BticinoSipCamera) { diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 2f60b084b7..e37f4a2e99 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -2,6 +2,8 @@ import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Lock import { randomBytes } from 'crypto'; import { BticinoSipCamera } from './bticino-camera'; +const { systemManager, deviceManager, mediaManager } = sdk; + export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { devices = new Map(); @@ -17,13 +19,10 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid } async createDevice(settings: DeviceCreatorSettings): Promise { - this.console.log("main::createDevice") const nativeId = randomBytes(4).toString('hex') const name = settings.newCamera?.toString() const camera = await this.updateDevice(nativeId, name); - this.console.log("main::createDevice: " + camera ) - const device: Device = { providerNativeId: nativeId, info: { @@ -38,32 +37,22 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid interfaces: [ScryptedInterface.Lock], }; - const ret = await sdk.deviceManager.onDevicesChanged({ + const ret = await deviceManager.onDevicesChanged({ providerNativeId: nativeId, devices: [device], }); - let x : BticinoSipCamera = await this.getDevice(nativeId) - - let foo : BticinoSipCamera = sdk.systemManager.getDeviceById(x.id) - - this.console.log("main::getDevice: " + x ) - this.console.log("main::" + this.devices.size ) - this.devices.forEach( e => this.console.log("main::device::" + e ) ) - - this.console.log("main::getDevice::done" ) - - let y = await x.getDevice(undefined) - y.lockState = LockState.Locked - //foo.getDevice() + let sipCamera : BticinoSipCamera = await this.getDevice(nativeId) + let foo : BticinoSipCamera = systemManager.getDeviceById(sipCamera.id) - //let y = await foo. + let lock = await sipCamera.getDevice(undefined) + lock.lockState = LockState.Locked return nativeId; } updateDevice(nativeId: string, name: string) { - return sdk.deviceManager.onDeviceDiscovered({ + return deviceManager.onDeviceDiscovered({ nativeId, info: { //model: `${camera.model} (${camera.data.kind})`, diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index 49963e2372..d66e95924f 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -1,4 +1,4 @@ -import { ScryptedInterface, Setting, SettingValue } from '@scrypted/sdk'; +import { Setting, SettingValue } from '@scrypted/sdk'; import { StorageSettings } from '@scrypted/sdk/storage-settings'; import { BticinoSipCamera } from './bticino-camera'; diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 5c2b6e0045..24efac0e12 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -1,15 +1,14 @@ { "name": "@scrypted/sip", - "version": "0.0.5", + "version": "0.0.6-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sip", - "version": "0.0.5", + "version": "0.0.6-beta.1", "dependencies": { "@homebridge/camera-utils": "^2.0.4", - "rxjs": "^7.5.5", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", @@ -23,6 +22,7 @@ } }, "../../common": { + "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -38,6 +38,7 @@ } }, "../../sdk": { + "name": "@scrypted/sdk", "version": "0.2.55", "dev": true, "license": "ISC", @@ -753,9 +754,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1887,9 +1888,9 @@ } }, "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, "once": { "version": "1.4.0", diff --git a/plugins/sip/package.json b/plugins/sip/package.json index d00af7b5c1..f3dcab0c3b 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sip", - "version": "0.0.5", + "version": "0.0.6-beta.1", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", @@ -34,11 +34,10 @@ }, "dependencies": { "@homebridge/camera-utils": "^2.0.4", - "rxjs": "^7.5.5", "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", - "uuid": "^8.3.2" + "uuid": "^8.3.4" }, "devDependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index a0e02466bd..6a6ab68caf 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -1,7 +1,7 @@ import { noop, Subject } from 'rxjs' import { randomInteger, randomString } from './util' import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' -import { decodeSrtpOptions } from '@homebridge/camera-utils' +import { decodeSrtpOptions } from '../../ring/src/srtp-utils' import { stringify } from 'sip/sip' import { timeoutPromise } from '@scrypted/common/src/promise-utils'; @@ -222,6 +222,7 @@ export class SipCall { // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + } } } From 9c869e23912ccf5a27532aef4164ed706516872e Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 12:06:42 +0100 Subject: [PATCH 04/24] Cleanup dependencies, code in sip, bticino plugins --- plugins/bticino/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index e37f4a2e99..09898c236c 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -82,7 +82,6 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid } async releaseDevice(id: string, nativeId: string): Promise { - this.console.log('release') } } From 06f932089cabd01df0789888267eccd1a3b14711 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 14:43:08 +0100 Subject: [PATCH 05/24] Clear stale devices from our map and clear the voicemail check --- plugins/bticino/src/bticino-camera.ts | 15 ++++++++------- plugins/bticino/src/bticino-voicemailHandler.ts | 13 ++++++++++--- plugins/bticino/src/main.ts | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index dcd8a14699..7d2a085321 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -15,17 +15,18 @@ import { BticinoSipLock } from './bticino-lock'; const STREAM_TIMEOUT = 50000; export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { - session: SipSession - currentMedia: FFmpegInput | MediaStreamUrl - currentMediaMimeType: string - refreshTimeout: NodeJS.Timeout - messageHandler: CompositeSipMessageHandler - settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) + private session: SipSession + private currentMedia: FFmpegInput | MediaStreamUrl + private currentMediaMimeType: string + private refreshTimeout: NodeJS.Timeout + public messageHandler: CompositeSipMessageHandler + private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) + public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) this.messageHandler = new CompositeSipMessageHandler() - this.messageHandler.add( new VoicemailHandler( this ) ) + this.messageHandler.add( this.voicemailHandler ) } sipUnlock(): Promise { diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index 0633736f88..e26633f641 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -3,6 +3,7 @@ import { BticinoSipCamera } from "./bticino-camera"; export class VoicemailHandler extends SipMessageHandler { private sipCamera : BticinoSipCamera + private timeout : NodeJS.Timeout constructor( sipCamera : BticinoSipCamera ) { super() @@ -14,13 +15,19 @@ export class VoicemailHandler extends SipMessageHandler { if( !this.sipCamera ) return if( this.isEnabled() ) { - this.sipCamera.console.info("Checking answering machine.") + this.sipCamera.console.info("Checking answering machine, cameraId: " + this.sipCamera.id ) this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) ) } else { - this.sipCamera.console.info("Answering machine check not enabled.") + this.sipCamera.console.info("Answering machine check not enabled, cameraId: " + this.sipCamera.id ) } //TODO: make interval customizable, now every 5 minutes - setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) + this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) + } + + cancelVoicemailCheck() { + if( this.timeout ) { + clearTimeout(this.timeout) + } } handle(request: SipRequest) { diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 09898c236c..11f418e5e3 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -8,6 +8,23 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid devices = new Map(); + constructor() { + super() + systemManager.listen( + async (eventSource, eventDetails, eventData) => { + if( !eventSource ) { + this.devices.forEach( (camera) => { + if(camera?.id === eventData) { + camera.voicemailHandler.cancelVoicemailCheck() + if( this.devices.delete(camera.nativeId) ) { + this.console.log("Removed device from list: " + eventData) + } + } + } ) + } + }); + } + async getCreateDeviceSettings(): Promise { return [ { From db8f01d6ed63d60288c17e41ea32909432e4d16b Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 15:54:56 +0100 Subject: [PATCH 06/24] Do not require register() for a SIP call --- plugins/bticino/src/bticino-camera.ts | 111 +++++++++--------- .../bticino/src/bticino-voicemailHandler.ts | 4 +- plugins/bticino/src/sip-helper.ts | 9 +- 3 files changed, 64 insertions(+), 60 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 7d2a085321..396955fc28 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -31,7 +31,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid sipUnlock(): Promise { this.log.i("unlocking C300X door ") - return SipHelper.sipSession( this ) + return SipHelper.sipSession( SipHelper.sipOptions( this ) ) .then( ( sip ) => { sip.sipCall.register() .then( () => @@ -49,7 +49,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } getAswmStatus() : Promise { - return SipHelper.sipSession( this ) + return SipHelper.sipSession( SipHelper.sipOptions( this ) ) .then( ( sip ) => { sip.sipCall.register() .then( () => sip.sipCall.message( "GetAswmStatus!") ) @@ -64,36 +64,36 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } async getPictureOptions(): Promise { - return; + return } getSettings(): Promise { - return this.settingsStorage.getSettings(); + return this.settingsStorage.getSettings() } putSetting(key: string, value: SettingValue): Promise { - return this.settingsStorage.putSetting(key, value); + return this.settingsStorage.putSetting(key, value) } async startIntercom(media: MediaObject): Promise { - this.log.d( "TODO: startIntercom" + media ); + this.log.d( "TODO: startIntercom" + media ) } async stopIntercom(): Promise { - this.log.d( "TODO: stopIntercom" ); + this.log.d( "TODO: stopIntercom" ) } resetStreamTimeout() { - this.log.d('starting/refreshing stream'); - clearTimeout(this.refreshTimeout); - this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT); + this.log.d('starting/refreshing stream') + clearTimeout(this.refreshTimeout) + this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT) } stopSession() { if (this.session) { - this.log.d('ending sip session'); - this.session.stop(); - this.session = undefined; + this.log.d('ending sip session') + this.session.stop() + this.session = undefined } } @@ -107,48 +107,53 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid if (!this.currentMedia?.mediaStreamOptions) throw new Error("no stream to refresh"); - const currentMedia = this.currentMedia; + const currentMedia = this.currentMedia currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT; currentMedia.mediaStreamOptions.metadata = { refreshAt: currentMedia.mediaStreamOptions.refreshAt }; - this.resetStreamTimeout(); - return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType); + this.resetStreamTimeout() + return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType) } this.stopSession(); - const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient(); + const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient() - const playbackUrl = `rtsp://127.0.0.1:${playbackPort}`; + const playbackUrl = `rtsp://127.0.0.1:${playbackPort}` playbackPromise.then(async (client) => { - client.setKeepAlive(true, 10000); - let sip: SipSession; + client.setKeepAlive(true, 10000) + let sip: SipSession try { let rtsp: RtspServer; const cleanup = () => { client.destroy(); if (this.session === sip) - this.session = undefined; + this.session = undefined try { - this.log.d('cleanup(): stopping sip session.'); - sip.stop(); + this.log.d('cleanup(): stopping sip session.') + sip.stop() } catch (e) { } - rtsp?.destroy(); + rtsp?.destroy() } - client.on('close', cleanup); - client.on('error', cleanup); + client.on('close', cleanup) + client.on('error', cleanup) - sip = await SipHelper.sipSession( this ) + let sipOptions = SipHelper.sipOptions( this ) + + // A normal call session doesn't require registering + sipOptions.shouldRegister = false + + sip = await SipHelper.sipSession( sipOptions ) // Validate this sooner - if( !sip ) return Promise.reject("Cannot create session"); + if( !sip ) return Promise.reject("Cannot create session") - sip.onCallEnded.subscribe(cleanup); + sip.onCallEnded.subscribe(cleanup) // Call the C300X let remoteRtpDescription = await sip.call( @@ -171,9 +176,9 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid if( sip.sipOptions.debugSip ) this.log.d('SIP: Received remote SDP:\n' + remoteRtpDescription.sdp) - let sdp: string = replacePorts( remoteRtpDescription.sdp, 0, 0 ); - sdp = addTrackControls(sdp); - sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n'); + let sdp: string = replacePorts( remoteRtpDescription.sdp, 0, 0 ) + sdp = addTrackControls(sdp) + sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') if( sip.sipOptions.debugSip ) this.log.d('SIP: Updated SDP:\n' + sdp); @@ -186,64 +191,64 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid rtsp = new RtspServer(client, sdp, true); const parsedSdp = parseSdp(rtsp.sdp); - const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control; - const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control; + const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control + const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control if( sip.sipOptions.debugSip ) { - rtsp.console = this.console; + rtsp.console = this.console } await rtsp.handlePlayback(); sip.videoSplitter.on('message', message => { if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); + const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)) if (!isRtpMessage) - return; + return vseen++; - rtsp.sendTrack(videoTrack, message, !isRtpMessage); - const seq = getSequenceNumber(message); + rtsp.sendTrack(videoTrack, message, !isRtpMessage) + const seq = getSequenceNumber(message) if (seq !== (vseq + 1) % 0x0FFFF) - vlost++; - vseq = seq; + vlost++ + vseq = seq } }); sip.videoRtcpSplitter.on('message', message => { - rtsp.sendTrack(videoTrack, message, true); + rtsp.sendTrack(videoTrack, message, true) }); sip.audioSplitter.on('message', message => { if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)); + const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)) if (!isRtpMessage) return; aseen++; - rtsp.sendTrack(audioTrack, message, !isRtpMessage); - const seq = getSequenceNumber(message); + rtsp.sendTrack(audioTrack, message, !isRtpMessage) + const seq = getSequenceNumber(message) if (seq !== (aseq + 1) % 0x0FFFF) alost++; - aseq = seq; + aseq = seq } }); sip.audioRtcpSplitter.on('message', message => { - rtsp.sendTrack(audioTrack, message, true); + rtsp.sendTrack(audioTrack, message, true) }); - this.session = sip; + this.session = sip try { - await rtsp.handleTeardown(); - this.log.d('rtsp client ended'); + await rtsp.handleTeardown() + this.log.d('rtsp client ended') } catch (e) { this.log.e('rtsp client ended ungracefully' + e); } finally { - cleanup(); + cleanup() } } catch (e) { - sip?.stop(); + sip?.stop() throw e; } }); @@ -287,7 +292,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } async getDevice(nativeId: string) : Promise { - return new BticinoSipLock(this); + return new BticinoSipLock(this) } async releaseDevice(id: string, nativeId: string): Promise { diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index e26633f641..5be6affa28 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -15,10 +15,10 @@ export class VoicemailHandler extends SipMessageHandler { if( !this.sipCamera ) return if( this.isEnabled() ) { - this.sipCamera.console.info("Checking answering machine, cameraId: " + this.sipCamera.id ) + this.sipCamera.console.debug("Checking answering machine, cameraId: " + this.sipCamera.id ) this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) ) } else { - this.sipCamera.console.info("Answering machine check not enabled, cameraId: " + this.sipCamera.id ) + this.sipCamera.console.debug("Answering machine check not enabled, cameraId: " + this.sipCamera.id ) } //TODO: make interval customizable, now every 5 minutes this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index be42436b22..2efc9a6be9 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -21,7 +21,7 @@ export class SipHelper { throw new Error('SIP From/To/Domain URIs not specified!') } - let sipOptions : SipOptions = { + return { from: "sip:" + from, //TCP is more reliable for large messages, also see useTcp=true below to: "sip:" + to + ";transport=tcp", @@ -33,11 +33,10 @@ export class SipHelper { debugSip: sipdebug, useTcp: true, messageHandler: camera.messageHandler - }; - return sipOptions + } } - public static sipSession( camera : BticinoSipCamera ) : Promise { - return SipSession.createSipSession(console, "Bticino", this.sipOptions( camera ) ) + public static sipSession( sipOptions : SipOptions ) : Promise { + return SipSession.createSipSession(console, "Bticino", sipOptions ) } } \ No newline at end of file From bbedc0fe10ca097356d8d6ef32e1a1d70d72c8cc Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 16:13:57 +0100 Subject: [PATCH 07/24] Narrow down the event matching to deletes of devices --- plugins/bticino/src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 11f418e5e3..737c3ce969 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -1,4 +1,4 @@ -import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting } from '@scrypted/sdk'; +import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting } from '@scrypted/sdk'; import { randomBytes } from 'crypto'; import { BticinoSipCamera } from './bticino-camera'; @@ -12,7 +12,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid super() systemManager.listen( async (eventSource, eventDetails, eventData) => { - if( !eventSource ) { + if( !eventSource && ScryptedInterface.ScryptedDevice == eventDetails?.eventInterface && ScryptedInterfaceProperty.id == eventDetails.property ) { this.devices.forEach( (camera) => { if(camera?.id === eventData) { camera.voicemailHandler.cancelVoicemailCheck() From fd1bfc9937e5bca4114f347590f1b6a2eeda6505 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Tue, 17 Jan 2023 18:35:43 +0100 Subject: [PATCH 08/24] Use releaseDevice to clean up stale entries --- .../bticino/src/bticino-voicemailHandler.ts | 4 +- plugins/bticino/src/main.ts | 48 ++++++++----------- plugins/sip/src/main.ts | 9 +++- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index 5be6affa28..2ed2890c36 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -1,5 +1,5 @@ -import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call"; -import { BticinoSipCamera } from "./bticino-camera"; +import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call" +import { BticinoSipCamera } from "./bticino-camera" export class VoicemailHandler extends SipMessageHandler { private sipCamera : BticinoSipCamera diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 737c3ce969..3e410f3b7b 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -1,29 +1,12 @@ -import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting } from '@scrypted/sdk'; -import { randomBytes } from 'crypto'; -import { BticinoSipCamera } from './bticino-camera'; +import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting } from '@scrypted/sdk' +import { randomBytes } from 'crypto' +import { BticinoSipCamera } from './bticino-camera' -const { systemManager, deviceManager, mediaManager } = sdk; +const { systemManager, deviceManager, mediaManager } = sdk export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { - devices = new Map(); - - constructor() { - super() - systemManager.listen( - async (eventSource, eventDetails, eventData) => { - if( !eventSource && ScryptedInterface.ScryptedDevice == eventDetails?.eventInterface && ScryptedInterfaceProperty.id == eventDetails.property ) { - this.devices.forEach( (camera) => { - if(camera?.id === eventData) { - camera.voicemailHandler.cancelVoicemailCheck() - if( this.devices.delete(camera.nativeId) ) { - this.console.log("Removed device from list: " + eventData) - } - } - } ) - } - }); - } + devices = new Map() async getCreateDeviceSettings(): Promise { return [ @@ -38,7 +21,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid async createDevice(settings: DeviceCreatorSettings): Promise { const nativeId = randomBytes(4).toString('hex') const name = settings.newCamera?.toString() - const camera = await this.updateDevice(nativeId, name); + const camera = await this.updateDevice(nativeId, name) const device: Device = { providerNativeId: nativeId, @@ -52,12 +35,12 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid name: name + ' Lock', type: ScryptedDeviceType.Lock, interfaces: [ScryptedInterface.Lock], - }; + } const ret = await deviceManager.onDevicesChanged({ providerNativeId: nativeId, devices: [device], - }); + }) let sipCamera : BticinoSipCamera = await this.getDevice(nativeId) let foo : BticinoSipCamera = systemManager.getDeviceById(sipCamera.id) @@ -65,7 +48,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid let lock = await sipCamera.getDevice(undefined) lock.lockState = LockState.Locked - return nativeId; + return nativeId } updateDevice(nativeId: string, name: string) { @@ -92,13 +75,20 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid async getDevice(nativeId: string): Promise { if (!this.devices.has(nativeId)) { - const camera = new BticinoSipCamera(nativeId, this); - this.devices.set(nativeId, camera); + const camera = new BticinoSipCamera(nativeId, this) + this.devices.set(nativeId, camera) } - return this.devices.get(nativeId); + return this.devices.get(nativeId) } async releaseDevice(id: string, nativeId: string): Promise { + let camera = this.devices.get(nativeId) + if( camera ) { + camera.voicemailHandler.cancelVoicemailCheck() + if( this.devices.delete( nativeId ) ) { + this.console.log("Removed device from list: " + id + " / " + nativeId ) + } + } } } diff --git a/plugins/sip/src/main.ts b/plugins/sip/src/main.ts index 703b021442..13066251d2 100644 --- a/plugins/sip/src/main.ts +++ b/plugins/sip/src/main.ts @@ -437,8 +437,7 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider } } - async releaseDevice(id: string, nativeId: string): Promise { - } + async createDevice(settings: DeviceCreatorSettings): Promise { const nativeId = randomBytes(4).toString('hex'); @@ -482,6 +481,12 @@ export class SipCamProvider extends ScryptedDeviceBase implements DeviceProvider return ret; } + async releaseDevice(id: string, nativeId: string): Promise { + if( this.devices.delete( nativeId ) ) { + this.console.log("Removed device from list: " + id + " / " + nativeId ) + } + } + createCamera(nativeId: string): SipCamera { return new SipCamera(nativeId, this); } From 7480fe156ba7b06f34d5364e024fdc84e336ff0c Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Wed, 18 Jan 2023 17:26:10 +0100 Subject: [PATCH 09/24] Fix uuid version --- plugins/sip/package-lock.json | 2 -- plugins/sip/package.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 24efac0e12..1cd05c7213 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -22,7 +22,6 @@ } }, "../../common": { - "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -38,7 +37,6 @@ } }, "../../sdk": { - "name": "@scrypted/sdk", "version": "0.2.55", "dev": true, "license": "ISC", diff --git a/plugins/sip/package.json b/plugins/sip/package.json index f3dcab0c3b..59f498d58a 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -37,7 +37,7 @@ "sdp": "^3.0.3", "sip": "0.0.6", "stun": "^2.1.0", - "uuid": "^8.3.4" + "uuid": "^8.3.2" }, "devDependencies": { "@scrypted/common": "file:../../common", From 0925fcc3efb631ad7d8d52ba9fd0be1d24f41ffa Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Wed, 18 Jan 2023 17:35:59 +0100 Subject: [PATCH 10/24] Attempt to make two way audio work --- plugins/bticino/src/bticino-camera.ts | 76 +++++++++++++++++++++++---- plugins/bticino/src/main.ts | 2 +- plugins/sip/package-lock.json | 4 +- plugins/sip/package.json | 2 +- plugins/sip/src/rtp-utils.ts | 3 +- plugins/sip/src/sip-call.ts | 6 ++- plugins/sip/src/sip-session.ts | 10 +++- 7 files changed, 83 insertions(+), 20 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 396955fc28..ff9e895f3a 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -1,27 +1,38 @@ -import { listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; +import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; import { SipSession } from '../../sip/src/sip-session'; -import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType } from '../../sip/src/rtp-utils'; +import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, RtpDescription } from '../../sip/src/rtp-utils'; import { VoicemailHandler } from './bticino-voicemailHandler'; import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; +import { decodeSrtpOptions, encodeSrtpOptions, SrtpOptions } from '../../ring/src/srtp-utils' import { sleep } from '@scrypted/common/src/sleep'; import { SipHelper } from './sip-helper'; +import child_process, { ChildProcess } from 'child_process'; +import dgram from 'dgram'; import { BticinoStorageSettings } from './storage-settings'; -import mediaManager, { BticinoSipPlugin } from './main'; +import { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; +import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; const STREAM_TIMEOUT = 50000; +const { mediaManager } = sdk; export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { private session: SipSession + private remoteRtpDescription: RtpDescription + private audioOutForwarder: dgram.Socket + private audioOutProcess: ChildProcess private currentMedia: FFmpegInput | MediaStreamUrl private currentMediaMimeType: string private refreshTimeout: NodeJS.Timeout public messageHandler: CompositeSipMessageHandler private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) + //TODO: randomize this + private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" + private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) @@ -76,11 +87,54 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } async startIntercom(media: MediaObject): Promise { - this.log.d( "TODO: startIntercom" + media ) + if (!this.session) + throw new Error("not in call"); + + this.stopIntercom(); + + const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString()); + + const rtpOptions = this.remoteRtpDescription + const audioOutForwarder = await createBindZero() + this.audioOutForwarder = audioOutForwarder.server + audioOutForwarder.server.on('message', message => { + if( this.session ) + this.session.audioSplitter.send(message, rtpOptions.audio.port, rtpOptions.address) + return null + }); + + const args = ffmpegInput.inputArguments.slice(); + args.push( + '-vn', '-dn', '-sn', + '-acodec', 'speex', + '-flags', '+global_header', + '-ac', '1', + '-ar', '8k', + '-f', 'rtp', + '-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', + '-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions), + `srtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`, + ); + + this.console.log("===========================================") + safePrintFFmpegArguments( this.console, args ) + this.console.log("===========================================") + + const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args); + ffmpegLogInitialOutput(this.console, cp) + this.audioOutProcess = cp; + cp.on('exit', () => this.console.log('two way audio ended')); + this.session.onCallEnded.subscribe(() => { + closeQuiet(audioOutForwarder.server); + safeKillFFmpeg(cp) + }); } async stopIntercom(): Promise { - this.log.d( "TODO: stopIntercom" ) + closeQuiet(this.audioOutForwarder) + this.audioOutProcess?.kill('SIGKILL') + this.audioOutProcess = undefined + this.audioOutForwarder = undefined } resetStreamTimeout() { @@ -156,27 +210,27 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid sip.onCallEnded.subscribe(cleanup) // Call the C300X - let remoteRtpDescription = await sip.call( + this.remoteRtpDescription = await sip.call( ( audio ) => { return [ 'a=DEVADDR:20', // Needed for bt_answering_machine (bticino specific) `m=audio ${audio.port} RTP/SAVP 97`, `a=rtpmap:97 speex/8000`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, ] }, ( video ) => { return [ `m=video ${video.port} RTP/SAVP 97`, `a=rtpmap:97 H264/90000`, `a=fmtp:97 profile-level-id=42801F`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, 'a=recvonly' ] } ); if( sip.sipOptions.debugSip ) - this.log.d('SIP: Received remote SDP:\n' + remoteRtpDescription.sdp) + this.log.d('SIP: Received remote SDP:\n' + this.remoteRtpDescription.sdp) - let sdp: string = replacePorts( remoteRtpDescription.sdp, 0, 0 ) + let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) sdp = addTrackControls(sdp) sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') if( sip.sipOptions.debugSip ) diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 3e410f3b7b..9ddab9c54a 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -2,7 +2,7 @@ import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Lock import { randomBytes } from 'crypto' import { BticinoSipCamera } from './bticino-camera' -const { systemManager, deviceManager, mediaManager } = sdk +const { systemManager, deviceManager } = sdk export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 1cd05c7213..958be0ff7e 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sip", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sip", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "dependencies": { "@homebridge/camera-utils": "^2.0.4", "sdp": "^3.0.3", diff --git a/plugins/sip/package.json b/plugins/sip/package.json index 59f498d58a..f416891703 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sip", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/sip/src/rtp-utils.ts b/plugins/sip/src/rtp-utils.ts index 1c48dcc176..360835ddff 100644 --- a/plugins/sip/src/rtp-utils.ts +++ b/plugins/sip/src/rtp-utils.ts @@ -1,10 +1,11 @@ // by @dgrief from @homebridge/camera-utils +import { SrtpOptions } from '@homebridge/camera-utils' import dgram from 'dgram' const stun = require('stun') const stunMagicCookie = 0x2112a442 // https://tools.ietf.org/html/rfc5389#section-6 -export interface RtpStreamOptions { +export interface RtpStreamOptions extends SrtpOptions { port: number rtcpPort: number } diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index 6a6ab68caf..2f8d2fed4a 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -87,7 +87,9 @@ function getRtpDescription( if( section === undefined ) { return { port: 0, - rtcpPort: 0 + rtcpPort: 0, + srtpKey: undefined, + srtpSalt: undefined }; } @@ -115,7 +117,7 @@ function getRtpDescription( ssrc: (ssrcLine && Number(ssrcLine.match(/ssrc:(\S*)/)?.[1])) || undefined, iceUFrag: (iceUFragLine && iceUFragLine.match(/ice-ufrag:(\S*)/)?.[1]) || undefined, icePwd: (icePwdLine && icePwdLine.match(/ice-pwd:(\S*)/)?.[1]) || undefined, - ...(encodedCrypto? decodeSrtpOptions(encodedCrypto) : {}) + ...(encodedCrypto? decodeSrtpOptions(encodedCrypto) : { srtpKey: undefined, srtpSalt: undefined }) } } catch (e) { console.error('Failed to parse SDP from remote end') diff --git a/plugins/sip/src/sip-session.ts b/plugins/sip/src/sip-session.ts index e146a1e056..5d65ec1d66 100644 --- a/plugins/sip/src/sip-session.ts +++ b/plugins/sip/src/sip-session.ts @@ -36,11 +36,17 @@ export class SipSession extends Subscribed { rtpOptions : RtpOptions = { audio: { port: audioSplitter.port, - rtcpPort: audioRtcpSplitter.port + rtcpPort: audioRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined }, video: { port: videoSplitter.port, - rtcpPort: videoRtcpSplitter.port + rtcpPort: videoRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined } } From fd276fad84ec5f85e86e5ed44b849b36b30740a5 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Wed, 18 Jan 2023 21:03:06 +0100 Subject: [PATCH 11/24] Attempt to make two way audio work - fine tuning --- plugins/bticino/package-lock.json | 4 ++-- plugins/bticino/package.json | 2 +- plugins/bticino/src/bticino-camera.ts | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 29f8209a84..7589b17e3a 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "dependencies": { "sdp": "^3.0.3", "sip": "0.0.6", diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index a252cfaff5..9920bc25df 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.1", + "version": "0.0.6-beta.2", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index ff9e895f3a..ed0c48693c 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -16,7 +16,7 @@ import { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; -const STREAM_TIMEOUT = 50000; +const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { @@ -27,7 +27,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private currentMedia: FFmpegInput | MediaStreamUrl private currentMediaMimeType: string private refreshTimeout: NodeJS.Timeout - public messageHandler: CompositeSipMessageHandler + public messageHandler: CompositeSipMessageHandler = new CompositeSipMessageHandler() private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) //TODO: randomize this @@ -36,7 +36,6 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) - this.messageHandler = new CompositeSipMessageHandler() this.messageHandler.add( this.voicemailHandler ) } From 7e62f4dfcc60a98f8c7f6ececd63c5337d57ce23 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Mon, 23 Jan 2023 19:13:09 +0100 Subject: [PATCH 12/24] Enable incoming doorbell events --- plugins/bticino/src/bticino-camera.ts | 43 +++++++---------- plugins/bticino/src/bticino-inviteHandler.ts | 20 ++++++++ .../bticino/src/bticino-voicemailHandler.ts | 15 +++--- plugins/bticino/src/sip-helper.ts | 4 +- plugins/bticino/src/sip-registered-session.ts | 47 +++++++++++++++++++ plugins/sip/src/compositeSipMessageHandler.ts | 8 ++-- plugins/sip/src/sip-call.ts | 16 ++++--- 7 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 plugins/bticino/src/bticino-inviteHandler.ts create mode 100644 plugins/bticino/src/sip-registered-session.ts diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index ed0c48693c..1b55e181a2 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -15,6 +15,8 @@ import { BticinoStorageSettings } from './storage-settings'; import { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; +import { SipRegisteredSession } from './sip-registered-session'; +import { InviteHandler } from './bticino-inviteHandler'; const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; @@ -27,46 +29,37 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private currentMedia: FFmpegInput | MediaStreamUrl private currentMediaMimeType: string private refreshTimeout: NodeJS.Timeout - public messageHandler: CompositeSipMessageHandler = new CompositeSipMessageHandler() + public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler() private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) + private inviteHandler : InviteHandler = new InviteHandler(this) //TODO: randomize this private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) + private persistentSipSession : SipRegisteredSession constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) - this.messageHandler.add( this.voicemailHandler ) + this.requestHandlers.add( this.voicemailHandler ) + this.requestHandlers.add( this.inviteHandler ) + this.persistentSipSession = new SipRegisteredSession( this ) } sipUnlock(): Promise { this.log.i("unlocking C300X door ") - return SipHelper.sipSession( SipHelper.sipOptions( this ) ) - .then( ( sip ) => { - sip.sipCall.register() - .then( () => - sip.sipCall.message( '*8*19*20##' ) - .then( () => - sleep(1000) - .then( () => sip.sipCall.message( '*8*20*20##' ) ) - ) - .catch( () => {} ) - .finally( () => sip.sipCall.destroy() ) - ) - .catch( e => this.console.error() ) - } ) - .catch( e => this.console.error(e) ) + return this.persistentSipSession.enable().then( (sipCall) => { + sipCall.message( '*8*19*20##' ) + .then( () => + sleep(1000) + .then( () => sipCall.message( '*8*20*20##' ) ) + ) + } ) } getAswmStatus() : Promise { - return SipHelper.sipSession( SipHelper.sipOptions( this ) ) - .then( ( sip ) => { - sip.sipCall.register() - .then( () => sip.sipCall.message( "GetAswmStatus!") ) - .catch( () => {}) - .finally( () => sip.sipCall.destroy() ) - } ) - .catch( e => this.console.error(e) ) + return this.persistentSipSession.enable().then( (sipCall) => { + sipCall.message( "GetAswmStatus!" ) + } ) } async takePicture(option?: PictureOptions): Promise { diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts new file mode 100644 index 0000000000..5a5efb3efa --- /dev/null +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -0,0 +1,20 @@ +import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call" +import { BticinoSipCamera } from "./bticino-camera" + +export class InviteHandler extends SipRequestHandler { + constructor( private sipCamera : BticinoSipCamera ) { + super() + } + + handle(request: SipRequest) { + if( request.method === 'INVITE' ) { + this.sipCamera.console.log("INCOMING voice call from: " + request.headers.from ) + this.sipCamera.binaryState = true + + setTimeout( () => { + // Assumption that flexisip only holds this call active for 20 seconds ... might be revised + this.sipCamera.binaryState = false + }, 20 * 1000 ) + } + } +} \ No newline at end of file diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index 2ed2890c36..448aaa687e 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -1,14 +1,15 @@ -import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call" +import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call" import { BticinoSipCamera } from "./bticino-camera" -export class VoicemailHandler extends SipMessageHandler { - private sipCamera : BticinoSipCamera +export class VoicemailHandler extends SipRequestHandler { private timeout : NodeJS.Timeout - constructor( sipCamera : BticinoSipCamera ) { + constructor( private sipCamera : BticinoSipCamera ) { super() - this.sipCamera = sipCamera - this.checkVoicemail() + setTimeout( () => { + // Delay a bit an run in a different thread in case this fails + this.checkVoicemail() + }, 10000 ) } checkVoicemail() { @@ -21,7 +22,7 @@ export class VoicemailHandler extends SipMessageHandler { this.sipCamera.console.debug("Answering machine check not enabled, cameraId: " + this.sipCamera.id ) } //TODO: make interval customizable, now every 5 minutes - this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) + this.timeout = setTimeout( () => this.checkVoicemail() , 60 * 1000 ) } cancelVoicemailCheck() { diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index 2efc9a6be9..57ff659bb6 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -13,7 +13,7 @@ export class SipHelper { const localIp = from?.split(':')[0].split('@')[1] const localPort = parseInt(from?.split(':')[1]) || 5060 const domain = camera.storage.getItem('sipdomain')?.trim() - const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '3600' + const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '600' const sipdebug : boolean = camera.storage.getItem('sipdebug')?.toLocaleLowerCase() === 'true' || false if (!from || !to || !localIp || !localPort || !domain || !expiration ) { @@ -32,7 +32,7 @@ export class SipHelper { shouldRegister: true, debugSip: sipdebug, useTcp: true, - messageHandler: camera.messageHandler + sipRequestHandler: camera.requestHandlers } } diff --git a/plugins/bticino/src/sip-registered-session.ts b/plugins/bticino/src/sip-registered-session.ts new file mode 100644 index 0000000000..2aec7b1b8d --- /dev/null +++ b/plugins/bticino/src/sip-registered-session.ts @@ -0,0 +1,47 @@ +import { SipSession } from "../../sip/src/sip-session"; +import { BticinoSipCamera } from "./bticino-camera"; +import { SipHelper } from "./sip-helper"; +import { SipCall, SipOptions } from "../../sip/src/sip-call"; + +/** + * This class registers itself with the SIP server as a contact for a user account. + * The registration expires after the expires time in sipOptions is reached. + * The sip session will re-register itself after the expires time is reached. + */ +export class SipRegisteredSession { + private currentSipSession : SipSession + + constructor( private camera : BticinoSipCamera ) { + // Give it a second + setTimeout( () => this.enable(), 10 * 1000 ) + } + + async enable() : Promise { + if( this.currentSipSession ) { + return this.currentSipSession.sipCall + } + let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) + + if( sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { + // Safe guard just in case + sipOptions.expire = 300 + } + + setTimeout( () => { + this.currentSipSession?.stop() + this.currentSipSession = undefined + this.enable() + }, sipOptions.expire * 1000 ) + + try { + this.currentSipSession = await SipHelper.sipSession( sipOptions ) + await this.currentSipSession.sipCall.register() + return this.currentSipSession.sipCall + } catch(e) { + this.currentSipSession?.stop() + this.currentSipSession = undefined + this.camera.console.error("Error enabling SIP session: " + e ) + throw e + } + } +} \ No newline at end of file diff --git a/plugins/sip/src/compositeSipMessageHandler.ts b/plugins/sip/src/compositeSipMessageHandler.ts index e0a055cf4c..f206db4a52 100644 --- a/plugins/sip/src/compositeSipMessageHandler.ts +++ b/plugins/sip/src/compositeSipMessageHandler.ts @@ -1,14 +1,14 @@ -import { SipMessageHandler, SipRequest } from "../../sip/src/sip-call"; +import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call"; -export class CompositeSipMessageHandler extends SipMessageHandler { - private handlers : SipMessageHandler[] = [] +export class CompositeSipMessageHandler extends SipRequestHandler { + private handlers : SipRequestHandler[] = [] constructor() { super() } handle(request: SipRequest) { this.handlers.forEach( (handler) => handler.handle( request ) ) } - add( handler : SipMessageHandler ) { + add( handler : SipRequestHandler ) { this.handlers.push( handler ) } } \ No newline at end of file diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-call.ts index 2f8d2fed4a..97cbb76af4 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-call.ts @@ -17,14 +17,14 @@ export interface SipOptions { localPort: number debugSip?: boolean useTcp?: boolean - messageHandler?: SipMessageHandler + sipRequestHandler?: SipRequestHandler shouldRegister?: boolean } /** * Allows handling of SIP messages */ -export abstract class SipMessageHandler { +export abstract class SipRequestHandler { abstract handle( request: SipRequest ) } @@ -206,18 +206,20 @@ export class SipCall { if( m.method == 'REGISTER' ) { m.uri = "sip:" + sipOptions.domain + m.headers.to.uri = fromWithDomain } else if( m.method == 'INVITE' || m.method == 'MESSAGE' ) { m.uri = toWithDomain + m.headers.to.uri = toWithDomain if( m.method == 'MESSAGE' && m.headers.to ) { m.headers.to.params = null; } } else if( m.method == 'ACK' || m.method == 'BYE' ) { + m.headers.to.uri = toWithDomain m.uri = this.registrarContact } else { throw new Error("Error: Method construct for uri not implemented: " + m.method) } - m.headers.to.uri = toWithDomain m.headers.from.uri = fromWithDomain if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); @@ -244,16 +246,18 @@ export class SipCall { if (this.destroyed) { this.onEndedByRemote.next(null) } - } else if( request.method === 'MESSAGE' && sipOptions.messageHandler ) { - sipOptions.messageHandler.handle( request ) + } else if( request.method === 'MESSAGE' && sipOptions.sipRequestHandler ) { + sipOptions.sipRequestHandler.handle( request ) this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) + } else if( request.method === 'INVITE' && sipOptions.sipRequestHandler ) { + sipOptions.sipRequestHandler.handle( request ) } else { if( sipOptions.debugSip ) { this.console.warn("unimplemented method received from remote: " + request.method) } } } - ) + ) } } From 3177c429735aa62c36bef4fea0dd72c34e96f5d7 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Thu, 2 Feb 2023 19:17:35 +0100 Subject: [PATCH 13/24] SipCall was never a "sip call" but more like a manager SipSession was more the "sip call" --- plugins/bticino/package-lock.json | 4 +- plugins/bticino/package.json | 2 +- plugins/bticino/src/bticino-camera.ts | 6 +- plugins/bticino/src/bticino-inviteHandler.ts | 2 +- .../bticino/src/bticino-voicemailHandler.ts | 2 +- plugins/bticino/src/sip-helper.ts | 8 +- plugins/bticino/src/sip-registered-session.ts | 8 +- plugins/sip/src/compositeSipMessageHandler.ts | 2 +- plugins/sip/src/main.ts | 30 +- .../{sip-session.ts => sip-call-session.ts} | 387 ++++---- .../sip/src/{sip-call.ts => sip-manager.ts} | 876 +++++++++--------- 11 files changed, 665 insertions(+), 662 deletions(-) rename plugins/sip/src/{sip-session.ts => sip-call-session.ts} (89%) rename plugins/sip/src/{sip-call.ts => sip-manager.ts} (96%) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 7589b17e3a..242c799cc4 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "dependencies": { "sdp": "^3.0.3", "sip": "0.0.6", diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index 9920bc25df..c168148695 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 1b55e181a2..d0ef4591c8 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -2,7 +2,7 @@ import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/co import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; -import { SipSession } from '../../sip/src/sip-session'; +import { SipCallSession } from '../../sip/src/sip-call-session'; import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, RtpDescription } from '../../sip/src/rtp-utils'; import { VoicemailHandler } from './bticino-voicemailHandler'; import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; @@ -22,7 +22,7 @@ const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { - private session: SipSession + private session: SipCallSession private remoteRtpDescription: RtpDescription private audioOutForwarder: dgram.Socket private audioOutProcess: ChildProcess @@ -171,7 +171,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid playbackPromise.then(async (client) => { client.setKeepAlive(true, 10000) - let sip: SipSession + let sip: SipCallSession try { let rtsp: RtspServer; const cleanup = () => { diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index 5a5efb3efa..2909f50de4 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -1,4 +1,4 @@ -import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call" +import { SipRequestHandler, SipRequest } from "../../sip/src/sip-manager" import { BticinoSipCamera } from "./bticino-camera" export class InviteHandler extends SipRequestHandler { diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index 448aaa687e..5932a7cc9c 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -1,4 +1,4 @@ -import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call" +import { SipRequestHandler, SipRequest } from "../../sip/src/sip-manager" import { BticinoSipCamera } from "./bticino-camera" export class VoicemailHandler extends SipRequestHandler { diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index 57ff659bb6..f40bc35386 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -1,5 +1,5 @@ -import { SipOptions } from "../../sip/src/sip-call"; -import { SipSession } from "../../sip/src/sip-session"; +import { SipOptions } from "../../sip/src/sip-manager"; +import { SipCallSession } from "../../sip/src/sip-call-session"; import { BticinoSipCamera } from "./bticino-camera"; export class SipHelper { @@ -36,7 +36,7 @@ export class SipHelper { } } - public static sipSession( sipOptions : SipOptions ) : Promise { - return SipSession.createSipSession(console, "Bticino", sipOptions ) + public static sipSession( sipOptions : SipOptions ) : Promise { + return SipCallSession.createCallSession(console, "Bticino", sipOptions ) } } \ No newline at end of file diff --git a/plugins/bticino/src/sip-registered-session.ts b/plugins/bticino/src/sip-registered-session.ts index 2aec7b1b8d..c8d85d6442 100644 --- a/plugins/bticino/src/sip-registered-session.ts +++ b/plugins/bticino/src/sip-registered-session.ts @@ -1,7 +1,7 @@ -import { SipSession } from "../../sip/src/sip-session"; +import { SipCallSession } from "../../sip/src/sip-call-session"; import { BticinoSipCamera } from "./bticino-camera"; import { SipHelper } from "./sip-helper"; -import { SipCall, SipOptions } from "../../sip/src/sip-call"; +import { SipManager, SipOptions } from "../../sip/src/sip-manager"; /** * This class registers itself with the SIP server as a contact for a user account. @@ -9,14 +9,14 @@ import { SipCall, SipOptions } from "../../sip/src/sip-call"; * The sip session will re-register itself after the expires time is reached. */ export class SipRegisteredSession { - private currentSipSession : SipSession + private currentSipSession : SipCallSession constructor( private camera : BticinoSipCamera ) { // Give it a second setTimeout( () => this.enable(), 10 * 1000 ) } - async enable() : Promise { + async enable() : Promise { if( this.currentSipSession ) { return this.currentSipSession.sipCall } diff --git a/plugins/sip/src/compositeSipMessageHandler.ts b/plugins/sip/src/compositeSipMessageHandler.ts index f206db4a52..a01154102d 100644 --- a/plugins/sip/src/compositeSipMessageHandler.ts +++ b/plugins/sip/src/compositeSipMessageHandler.ts @@ -1,4 +1,4 @@ -import { SipRequestHandler, SipRequest } from "../../sip/src/sip-call"; +import { SipRequestHandler, SipRequest } from "./sip-manager"; export class CompositeSipMessageHandler extends SipRequestHandler { private handlers : SipRequestHandler[] = [] diff --git a/plugins/sip/src/main.ts b/plugins/sip/src/main.ts index 13066251d2..1061a208f4 100644 --- a/plugins/sip/src/main.ts +++ b/plugins/sip/src/main.ts @@ -5,8 +5,8 @@ import child_process, { ChildProcess } from 'child_process'; import { ffmpegLogInitialOutput, safePrintFFmpegArguments } from "@scrypted/common/src/media-helpers"; import dgram from 'dgram'; import net from 'net'; -import { SipSession } from './sip-session'; -import { SipOptions } from './sip-call'; +import { SipCallSession } from './sip-call-session'; +import { SipOptions } from './sip-manager'; import { RtpDescription, isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType } from './rtp-utils'; import { randomBytes } from "crypto"; @@ -14,7 +14,7 @@ const { deviceManager, mediaManager } = sdk; class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCamera, Settings, BinarySensor { buttonTimeout: NodeJS.Timeout; - session: SipSession; + callSession: SipCallSession; remoteRtpDescription: RtpDescription; audioOutForwarder: dgram.Socket; audioOutProcess: ChildProcess; @@ -107,7 +107,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam await this.callDoorbell(); - if (!this.session) + if (!this.callSession) throw new Error("not in call"); this.stopAudioOut(); @@ -118,7 +118,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam const audioOutForwarder = await createBindZero(); this.audioOutForwarder = audioOutForwarder.server; audioOutForwarder.server.on('message', message => { - this.session.audioSplitter.send(message, remoteRtpDescription.audio.port, remoteRtpDescription.address); + this.callSession.audioSplitter.send(message, remoteRtpDescription.audio.port, remoteRtpDescription.address); return null; }); @@ -136,7 +136,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args); this.audioOutProcess = cp; cp.on('exit', () => this.console.log('two way audio ended')); - this.session.onCallEnded.subscribe(() => { + this.callSession.onCallEnded.subscribe(() => { closeQuiet(audioOutForwarder.server); cp.kill('SIGKILL'); }); @@ -157,19 +157,19 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam stopSession() { this.doorbellAudioActive = false; this.audioInProcess?.kill('SIGKILL'); - if (this.session) { + if (this.callSession) { this.console.log('ending sip session'); - this.session.stop(); - this.session = undefined; + this.callSession.stop(); + this.callSession = undefined; } } async callDoorbell(): Promise { - let sip: SipSession; + let sip: SipCallSession; const cleanup = () => { - if (this.session === sip) - this.session = undefined; + if (this.callSession === sip) + this.callSession = undefined; try { this.console.log('stopping sip session.'); sip.stop(); @@ -193,7 +193,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam let sipOptions: SipOptions = { from: "sip:" + from, to: "sip:" + to, localIp, localPort }; - sip = await SipSession.createSipSession(this.console, this.name, sipOptions); + sip = await SipCallSession.createCallSession(this.console, this.name, sipOptions); sip.onCallEnded.subscribe(cleanup); this.remoteRtpDescription = await sip.call( ( audio ) => { @@ -206,7 +206,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam ); this.console.log('SIP: Received remote SDP:\n', this.remoteRtpDescription.sdp) - let [rtpPort, rtcpPort] = await SipSession.reserveRtpRtcpPorts() + let [rtpPort, rtcpPort] = await SipCallSession.reserveRtpRtcpPorts() this.console.log(`Reserved RTP port ${rtpPort} and RTCP port ${rtcpPort} for incoming SIP audio`); const ffmpegPath = await mediaManager.getFFmpegPath(); @@ -260,7 +260,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam sip.audioRtcpSplitter.send(message, rtcpPort, "127.0.0.1"); }); - this.session = sip; + this.callSession = sip; } getRawVideoStreamOptions(): ResponseMediaStreamOptions[] { diff --git a/plugins/sip/src/sip-session.ts b/plugins/sip/src/sip-call-session.ts similarity index 89% rename from plugins/sip/src/sip-session.ts rename to plugins/sip/src/sip-call-session.ts index 5d65ec1d66..5399002870 100644 --- a/plugins/sip/src/sip-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -1,193 +1,196 @@ -import { reservePorts } from '@homebridge/camera-utils'; -import { createBindUdp, createBindZero } from '@scrypted/common/src/listen-cluster'; -import dgram from 'dgram'; -import { ReplaySubject, timer } from 'rxjs'; -import { createStunResponder, RtpDescription, RtpOptions, sendStunBindingRequest } from './rtp-utils'; -import { SipCall, SipOptions } from './sip-call'; -import { Subscribed } from './subscribed'; - -export class SipSession extends Subscribed { - private hasStarted = false - private hasCallEnded = false - private onCallEndedSubject = new ReplaySubject(1) - public sipCall: SipCall - onCallEnded = this.onCallEndedSubject.asObservable() - - constructor( - public readonly console: Console, - public readonly sipOptions: SipOptions, - public readonly rtpOptions: RtpOptions, - public readonly audioSplitter: dgram.Socket, - public audioRtcpSplitter: dgram.Socket, - public readonly videoSplitter: dgram.Socket, - public videoRtcpSplitter: dgram.Socket, - public readonly cameraName: string - ) { - super() - - this.sipCall = this.createSipCall(this.sipOptions) - } - - static async createSipSession(console: any, cameraName: string, sipOptions: SipOptions) { - const audioSplitter = await createBindZero(), - audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), - videoSplitter = await createBindZero(), - videoRtcpSplitter = await createBindUdp(videoSplitter.port + 1), - rtpOptions : RtpOptions = { - audio: { - port: audioSplitter.port, - rtcpPort: audioRtcpSplitter.port, - //TODO: make this cleaner - srtpKey: undefined, - srtpSalt: undefined - }, - video: { - port: videoSplitter.port, - rtcpPort: videoRtcpSplitter.port, - //TODO: make this cleaner - srtpKey: undefined, - srtpSalt: undefined - } - } - - return new SipSession( - console, - sipOptions, - rtpOptions, - audioSplitter.server, - audioRtcpSplitter.server, - videoSplitter.server, - videoRtcpSplitter.server, - cameraName - ) - } - - createSipCall(sipOptions: SipOptions) { - if (this.sipCall) { - this.sipCall.destroy() - } - - const call = (this.sipCall = new SipCall( - this.console, - sipOptions, - this.rtpOptions - )) - - this.addSubscriptions( - call.onEndedByRemote.subscribe(() => this.callEnded(false)) - ) - - return this.sipCall - } - - async call( audioSection, videoSection? ): Promise { - this.console.log(`SipSession::start()`); - - if (this.hasStarted) { - throw new Error('SIP Session has already been started') - } - this.hasStarted = true - - if (this.hasCallEnded) { - throw new Error('SIP Session has already ended') - } - - - try { - if( this.sipOptions.shouldRegister ) - await this.sipCall.register() - const rtpDescription : RtpDescription = await this.sipCall.invite( audioSection, videoSection ), - sendStunRequests = () => { - sendStunBindingRequest({ - rtpSplitter: this.audioSplitter, - rtcpSplitter: this.audioRtcpSplitter, - rtpDescription, - localUfrag: this.sipCall.audioUfrag, - type: 'audio', - }) - sendStunBindingRequest({ - rtpSplitter: this.videoSplitter, - rtcpSplitter: this.videoRtcpSplitter, - rtpDescription, - localUfrag: this.sipCall.videoUfrag, - type: 'video', - }) - } - - // if rtcp-mux is supported, rtp splitter will be used for both rtp and rtcp - if ( rtpDescription.audio.port > 0 && rtpDescription.audio.port === rtpDescription.audio.rtcpPort) { - this.audioRtcpSplitter.close() - this.audioRtcpSplitter = this.audioSplitter - } - - if ( rtpDescription.video.port > 0 && rtpDescription.video.port === rtpDescription.video.rtcpPort) { - this.videoRtcpSplitter.close() - this.videoRtcpSplitter = this.videoSplitter - } - - if ( (rtpDescription.audio.port > 0 && rtpDescription.audio.iceUFrag)|| (rtpDescription.video.port > 0 && rtpDescription.video.iceUFrag ) ) { - // ICE is supported - this.console.log(`Connecting to ${this.cameraName} using ICE`) - if( rtpDescription.audio.port > 0 ) { - createStunResponder(this.audioSplitter) - } - if( rtpDescription.video.port > 0 ) { - createStunResponder(this.videoSplitter) - } - - sendStunRequests() - } else { - // ICE is not supported, use stun as keep alive - this.console.log(`Connecting to ${this.cameraName} using STUN`) - this.addSubscriptions( - // hole punch every .5 seconds to keep stream alive and port open (matches behavior from Ring app) - timer(0, 500).subscribe(sendStunRequests) - ) - } - - this.audioSplitter.once('message', () => { - this.console.log(`Audio stream latched for ${this.cameraName}, port: ${this.rtpOptions.audio.port}`) - }) - - this.videoSplitter.once('message', () => { - this.console.log(`Video stream latched for ${this.cameraName}, port: ${this.rtpOptions.video.port}`) - }) - - return rtpDescription - } catch (e) { - this.callEnded(true) - throw e - } - } - - static async reserveRtpRtcpPorts() { - const ports = await reservePorts({ count: 4, type: 'udp' }) - return ports - } - - private async callEnded(sendBye: boolean) { - if (this.hasCallEnded) { - return - } - this.hasCallEnded = true - - if (sendBye) { - await this.sipCall.sendBye().catch(this.console.error) - } - - // clean up - this.console.log("sip-session callEnded") - this.onCallEndedSubject.next(null) - this.sipCall.destroy() - this.audioSplitter.close() - this.audioRtcpSplitter.close() - this.videoSplitter.close() - this.videoRtcpSplitter.close() - this.unsubscribe() - this.console.log("sip-session callEnded: done") - } - - async stop() { - await this.callEnded(true) - } +import { reservePorts } from '@homebridge/camera-utils'; +import { createBindUdp, createBindZero } from '@scrypted/common/src/listen-cluster'; +import dgram from 'dgram'; +import { ReplaySubject, timer } from 'rxjs'; +import { createStunResponder, RtpDescription, RtpOptions, sendStunBindingRequest } from './rtp-utils'; +import { SipManager, SipOptions } from './sip-manager'; +import { Subscribed } from './subscribed'; + +/* + A SipCallSession + */ +export class SipCallSession extends Subscribed { + private hasStarted = false + private hasCallEnded = false + private onCallEndedSubject = new ReplaySubject(1) + public sipCall: SipManager + onCallEnded = this.onCallEndedSubject.asObservable() + + constructor( + public readonly console: Console, + public readonly sipOptions: SipOptions, + public readonly rtpOptions: RtpOptions, + public readonly audioSplitter: dgram.Socket, + public audioRtcpSplitter: dgram.Socket, + public readonly videoSplitter: dgram.Socket, + public videoRtcpSplitter: dgram.Socket, + public readonly cameraName: string + ) { + super() + + this.sipCall = this.createSipManager(this.sipOptions) + } + + static async createCallSession(console: any, cameraName: string, sipOptions: SipOptions) { + const audioSplitter = await createBindZero(), + audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), + videoSplitter = await createBindZero(), + videoRtcpSplitter = await createBindUdp(videoSplitter.port + 1), + rtpOptions : RtpOptions = { + audio: { + port: audioSplitter.port, + rtcpPort: audioRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined + }, + video: { + port: videoSplitter.port, + rtcpPort: videoRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined + } + } + + return new SipCallSession( + console, + sipOptions, + rtpOptions, + audioSplitter.server, + audioRtcpSplitter.server, + videoSplitter.server, + videoRtcpSplitter.server, + cameraName + ) + } + + createSipManager(sipOptions: SipOptions) { + if (this.sipCall) { + this.sipCall.destroy() + } + + const call = (this.sipCall = new SipManager( + this.console, + sipOptions, + this.rtpOptions + )) + + this.addSubscriptions( + call.onEndedByRemote.subscribe(() => this.callEnded(false)) + ) + + return this.sipCall + } + + async call( audioSection, videoSection? ): Promise { + this.console.log(`SipSession::start()`); + + if (this.hasStarted) { + throw new Error('SIP Session has already been started') + } + this.hasStarted = true + + if (this.hasCallEnded) { + throw new Error('SIP Session has already ended') + } + + + try { + if( this.sipOptions.shouldRegister ) + await this.sipCall.register() + const rtpDescription : RtpDescription = await this.sipCall.invite( audioSection, videoSection ), + sendStunRequests = () => { + sendStunBindingRequest({ + rtpSplitter: this.audioSplitter, + rtcpSplitter: this.audioRtcpSplitter, + rtpDescription, + localUfrag: this.sipCall.audioUfrag, + type: 'audio', + }) + sendStunBindingRequest({ + rtpSplitter: this.videoSplitter, + rtcpSplitter: this.videoRtcpSplitter, + rtpDescription, + localUfrag: this.sipCall.videoUfrag, + type: 'video', + }) + } + + // if rtcp-mux is supported, rtp splitter will be used for both rtp and rtcp + if ( rtpDescription.audio.port > 0 && rtpDescription.audio.port === rtpDescription.audio.rtcpPort) { + this.audioRtcpSplitter.close() + this.audioRtcpSplitter = this.audioSplitter + } + + if ( rtpDescription.video.port > 0 && rtpDescription.video.port === rtpDescription.video.rtcpPort) { + this.videoRtcpSplitter.close() + this.videoRtcpSplitter = this.videoSplitter + } + + if ( (rtpDescription.audio.port > 0 && rtpDescription.audio.iceUFrag)|| (rtpDescription.video.port > 0 && rtpDescription.video.iceUFrag ) ) { + // ICE is supported + this.console.log(`Connecting to ${this.cameraName} using ICE`) + if( rtpDescription.audio.port > 0 ) { + createStunResponder(this.audioSplitter) + } + if( rtpDescription.video.port > 0 ) { + createStunResponder(this.videoSplitter) + } + + sendStunRequests() + } else { + // ICE is not supported, use stun as keep alive + this.console.log(`Connecting to ${this.cameraName} using STUN`) + this.addSubscriptions( + // hole punch every .5 seconds to keep stream alive and port open (matches behavior from Ring app) + timer(0, 500).subscribe(sendStunRequests) + ) + } + + this.audioSplitter.once('message', () => { + this.console.log(`Audio stream latched for ${this.cameraName}, port: ${this.rtpOptions.audio.port}`) + }) + + this.videoSplitter.once('message', () => { + this.console.log(`Video stream latched for ${this.cameraName}, port: ${this.rtpOptions.video.port}`) + }) + + return rtpDescription + } catch (e) { + this.callEnded(true) + throw e + } + } + + static async reserveRtpRtcpPorts() { + const ports = await reservePorts({ count: 4, type: 'udp' }) + return ports + } + + private async callEnded(sendBye: boolean) { + if (this.hasCallEnded) { + return + } + this.hasCallEnded = true + + if (sendBye) { + await this.sipCall.sendBye().catch(this.console.error) + } + + // clean up + this.console.log("sip-session callEnded") + this.onCallEndedSubject.next(null) + this.sipCall.destroy() + this.audioSplitter.close() + this.audioRtcpSplitter.close() + this.videoSplitter.close() + this.videoRtcpSplitter.close() + this.unsubscribe() + this.console.log("sip-session callEnded: done") + } + + async stop() { + await this.callEnded(true) + } } \ No newline at end of file diff --git a/plugins/sip/src/sip-call.ts b/plugins/sip/src/sip-manager.ts similarity index 96% rename from plugins/sip/src/sip-call.ts rename to plugins/sip/src/sip-manager.ts index 97cbb76af4..efbf78cf55 100644 --- a/plugins/sip/src/sip-call.ts +++ b/plugins/sip/src/sip-manager.ts @@ -1,438 +1,438 @@ -import { noop, Subject } from 'rxjs' -import { randomInteger, randomString } from './util' -import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' -import { decodeSrtpOptions } from '../../ring/src/srtp-utils' -import { stringify } from 'sip/sip' -import { timeoutPromise } from '@scrypted/common/src/promise-utils'; - -const sip = require('sip'), - sdp = require('sdp') - -export interface SipOptions { - to: string - from: string - domain?: string - expire?: number - localIp: string - localPort: number - debugSip?: boolean - useTcp?: boolean - sipRequestHandler?: SipRequestHandler - shouldRegister?: boolean -} - -/** - * Allows handling of SIP messages - */ -export abstract class SipRequestHandler { - abstract handle( request: SipRequest ) -} - -interface UriOptions { - name?: string - uri: string - params?: { - tag?: string - expires?: number - } -} - -interface SipHeaders { - [name: string]: string | any - cseq: { seq: number; method: string } - to: UriOptions - from: UriOptions - contact?: UriOptions[] - via?: UriOptions[] -} - -export interface SipRequest { - uri: UriOptions | string - method: string - headers: SipHeaders - content: string -} - -export interface SipResponse { - status: number - reason: string - headers: SipHeaders - content: string -} - -interface SipStack { - send: ( - request: SipRequest | SipResponse, - handler?: (response: SipResponse) => void - ) => void - destroy: () => void - makeResponse: ( - response: SipRequest, - status: number, - method: string - ) => SipResponse -} - -function getRandomId() { - return Math.floor(Math.random() * 1e6).toString() -} - -function getRtpDescription( - console: any, - sections: string[], - mediaType: 'audio' | 'video' -): RtpStreamDescription { - try { - const section = sections.find((s) => s.startsWith('m=' + mediaType)); - if( section === undefined ) { - return { - port: 0, - rtcpPort: 0, - srtpKey: undefined, - srtpSalt: undefined - }; - } - - const { port } = sdp.parseMLine(section), - lines: string[] = sdp.splitLines(section), - rtcpLine = lines.find((l: string) => l.startsWith('a=rtcp:')), - cryptoLine = lines.find((l: string) => l.startsWith('a=crypto'))!, - rtcpMuxLine = lines.find((l: string) => l.startsWith('a=rtcp-mux')), - ssrcLine = lines.find((l: string) => l.startsWith('a=ssrc')), - iceUFragLine = lines.find((l: string) => l.startsWith('a=ice-ufrag')), - icePwdLine = lines.find((l: string) => l.startsWith('a=ice-pwd')), - encodedCrypto = cryptoLine?.match(/inline:(\S*)/)![1] || undefined - - let rtcpPort: number; - if (rtcpMuxLine) { - rtcpPort = port; // rtcp-mux would cause rtcpLine to not be present - } - else { - rtcpPort = (rtcpLine && Number(rtcpLine.match(/rtcp:(\S*)/)?.[1])) || port + 1; // if there is no explicit RTCP port, then use RTP port + 1 - } - - return { - port, - rtcpPort, - ssrc: (ssrcLine && Number(ssrcLine.match(/ssrc:(\S*)/)?.[1])) || undefined, - iceUFrag: (iceUFragLine && iceUFragLine.match(/ice-ufrag:(\S*)/)?.[1]) || undefined, - icePwd: (icePwdLine && icePwdLine.match(/ice-pwd:(\S*)/)?.[1]) || undefined, - ...(encodedCrypto? decodeSrtpOptions(encodedCrypto) : { srtpKey: undefined, srtpSalt: undefined }) - } - } catch (e) { - console.error('Failed to parse SDP from remote end') - console.error(sections.join('\r\n')) - throw e - } -} - -function parseRtpDescription(console: any, inviteResponse: { - content: string -}): RtpDescription { - const sections: string[] = sdp.splitSections(inviteResponse.content), - lines: string[] = sdp.splitLines(sections[0]), - cLine = lines.find((line: string) => line.startsWith('c='))! - - return { - sdp: inviteResponse.content, - address: cLine.match(/c=IN IP4 (\S*)/)![1], - audio: getRtpDescription(console, sections, 'audio'), - video: getRtpDescription(console, sections, 'video') - } -} - -export class SipCall { - private seq = 20 - private fromParams = { tag: getRandomId() } - private toParams: { tag?: string } = {} - private callId = getRandomId() - private sipStack: SipStack - public readonly onEndedByRemote = new Subject() - private destroyed = false - private readonly console: Console - - public readonly audioUfrag = randomString(16) - public readonly videoUfrag = randomString(16) - - constructor( - console: Console, - private sipOptions: SipOptions, - private rtpOptions: RtpOptions, - //tlsPort: number - ) { - this.console = console; - - const host = this.sipOptions.localIp, - port = this.sipOptions.localPort, - contactId = randomInteger() - - this.sipStack = { - makeResponse: sip.makeResponse, - ...sip.create({ - host, - hostname: host, - port: port, - udp: !this.sipOptions.useTcp, - tcp: this.sipOptions.useTcp, - tls: false, - // tls_port: tlsPort, - // tls: { - // rejectUnauthorized: false, - // }, - ws: false, - logger: { - recv: function(m, remote) { - if( m.status == '200' && m.reason =='Ok' && m.headers.contact ) { - // ACK for INVITE and BYE must use the registrar contact uri - this.registrarContact = m.headers.contact[0].uri; - } - if( sipOptions.debugSip ) { - console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") - console.log(stringify( m )); - console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") - } - }, - send: function(m, remote) { - /* - Some door bells run an embedded SIP server with an unresolvable public domain - Due to bugs in the DNS resolution in sip/sip we abuse the 'send' logger to modify some headers - just before they get sent to the SIP server. - */ - if( sipOptions.domain && sipOptions.domain.length > 0 ) { - // Bticino CX300 specific: runs on an internet 2048362.bs.iotleg.com domain - // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain - let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() - let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() - - if( m.method == 'REGISTER' ) { - m.uri = "sip:" + sipOptions.domain - m.headers.to.uri = fromWithDomain - } else if( m.method == 'INVITE' || m.method == 'MESSAGE' ) { - m.uri = toWithDomain - m.headers.to.uri = toWithDomain - if( m.method == 'MESSAGE' && m.headers.to ) { - m.headers.to.params = null; - } - } else if( m.method == 'ACK' || m.method == 'BYE' ) { - m.headers.to.uri = toWithDomain - m.uri = this.registrarContact - } else { - throw new Error("Error: Method construct for uri not implemented: " + m.method) - } - - m.headers.from.uri = fromWithDomain - if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); - // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) - if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol - } - } - } - - if( sipOptions.debugSip ) { - console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - console.log(stringify( m )); - console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - } - }, - }, - }, - (request: SipRequest) => { - if (request.method === 'BYE') { - this.console.info('received BYE from remote end') - this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) - - if (this.destroyed) { - this.onEndedByRemote.next(null) - } - } else if( request.method === 'MESSAGE' && sipOptions.sipRequestHandler ) { - sipOptions.sipRequestHandler.handle( request ) - this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) - } else if( request.method === 'INVITE' && sipOptions.sipRequestHandler ) { - sipOptions.sipRequestHandler.handle( request ) - } else { - if( sipOptions.debugSip ) { - this.console.warn("unimplemented method received from remote: " + request.method) - } - } - } - ) - } - } - - request({ - method, - headers, - content, - seq, - }: { - method: string - headers?: Partial - content?: string - seq?: number - }) { - if (this.destroyed) { - return Promise.reject( - new Error('SIP request made after call was destroyed') - ) - } - - return new Promise((resolve, reject) => { - seq = seq || this.seq++ - this.sipStack.send( - { - method, - uri: this.sipOptions.to, - headers: { - to: { - //name: '"Scrypted SIP Plugin Client"', - uri: this.sipOptions.to, - params: (method == 'REGISTER' || method == 'INVITE' ? null : this.toParams), - }, - from: { - uri: this.sipOptions.from, - params: this.fromParams, - }, - 'max-forwards': 70, - 'call-id': this.callId, - cseq: { seq, method }, - ...headers, - }, - content: content || '', - }, - (response: SipResponse) => { - if (response.headers.to.params && response.headers.to.params.tag) { - this.toParams.tag = response.headers.to.params.tag - } - - if (response.status >= 300) { - if (response.status !== 408 || method !== 'BYE') { - this.console.error( - `sip ${method} request failed with status ` + response.status - ) - } - reject( - new Error( - `sip ${method} request failed with status ` + response.status - ) - ) - } else if (response.status < 200) { - // call made progress, do nothing and wait for another response - // console.log('call progress status ' + response.status) - } else { - if (method === 'INVITE') { - // The ACK must be sent with every OK to keep the connection alive. - this.acknowledge(seq!).catch((e) => { - this.console.error('Failed to send SDP ACK') - this.console.error(e) - }) - } - resolve(response) - } - } - ) - }) - } - - private async acknowledge(seq: number) { - // Don't wait for ack, it won't ever come back. - this.request({ - method: 'ACK', - seq, // The ACK must have the original sequence number. - }).catch(noop) - } - - sendDtmf(key: string) { - return this.request({ - method: 'INFO', - headers: { - 'Content-Type': 'application/dtmf-relay', - }, - content: `Signal=${key}\r\nDuration=250`, - }) - } - - /** - * Initiate a call by sending a SIP INVITE request - */ - async invite( audioSection, videoSection? ) : Promise { - let ssrc = randomInteger() - let audio = audioSection ? audioSection( this.rtpOptions.audio, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.audio.rtcpPort}`] ) : [] - let video = videoSection ? videoSection( this.rtpOptions.video, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.video.rtcpPort}`] ) : [] - const { from, localIp } = this.sipOptions, - inviteResponse = await this.request({ - method: 'INVITE', - headers: { - supported: 'replaces, outbound', - allow: - 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'application/sdp', - contact: [{ uri: from }], - }, - content: ([ - 'v=0', - `o=${from.split(':')[1].split('@')[0]} 3747 461 IN IP4 ${localIp}`, - 's=ScryptedSipPlugin', - `c=IN IP4 ${this.sipOptions.localIp}`, - 't=0 0', - ...audio, - ...video - ] - .filter((l) => l) - .join('\r\n')) + '\r\n' - }) - - return parseRtpDescription(this.console, inviteResponse) - } - - /** - * Register the user agent with a Registrar - */ - async register() : Promise { - const { from } = this.sipOptions; - await timeoutPromise( 500, - this.request({ - method: 'REGISTER', - headers: { - //supported: 'replaces, outbound', - allow: - 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], - }, - }).catch(noop)); - } - - /** - * Send a message to the current call contact - */ - async message( content: string ) : Promise { - const { from } = this.sipOptions, - messageResponse = await this.request({ - method: 'MESSAGE', - headers: { - //supported: 'replaces, outbound', - allow: - 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - 'content-type': 'text/plain', - contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], - }, - content: content - }); - return messageResponse; - } - - async sendBye() : Promise { - this.console.log('Sending BYE...') - return await timeoutPromise( 3000, this.request({ method: 'BYE' }).catch(() => { - // Don't care if we get an exception here. - })); - } - - destroy() { - this.console.debug("detroying sip-call") - this.destroyed = true - this.sipStack.destroy() - this.console.debug("detroying sip-call: done") - } -} +import { noop, Subject } from 'rxjs' +import { randomInteger, randomString } from './util' +import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' +import { decodeSrtpOptions } from '../../ring/src/srtp-utils' +import { stringify } from 'sip/sip' +import { timeoutPromise } from '@scrypted/common/src/promise-utils'; + +const sip = require('sip'), + sdp = require('sdp') + +export interface SipOptions { + to: string + from: string + domain?: string + expire?: number + localIp: string + localPort: number + debugSip?: boolean + useTcp?: boolean + sipRequestHandler?: SipRequestHandler + shouldRegister?: boolean +} + +/** + * Allows handling of SIP messages + */ +export abstract class SipRequestHandler { + abstract handle( request: SipRequest ) +} + +interface UriOptions { + name?: string + uri: string + params?: { + tag?: string + expires?: number + } +} + +interface SipHeaders { + [name: string]: string | any + cseq: { seq: number; method: string } + to: UriOptions + from: UriOptions + contact?: UriOptions[] + via?: UriOptions[] +} + +export interface SipRequest { + uri: UriOptions | string + method: string + headers: SipHeaders + content: string +} + +export interface SipResponse { + status: number + reason: string + headers: SipHeaders + content: string +} + +interface SipStack { + send: ( + request: SipRequest | SipResponse, + handler?: (response: SipResponse) => void + ) => void + destroy: () => void + makeResponse: ( + response: SipRequest, + status: number, + method: string + ) => SipResponse +} + +function getRandomId() { + return Math.floor(Math.random() * 1e6).toString() +} + +function getRtpDescription( + console: any, + sections: string[], + mediaType: 'audio' | 'video' +): RtpStreamDescription { + try { + const section = sections.find((s) => s.startsWith('m=' + mediaType)); + if( section === undefined ) { + return { + port: 0, + rtcpPort: 0, + srtpKey: undefined, + srtpSalt: undefined + }; + } + + const { port } = sdp.parseMLine(section), + lines: string[] = sdp.splitLines(section), + rtcpLine = lines.find((l: string) => l.startsWith('a=rtcp:')), + cryptoLine = lines.find((l: string) => l.startsWith('a=crypto'))!, + rtcpMuxLine = lines.find((l: string) => l.startsWith('a=rtcp-mux')), + ssrcLine = lines.find((l: string) => l.startsWith('a=ssrc')), + iceUFragLine = lines.find((l: string) => l.startsWith('a=ice-ufrag')), + icePwdLine = lines.find((l: string) => l.startsWith('a=ice-pwd')), + encodedCrypto = cryptoLine?.match(/inline:(\S*)/)![1] || undefined + + let rtcpPort: number; + if (rtcpMuxLine) { + rtcpPort = port; // rtcp-mux would cause rtcpLine to not be present + } + else { + rtcpPort = (rtcpLine && Number(rtcpLine.match(/rtcp:(\S*)/)?.[1])) || port + 1; // if there is no explicit RTCP port, then use RTP port + 1 + } + + return { + port, + rtcpPort, + ssrc: (ssrcLine && Number(ssrcLine.match(/ssrc:(\S*)/)?.[1])) || undefined, + iceUFrag: (iceUFragLine && iceUFragLine.match(/ice-ufrag:(\S*)/)?.[1]) || undefined, + icePwd: (icePwdLine && icePwdLine.match(/ice-pwd:(\S*)/)?.[1]) || undefined, + ...(encodedCrypto? decodeSrtpOptions(encodedCrypto) : { srtpKey: undefined, srtpSalt: undefined }) + } + } catch (e) { + console.error('Failed to parse SDP from remote end') + console.error(sections.join('\r\n')) + throw e + } +} + +function parseRtpDescription(console: any, inviteResponse: { + content: string +}): RtpDescription { + const sections: string[] = sdp.splitSections(inviteResponse.content), + lines: string[] = sdp.splitLines(sections[0]), + cLine = lines.find((line: string) => line.startsWith('c='))! + + return { + sdp: inviteResponse.content, + address: cLine.match(/c=IN IP4 (\S*)/)![1], + audio: getRtpDescription(console, sections, 'audio'), + video: getRtpDescription(console, sections, 'video') + } +} + +export class SipManager { + private seq = 20 + private fromParams = { tag: getRandomId() } + private toParams: { tag?: string } = {} + private callId = getRandomId() + private sipStack: SipStack + public readonly onEndedByRemote = new Subject() + private destroyed = false + private readonly console: Console + + public readonly audioUfrag = randomString(16) + public readonly videoUfrag = randomString(16) + + constructor( + console: Console, + private sipOptions: SipOptions, + private rtpOptions: RtpOptions, + //tlsPort: number + ) { + this.console = console; + + const host = this.sipOptions.localIp, + port = this.sipOptions.localPort, + contactId = randomInteger() + + this.sipStack = { + makeResponse: sip.makeResponse, + ...sip.create({ + host, + hostname: host, + port: port, + udp: !this.sipOptions.useTcp, + tcp: this.sipOptions.useTcp, + tls: false, + // tls_port: tlsPort, + // tls: { + // rejectUnauthorized: false, + // }, + ws: false, + logger: { + recv: function(m, remote) { + if( m.status == '200' && m.reason =='Ok' && m.headers.contact ) { + // ACK for INVITE and BYE must use the registrar contact uri + this.registrarContact = m.headers.contact[0].uri; + } + if( sipOptions.debugSip ) { + console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + console.log(stringify( m )); + console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + } + }, + send: function(m, remote) { + /* + Some door bells run an embedded SIP server with an unresolvable public domain + Due to bugs in the DNS resolution in sip/sip we abuse the 'send' logger to modify some headers + just before they get sent to the SIP server. + */ + if( sipOptions.domain && sipOptions.domain.length > 0 ) { + // Bticino CX300 specific: runs on an internet 2048362.bs.iotleg.com domain + // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain + let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() + let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() + + if( m.method == 'REGISTER' ) { + m.uri = "sip:" + sipOptions.domain + m.headers.to.uri = fromWithDomain + } else if( m.method == 'INVITE' || m.method == 'MESSAGE' ) { + m.uri = toWithDomain + m.headers.to.uri = toWithDomain + if( m.method == 'MESSAGE' && m.headers.to ) { + m.headers.to.params = null; + } + } else if( m.method == 'ACK' || m.method == 'BYE' ) { + m.headers.to.uri = toWithDomain + m.uri = this.registrarContact + } else { + throw new Error("Error: Method construct for uri not implemented: " + m.method) + } + + m.headers.from.uri = fromWithDomain + if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { + m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); + // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) + if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { + m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + } + } + } + + if( sipOptions.debugSip ) { + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + console.log(stringify( m )); + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + } + }, + }, + }, + (request: SipRequest) => { + if (request.method === 'BYE') { + this.console.info('received BYE from remote end') + this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) + + if (this.destroyed) { + this.onEndedByRemote.next(null) + } + } else if( request.method === 'MESSAGE' && sipOptions.sipRequestHandler ) { + sipOptions.sipRequestHandler.handle( request ) + this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) + } else if( request.method === 'INVITE' && sipOptions.sipRequestHandler ) { + sipOptions.sipRequestHandler.handle( request ) + } else { + if( sipOptions.debugSip ) { + this.console.warn("unimplemented method received from remote: " + request.method) + } + } + } + ) + } + } + + request({ + method, + headers, + content, + seq, + }: { + method: string + headers?: Partial + content?: string + seq?: number + }) { + if (this.destroyed) { + return Promise.reject( + new Error('SIP request made after call was destroyed') + ) + } + + return new Promise((resolve, reject) => { + seq = seq || this.seq++ + this.sipStack.send( + { + method, + uri: this.sipOptions.to, + headers: { + to: { + //name: '"Scrypted SIP Plugin Client"', + uri: this.sipOptions.to, + params: (method == 'REGISTER' || method == 'INVITE' ? null : this.toParams), + }, + from: { + uri: this.sipOptions.from, + params: this.fromParams, + }, + 'max-forwards': 70, + 'call-id': this.callId, + cseq: { seq, method }, + ...headers, + }, + content: content || '', + }, + (response: SipResponse) => { + if (response.headers.to.params && response.headers.to.params.tag) { + this.toParams.tag = response.headers.to.params.tag + } + + if (response.status >= 300) { + if (response.status !== 408 || method !== 'BYE') { + this.console.error( + `sip ${method} request failed with status ` + response.status + ) + } + reject( + new Error( + `sip ${method} request failed with status ` + response.status + ) + ) + } else if (response.status < 200) { + // call made progress, do nothing and wait for another response + // console.log('call progress status ' + response.status) + } else { + if (method === 'INVITE') { + // The ACK must be sent with every OK to keep the connection alive. + this.acknowledge(seq!).catch((e) => { + this.console.error('Failed to send SDP ACK') + this.console.error(e) + }) + } + resolve(response) + } + } + ) + }) + } + + private async acknowledge(seq: number) { + // Don't wait for ack, it won't ever come back. + this.request({ + method: 'ACK', + seq, // The ACK must have the original sequence number. + }).catch(noop) + } + + sendDtmf(key: string) { + return this.request({ + method: 'INFO', + headers: { + 'Content-Type': 'application/dtmf-relay', + }, + content: `Signal=${key}\r\nDuration=250`, + }) + } + + /** + * Initiate a call by sending a SIP INVITE request + */ + async invite( audioSection, videoSection? ) : Promise { + let ssrc = randomInteger() + let audio = audioSection ? audioSection( this.rtpOptions.audio, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.audio.rtcpPort}`] ) : [] + let video = videoSection ? videoSection( this.rtpOptions.video, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.video.rtcpPort}`] ) : [] + const { from, localIp } = this.sipOptions, + inviteResponse = await this.request({ + method: 'INVITE', + headers: { + supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + 'content-type': 'application/sdp', + contact: [{ uri: from }], + }, + content: ([ + 'v=0', + `o=${from.split(':')[1].split('@')[0]} 3747 461 IN IP4 ${localIp}`, + 's=ScryptedSipPlugin', + `c=IN IP4 ${this.sipOptions.localIp}`, + 't=0 0', + ...audio, + ...video + ] + .filter((l) => l) + .join('\r\n')) + '\r\n' + }) + + return parseRtpDescription(this.console, inviteResponse) + } + + /** + * Register the user agent with a Registrar + */ + async register() : Promise { + const { from } = this.sipOptions; + await timeoutPromise( 500, + this.request({ + method: 'REGISTER', + headers: { + //supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], + }, + }).catch(noop)); + } + + /** + * Send a message to the current call contact + */ + async message( content: string ) : Promise { + const { from } = this.sipOptions, + messageResponse = await this.request({ + method: 'MESSAGE', + headers: { + //supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + 'content-type': 'text/plain', + contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], + }, + content: content + }); + return messageResponse; + } + + async sendBye() : Promise { + this.console.log('Sending BYE...') + return await timeoutPromise( 3000, this.request({ method: 'BYE' }).catch(() => { + // Don't care if we get an exception here. + })); + } + + destroy() { + this.console.debug("detroying sip-call") + this.destroyed = true + this.sipStack.destroy() + this.console.debug("detroying sip-call: done") + } +} From 6cc04f672cb8d2b4993ff3f4f0641bac24cc5d29 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Sun, 5 Feb 2023 22:09:22 +0100 Subject: [PATCH 14/24] * Rename sip registered session to persistent sip manager * Allow handling of call pickup in homekit (hopefully!) --- plugins/bticino/package-lock.json | 4 +- plugins/bticino/package.json | 2 +- plugins/bticino/src/bticino-camera.ts | 35 ++++---- plugins/bticino/src/bticino-inviteHandler.ts | 20 ++++- .../bticino/src/bticino-voicemailHandler.ts | 2 +- plugins/bticino/src/persistent-sip-manager.ts | 67 ++++++++++++++ plugins/bticino/src/sip-helper.ts | 9 +- plugins/bticino/src/sip-registered-session.ts | 47 ---------- plugins/sip/package-lock.json | 4 +- plugins/sip/package.json | 2 +- plugins/sip/src/compositeSipMessageHandler.ts | 1 + plugins/sip/src/sip-call-session.ts | 89 ++++++++++--------- plugins/sip/src/sip-manager.ts | 89 ++++++++++++++----- 13 files changed, 222 insertions(+), 149 deletions(-) create mode 100644 plugins/bticino/src/persistent-sip-manager.ts delete mode 100644 plugins/bticino/src/sip-registered-session.ts diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 242c799cc4..89e987f889 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.4", + "version": "0.0.6-beta.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.6-beta.4", + "version": "0.0.6-beta.5", "dependencies": { "sdp": "^3.0.3", "sip": "0.0.6", diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index c168148695..735f058232 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.4", + "version": "0.0.6-beta.5", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index d0ef4591c8..30766c5098 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -15,8 +15,9 @@ import { BticinoStorageSettings } from './storage-settings'; import { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; -import { SipRegisteredSession } from './sip-registered-session'; +import { PersistentSipManager } from './persistent-sip-manager'; import { InviteHandler } from './bticino-inviteHandler'; +import { SipManager, SipRequest } from '../../sip/src/sip-manager'; const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; @@ -30,24 +31,24 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private currentMediaMimeType: string private refreshTimeout: NodeJS.Timeout public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler() + public incomingCallRequest : SipRequest private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) private inviteHandler : InviteHandler = new InviteHandler(this) //TODO: randomize this private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) - private persistentSipSession : SipRegisteredSession + private persistentSipManager : PersistentSipManager constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) - this.requestHandlers.add( this.voicemailHandler ) - this.requestHandlers.add( this.inviteHandler ) - this.persistentSipSession = new SipRegisteredSession( this ) + this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) + this.persistentSipManager = new PersistentSipManager( this ) } sipUnlock(): Promise { this.log.i("unlocking C300X door ") - return this.persistentSipSession.enable().then( (sipCall) => { + return this.persistentSipManager.enable().then( (sipCall) => { sipCall.message( '*8*19*20##' ) .then( () => sleep(1000) @@ -57,7 +58,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } getAswmStatus() : Promise { - return this.persistentSipSession.enable().then( (sipCall) => { + return this.persistentSipManager.enable().then( (sipCall) => { sipCall.message( "GetAswmStatus!" ) } ) } @@ -195,17 +196,16 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid // A normal call session doesn't require registering sipOptions.shouldRegister = false - sip = await SipHelper.sipSession( sipOptions ) + sip = await this.persistentSipManager.session( sipOptions ); // Validate this sooner if( !sip ) return Promise.reject("Cannot create session") sip.onCallEnded.subscribe(cleanup) // Call the C300X - this.remoteRtpDescription = await sip.call( + this.remoteRtpDescription = await sip.callOrAcceptInvite( ( audio ) => { return [ - 'a=DEVADDR:20', // Needed for bt_answering_machine (bticino specific) `m=audio ${audio.port} RTP/SAVP 97`, `a=rtpmap:97 speex/8000`, `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, @@ -218,28 +218,23 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, 'a=recvonly' ] - } ); - if( sip.sipOptions.debugSip ) + }, this.incomingCallRequest ); + if( sipOptions.debugSip ) this.log.d('SIP: Received remote SDP:\n' + this.remoteRtpDescription.sdp) let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) sdp = addTrackControls(sdp) sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') - if( sip.sipOptions.debugSip ) + if( sipOptions.debugSip ) this.log.d('SIP: Updated SDP:\n' + sdp); - let vseq = 0; - let vseen = 0; - let vlost = 0; - let aseq = 0; - let aseen = 0; - let alost = 0; + let vseq = 0, vseen = 0, vlost = 0, aseq = 0, aseen = 0, alost = 0; rtsp = new RtspServer(client, sdp, true); const parsedSdp = parseSdp(rtsp.sdp); const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control - if( sip.sipOptions.debugSip ) { + if( sipOptions.debugSip ) { rtsp.console = this.console } diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index 2909f50de4..fc4fddbaad 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -1,5 +1,6 @@ import { SipRequestHandler, SipRequest } from "../../sip/src/sip-manager" import { BticinoSipCamera } from "./bticino-camera" +import { stringifyUri } from 'sip/sip' export class InviteHandler extends SipRequestHandler { constructor( private sipCamera : BticinoSipCamera ) { @@ -7,14 +8,27 @@ export class InviteHandler extends SipRequestHandler { } handle(request: SipRequest) { + //TODO: restrict this to call from:c300x@ AND to:alluser@ ? + if( request.method == 'CANCEL' ) { + this.sipCamera.console.log('CANCEL voice call from: ' + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) ) + this.reset() + } if( request.method === 'INVITE' ) { - this.sipCamera.console.log("INCOMING voice call from: " + request.headers.from ) + this.sipCamera.console.log("INCOMING voice call from: " + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) ) + this.sipCamera.binaryState = true + this.sipCamera.incomingCallRequest = request setTimeout( () => { // Assumption that flexisip only holds this call active for 20 seconds ... might be revised - this.sipCamera.binaryState = false + this.reset() }, 20 * 1000 ) } - } + } + + reset() { + console.log("Reset the incoming call request") + this.sipCamera.incomingCallRequest = undefined + this.sipCamera.binaryState = false + } } \ No newline at end of file diff --git a/plugins/bticino/src/bticino-voicemailHandler.ts b/plugins/bticino/src/bticino-voicemailHandler.ts index 5932a7cc9c..797868d61d 100644 --- a/plugins/bticino/src/bticino-voicemailHandler.ts +++ b/plugins/bticino/src/bticino-voicemailHandler.ts @@ -22,7 +22,7 @@ export class VoicemailHandler extends SipRequestHandler { this.sipCamera.console.debug("Answering machine check not enabled, cameraId: " + this.sipCamera.id ) } //TODO: make interval customizable, now every 5 minutes - this.timeout = setTimeout( () => this.checkVoicemail() , 60 * 1000 ) + this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 ) } cancelVoicemailCheck() { diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts new file mode 100644 index 0000000000..60bd7420b3 --- /dev/null +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -0,0 +1,67 @@ +import { SipCallSession } from "../../sip/src/sip-call-session"; +import { BticinoSipCamera } from "./bticino-camera"; +import { SipHelper } from "./sip-helper"; +import { SipManager, SipOptions } from "../../sip/src/sip-manager"; + +/** + * This class registers itself with the SIP server as a contact for a user account. + * The registration expires after the expires time in sipOptions is reached. + * The sip session will re-register itself after the expires time is reached. + */ +const CHECK_INTERVAL : number = 10 * 1000 +export class PersistentSipManager { + + private sipManager : SipManager + private lastRegistration : number = 0 + private expireInterval : number = 0 + + constructor( private camera : BticinoSipCamera ) { + // Give it a second and run in seperate thread to avoid failure on creation for from/to/domain check + setTimeout( () => this.register(), CHECK_INTERVAL ) + } + + async enable() : Promise { + if( this.sipManager ) { + return this.sipManager + } else { + return this.register() + } + } + + private async register() : Promise { + let now = Date.now() + try { + let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) + if( this.expireInterval == 0 ) { + if( sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { + // Safe guard just in case + sipOptions.expire = 300 + } + this.expireInterval = (sipOptions.expire * 1000) - 10000 + } + + if( now - this.lastRegistration >= this.expireInterval ) { + let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) + + this.sipManager?.destroy() + this.sipManager = new SipManager(console, sipOptions ) + await this.sipManager.register() + + this.lastRegistration = now + + return this.sipManager; + } + } catch(e) { + this.camera.console.error("Error enabling persistent SIP manager: " + e ) + // Try again in a minute + this.lastRegistration = now + (60 * 1000) + throw e + } finally { + setTimeout( () => this.register(), CHECK_INTERVAL ) + } + } + + async session( sipOptions: SipOptions ) : Promise { + return SipCallSession.createCallSession(console, "Bticino", sipOptions, this.sipManager ) + } +} \ No newline at end of file diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index f40bc35386..5d79426845 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -1,16 +1,17 @@ import { SipOptions } from "../../sip/src/sip-manager"; -import { SipCallSession } from "../../sip/src/sip-call-session"; import { BticinoSipCamera } from "./bticino-camera"; export class SipHelper { public static sipOptions( camera : BticinoSipCamera ) : SipOptions { - //Might be removed soon + // Might be removed soon? if( camera.storage.getItem('sipto') && camera.storage.getItem('sipto').toString().indexOf(';') > 0 ) { camera.storage.setItem('sipto', camera.storage.getItem('sipto').toString().split(';')[0] ) } const from = camera.storage.getItem('sipfrom')?.trim() const to = camera.storage.getItem('sipto')?.trim() const localIp = from?.split(':')[0].split('@')[1] + // Although this might not occur directly, each camera should run on its own port + // Might need to use a random free port here (?) const localPort = parseInt(from?.split(':')[1]) || 5060 const domain = camera.storage.getItem('sipdomain')?.trim() const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '600' @@ -35,8 +36,4 @@ export class SipHelper { sipRequestHandler: camera.requestHandlers } } - - public static sipSession( sipOptions : SipOptions ) : Promise { - return SipCallSession.createCallSession(console, "Bticino", sipOptions ) - } } \ No newline at end of file diff --git a/plugins/bticino/src/sip-registered-session.ts b/plugins/bticino/src/sip-registered-session.ts deleted file mode 100644 index c8d85d6442..0000000000 --- a/plugins/bticino/src/sip-registered-session.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { SipCallSession } from "../../sip/src/sip-call-session"; -import { BticinoSipCamera } from "./bticino-camera"; -import { SipHelper } from "./sip-helper"; -import { SipManager, SipOptions } from "../../sip/src/sip-manager"; - -/** - * This class registers itself with the SIP server as a contact for a user account. - * The registration expires after the expires time in sipOptions is reached. - * The sip session will re-register itself after the expires time is reached. - */ -export class SipRegisteredSession { - private currentSipSession : SipCallSession - - constructor( private camera : BticinoSipCamera ) { - // Give it a second - setTimeout( () => this.enable(), 10 * 1000 ) - } - - async enable() : Promise { - if( this.currentSipSession ) { - return this.currentSipSession.sipCall - } - let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) - - if( sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { - // Safe guard just in case - sipOptions.expire = 300 - } - - setTimeout( () => { - this.currentSipSession?.stop() - this.currentSipSession = undefined - this.enable() - }, sipOptions.expire * 1000 ) - - try { - this.currentSipSession = await SipHelper.sipSession( sipOptions ) - await this.currentSipSession.sipCall.register() - return this.currentSipSession.sipCall - } catch(e) { - this.currentSipSession?.stop() - this.currentSipSession = undefined - this.camera.console.error("Error enabling SIP session: " + e ) - throw e - } - } -} \ No newline at end of file diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 958be0ff7e..462f1a8dfd 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/sip", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/sip", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "dependencies": { "@homebridge/camera-utils": "^2.0.4", "sdp": "^3.0.3", diff --git a/plugins/sip/package.json b/plugins/sip/package.json index f416891703..6c6f9e5d16 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/sip", - "version": "0.0.6-beta.2", + "version": "0.0.6-beta.4", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/sip/src/compositeSipMessageHandler.ts b/plugins/sip/src/compositeSipMessageHandler.ts index a01154102d..2542de8bf1 100644 --- a/plugins/sip/src/compositeSipMessageHandler.ts +++ b/plugins/sip/src/compositeSipMessageHandler.ts @@ -10,5 +10,6 @@ export class CompositeSipMessageHandler extends SipRequestHandler { } add( handler : SipRequestHandler ) { this.handlers.push( handler ) + return this } } \ No newline at end of file diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index 5399002870..53a006e8b0 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -3,7 +3,7 @@ import { createBindUdp, createBindZero } from '@scrypted/common/src/listen-clust import dgram from 'dgram'; import { ReplaySubject, timer } from 'rxjs'; import { createStunResponder, RtpDescription, RtpOptions, sendStunBindingRequest } from './rtp-utils'; -import { SipManager, SipOptions } from './sip-manager'; +import { SipManager, SipOptions, SipRequest } from './sip-manager'; import { Subscribed } from './subscribed'; /* @@ -13,45 +13,43 @@ export class SipCallSession extends Subscribed { private hasStarted = false private hasCallEnded = false private onCallEndedSubject = new ReplaySubject(1) - public sipCall: SipManager onCallEnded = this.onCallEndedSubject.asObservable() constructor( - public readonly console: Console, - public readonly sipOptions: SipOptions, - public readonly rtpOptions: RtpOptions, + private readonly console: Console, + private readonly sipOptions: SipOptions, + private readonly rtpOptions: RtpOptions, public readonly audioSplitter: dgram.Socket, public audioRtcpSplitter: dgram.Socket, public readonly videoSplitter: dgram.Socket, public videoRtcpSplitter: dgram.Socket, - public readonly cameraName: string + public readonly cameraName: string, + private sipManager: SipManager ) { super() - - this.sipCall = this.createSipManager(this.sipOptions) } - static async createCallSession(console: any, cameraName: string, sipOptions: SipOptions) { - const audioSplitter = await createBindZero(), - audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), - videoSplitter = await createBindZero(), - videoRtcpSplitter = await createBindUdp(videoSplitter.port + 1), - rtpOptions : RtpOptions = { - audio: { - port: audioSplitter.port, - rtcpPort: audioRtcpSplitter.port, - //TODO: make this cleaner - srtpKey: undefined, - srtpSalt: undefined - }, - video: { - port: videoSplitter.port, - rtcpPort: videoRtcpSplitter.port, - //TODO: make this cleaner - srtpKey: undefined, - srtpSalt: undefined - } + static async createCallSession(console: any, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { + const audioSplitter = await createBindZero(), + audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), + videoSplitter = await createBindZero(), + videoRtcpSplitter = await createBindUdp(videoSplitter.port + 1), + rtpOptions : RtpOptions = { + audio: { + port: audioSplitter.port, + rtcpPort: audioRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined + }, + video: { + port: videoSplitter.port, + rtcpPort: videoRtcpSplitter.port, + //TODO: make this cleaner + srtpKey: undefined, + srtpSalt: undefined } + } return new SipCallSession( console, @@ -61,29 +59,33 @@ export class SipCallSession extends Subscribed { audioRtcpSplitter.server, videoSplitter.server, videoRtcpSplitter.server, - cameraName + cameraName, + sipManager ) } createSipManager(sipOptions: SipOptions) { - if (this.sipCall) { - this.sipCall.destroy() + if (this.sipManager) { + this.sipManager.destroy() } - const call = (this.sipCall = new SipManager( + const call = (this.sipManager = new SipManager( this.console, - sipOptions, - this.rtpOptions + sipOptions )) this.addSubscriptions( call.onEndedByRemote.subscribe(() => this.callEnded(false)) ) - return this.sipCall + return this.sipManager } async call( audioSection, videoSection? ): Promise { + return this.callOrAcceptInvite(audioSection, videoSection) + } + + async callOrAcceptInvite( audioSection, videoSection?, incomingCallRequest? : SipRequest ): Promise { this.console.log(`SipSession::start()`); if (this.hasStarted) { @@ -95,24 +97,23 @@ export class SipCallSession extends Subscribed { throw new Error('SIP Session has already ended') } - try { if( this.sipOptions.shouldRegister ) - await this.sipCall.register() - const rtpDescription : RtpDescription = await this.sipCall.invite( audioSection, videoSection ), + await this.sipManager.register() + const rtpDescription : RtpDescription = await this.sipManager.invite( this.rtpOptions, audioSection, videoSection, incomingCallRequest ), sendStunRequests = () => { sendStunBindingRequest({ rtpSplitter: this.audioSplitter, rtcpSplitter: this.audioRtcpSplitter, rtpDescription, - localUfrag: this.sipCall.audioUfrag, + localUfrag: this.sipManager.audioUfrag, type: 'audio', }) sendStunBindingRequest({ rtpSplitter: this.videoSplitter, rtcpSplitter: this.videoRtcpSplitter, rtpDescription, - localUfrag: this.sipCall.videoUfrag, + localUfrag: this.sipManager.videoUfrag, type: 'video', }) } @@ -175,19 +176,19 @@ export class SipCallSession extends Subscribed { this.hasCallEnded = true if (sendBye) { - await this.sipCall.sendBye().catch(this.console.error) + await this.sipManager.sendBye().catch(this.console.error) } // clean up - this.console.log("sip-session callEnded") + this.console.log("sip-call-session callEnded") this.onCallEndedSubject.next(null) - this.sipCall.destroy() + //this.sipManager.destroy() this.audioSplitter.close() this.audioRtcpSplitter.close() this.videoSplitter.close() this.videoRtcpSplitter.close() this.unsubscribe() - this.console.log("sip-session callEnded: done") + this.console.log("sip-call-session callEnded: done") } async stop() { diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index efbf78cf55..6edd3d0d0f 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -2,11 +2,10 @@ import { noop, Subject } from 'rxjs' import { randomInteger, randomString } from './util' import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' import { decodeSrtpOptions } from '../../ring/src/srtp-utils' -import { stringify } from 'sip/sip' +import { stringify, stringifyUri } from 'sip/sip' import { timeoutPromise } from '@scrypted/common/src/promise-utils'; -const sip = require('sip'), - sdp = require('sdp') +const sip = require('sip'), sdp = require('sdp') export interface SipOptions { to: string @@ -69,7 +68,11 @@ interface SipStack { makeResponse: ( response: SipRequest, status: number, - method: string + method: string, + extension?: { + headers: Partial, + content + } ) => SipResponse } @@ -157,8 +160,6 @@ export class SipManager { constructor( console: Console, private sipOptions: SipOptions, - private rtpOptions: RtpOptions, - //tlsPort: number ) { this.console = console; @@ -216,16 +217,20 @@ export class SipManager { } else if( m.method == 'ACK' || m.method == 'BYE' ) { m.headers.to.uri = toWithDomain m.uri = this.registrarContact + } else if( (m.method == undefined && m.status) && m.headers.cseq ) { + // 183, 200, OK, CSeq: INVITE } else { - throw new Error("Error: Method construct for uri not implemented: " + m.method) + console.error("Error: Method construct for uri not implemented: " + m.method) } - m.headers.from.uri = fromWithDomain - if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); - // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) - if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + if( m.method ) { + m.headers.from.uri = fromWithDomain + if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { + m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); + // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) + if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { + m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + } } } } @@ -250,6 +255,12 @@ export class SipManager { sipOptions.sipRequestHandler.handle( request ) this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) } else if( request.method === 'INVITE' && sipOptions.sipRequestHandler ) { + let ringResponse = this.sipStack.makeResponse(request, 180, 'Ringing') + ringResponse.headers["Supported"] = "replaces, outbound, gruu" + ringResponse.headers.to.params.tag = '2pAx2dBB' + this.sipStack.send(ringResponse) + sipOptions.sipRequestHandler.handle( request ) + } else if( request.method === 'CANCEL' ) { sipOptions.sipRequestHandler.handle( request ) } else { if( sipOptions.debugSip ) { @@ -356,12 +367,45 @@ export class SipManager { /** * Initiate a call by sending a SIP INVITE request */ - async invite( audioSection, videoSection? ) : Promise { + async invite( rtpOptions : RtpOptions, audioSection, videoSection?, incomingCallRequest? ) : Promise { let ssrc = randomInteger() - let audio = audioSection ? audioSection( this.rtpOptions.audio, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.audio.rtcpPort}`] ) : [] - let video = videoSection ? videoSection( this.rtpOptions.video, ssrc ).concat( ...[`a=ssrc:${ssrc}`, `a=rtcp:${this.rtpOptions.video.rtcpPort}`] ) : [] - const { from, localIp } = this.sipOptions, - inviteResponse = await this.request({ + let audio = audioSection ? audioSection( rtpOptions.audio, ssrc ).concat( ...[`a=rtcp:${rtpOptions.audio.rtcpPort}`] ) : [] + let video = videoSection ? videoSection( rtpOptions.video, ssrc ).concat( ...[`a=rtcp:${rtpOptions.video.rtcpPort}`] ) : [] + const { from, localIp } = this.sipOptions; + + + if( incomingCallRequest ) { + let callResponse = this.sipStack.makeResponse(incomingCallRequest, 200, 'Ok', { + headers: { + supported: 'replaces, outbound', + allow: + 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', + 'content-type': 'application/sdp', + }, + content: ([ + 'v=0', + `o=${from.split(':')[1].split('@')[0]} 3747 461 IN IP4 ${localIp}`, + 's=ScryptedSipPlugin', + `c=IN IP4 ${this.sipOptions.localIp}`, + 't=0 0', + ...audio, + ...video + ] + .filter((l) => l) + .join('\r\n')) + '\r\n' + } ) + callResponse.headers["record-route"] = '<' + stringifyUri( incomingCallRequest.headers["record-route"] ) + '>' + callResponse.headers.to.params.tag = '2pAx2dBB' //whatever, just some value + callResponse.headers.contact = [{ uri: incomingCallRequest.headers.to.uri }] + await this.sipStack.send(callResponse) + + return parseRtpDescription(this.console, incomingCallRequest) + } else { + if( this.sipOptions.to.toLocaleLowerCase().indexOf('c300x') >= 0 ) { + // Needed for bt_answering_machine (bticino specific) + audio.unshift('a=DEVADDR:20') + } + let inviteResponse = await this.request({ method: 'INVITE', headers: { supported: 'replaces, outbound', @@ -383,7 +427,8 @@ export class SipManager { .join('\r\n')) + '\r\n' }) - return parseRtpDescription(this.console, inviteResponse) + return parseRtpDescription(this.console, inviteResponse) + } } /** @@ -391,7 +436,7 @@ export class SipManager { */ async register() : Promise { const { from } = this.sipOptions; - await timeoutPromise( 500, + await timeoutPromise( 1000, this.request({ method: 'REGISTER', headers: { @@ -430,9 +475,9 @@ export class SipManager { } destroy() { - this.console.debug("detroying sip-call") + this.console.debug("detroying sip-manager") this.destroyed = true this.sipStack.destroy() - this.console.debug("detroying sip-call: done") + this.console.debug("detroying sip-manager: done") } } From b1f1f65de91ad5ae2f88dff24f89dbd390827151 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Sun, 5 Feb 2023 22:19:56 +0100 Subject: [PATCH 15/24] * use the consoles from the camera object --- plugins/bticino/src/bticino-inviteHandler.ts | 2 +- plugins/bticino/src/bticino-lock.ts | 2 +- plugins/bticino/src/persistent-sip-manager.ts | 4 ++-- plugins/bticino/src/storage-settings.ts | 4 +--- plugins/sip/src/sip-call-session.ts | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index fc4fddbaad..dad7055282 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -27,7 +27,7 @@ export class InviteHandler extends SipRequestHandler { } reset() { - console.log("Reset the incoming call request") + this.sipCamera.console.log("Reset the incoming call request") this.sipCamera.incomingCallRequest = undefined this.sipCamera.binaryState = false } diff --git a/plugins/bticino/src/bticino-lock.ts b/plugins/bticino/src/bticino-lock.ts index 65eb6658ba..0ef6dd5566 100644 --- a/plugins/bticino/src/bticino-lock.ts +++ b/plugins/bticino/src/bticino-lock.ts @@ -15,7 +15,7 @@ export class BticinoSipLock extends ScryptedDeviceBase implements Lock { this.timeout = undefined } , 3000); } else { - this.console.log("Still attempting previous locking ...") + this.camera.console.log("Still attempting previous locking ...") } return } diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts index 60bd7420b3..b6601de445 100644 --- a/plugins/bticino/src/persistent-sip-manager.ts +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -44,7 +44,7 @@ export class PersistentSipManager { let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) this.sipManager?.destroy() - this.sipManager = new SipManager(console, sipOptions ) + this.sipManager = new SipManager(this.camera.console, sipOptions ) await this.sipManager.register() this.lastRegistration = now @@ -62,6 +62,6 @@ export class PersistentSipManager { } async session( sipOptions: SipOptions ) : Promise { - return SipCallSession.createCallSession(console, "Bticino", sipOptions, this.sipManager ) + return SipCallSession.createCallSession(this.camera.console, "Bticino", sipOptions, this.sipManager ) } } \ No newline at end of file diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index d66e95924f..99d2cc26a4 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -3,9 +3,7 @@ import { StorageSettings } from '@scrypted/sdk/storage-settings'; import { BticinoSipCamera } from './bticino-camera'; export class BticinoStorageSettings { - private storageSettings - constructor(camera : BticinoSipCamera) { - + constructor(camera : BticinoSipCamera, private storageSettings : StorageSettings) { this.storageSettings = new StorageSettings( camera, { sipfrom: { title: 'SIP From: URI', diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index 53a006e8b0..8cdaeb00f5 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -29,7 +29,7 @@ export class SipCallSession extends Subscribed { super() } - static async createCallSession(console: any, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { + static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { const audioSplitter = await createBindZero(), audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), videoSplitter = await createBindZero(), From 35d991026b262b79851e277eb7b4009f8d2c090a Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Sun, 5 Feb 2023 22:30:18 +0100 Subject: [PATCH 16/24] * use the consoles from the camera object --- plugins/bticino/src/bticino-camera.ts | 1 + plugins/bticino/src/storage-settings.ts | 5 ++++- plugins/sip/src/sip-manager.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 30766c5098..4ecca82709 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -206,6 +206,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.remoteRtpDescription = await sip.callOrAcceptInvite( ( audio ) => { return [ + `m=audio ${audio.port} RTP/SAVP 97`, `a=rtpmap:97 speex/8000`, `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index 99d2cc26a4..1fc046a2f2 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -3,7 +3,10 @@ import { StorageSettings } from '@scrypted/sdk/storage-settings'; import { BticinoSipCamera } from './bticino-camera'; export class BticinoStorageSettings { - constructor(camera : BticinoSipCamera, private storageSettings : StorageSettings) { + private storageSettings + + constructor(camera : BticinoSipCamera) { + this.storageSettings = new StorageSettings( camera, { sipfrom: { title: 'SIP From: URI', diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index 6edd3d0d0f..658ca09574 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -225,7 +225,7 @@ export class SipManager { if( m.method ) { m.headers.from.uri = fromWithDomain - if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].indexOf('-') < 0 ) { + if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].lastIndexOf('-') < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { From 1e72ecb52762fdf0f79e58fe9564263877969d3b Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Mon, 6 Feb 2023 19:33:08 +0100 Subject: [PATCH 17/24] * Fix the retry timer --- plugins/bticino/src/persistent-sip-manager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts index b6601de445..aee9fc0edb 100644 --- a/plugins/bticino/src/persistent-sip-manager.ts +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -33,7 +33,7 @@ export class PersistentSipManager { try { let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) if( this.expireInterval == 0 ) { - if( sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { + if( !Number.isNaN( sipOptions.expire ) || sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { // Safe guard just in case sipOptions.expire = 300 } @@ -54,7 +54,7 @@ export class PersistentSipManager { } catch(e) { this.camera.console.error("Error enabling persistent SIP manager: " + e ) // Try again in a minute - this.lastRegistration = now + (60 * 1000) + this.lastRegistration = now + (60 * 1000) - this.expireInterval throw e } finally { setTimeout( () => this.register(), CHECK_INTERVAL ) From 65daaf454ca3b5e019737200938cbd19a9c7f267 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Sun, 12 Feb 2023 12:37:06 +0100 Subject: [PATCH 18/24] * Added webhook url --- plugins/bticino/src/bticino-camera.ts | 34 ++++++++++++++++--- plugins/bticino/src/persistent-sip-manager.ts | 2 +- plugins/bticino/src/storage-settings.ts | 10 +++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 4ecca82709..574f3345cd 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -1,13 +1,13 @@ import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; +import { sleep } from '@scrypted/common/src/sleep'; import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; import { SipCallSession } from '../../sip/src/sip-call-session'; import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, RtpDescription } from '../../sip/src/rtp-utils'; import { VoicemailHandler } from './bticino-voicemailHandler'; import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; import { decodeSrtpOptions, encodeSrtpOptions, SrtpOptions } from '../../ring/src/srtp-utils' -import { sleep } from '@scrypted/common/src/sleep'; import { SipHelper } from './sip-helper'; import child_process, { ChildProcess } from 'child_process'; import dgram from 'dgram'; @@ -22,7 +22,7 @@ import { SipManager, SipRequest } from '../../sip/src/sip-manager'; const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; -export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler { private session: SipCallSession private remoteRtpDescription: RtpDescription private audioOutForwarder: dgram.Socket @@ -39,11 +39,16 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) private persistentSipManager : PersistentSipManager + public webhookUrl : string constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) - this.persistentSipManager = new PersistentSipManager( this ) + this.persistentSipManager = new PersistentSipManager( this ); + (async() => { + this.webhookUrl = await this.getMotionDetectedWebhookUrl() + })(); + } sipUnlock(): Promise { @@ -339,4 +344,25 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid async releaseDevice(id: string, nativeId: string): Promise { } + + private async getMotionDetectedWebhookUrl(): Promise { + let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId, { insecure: false }); + webhookUrl += "doorbellDetected"; + this.console.log( webhookUrl ) + return `${webhookUrl}`; + } + + public async onRequest(request: HttpRequest, response: HttpResponse): Promise { + if (request.url.endsWith('/motionDetected')) { + this.binaryState = true; + + response.send('Success', { + code: 200, + }); + } else { + response.send('Unsupported operation', { + code: 400, + }); + } + } } \ No newline at end of file diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts index aee9fc0edb..775df9f164 100644 --- a/plugins/bticino/src/persistent-sip-manager.ts +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -32,8 +32,8 @@ export class PersistentSipManager { let now = Date.now() try { let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) + if( Number.isNaN( sipOptions.expire ) || sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { if( this.expireInterval == 0 ) { - if( !Number.isNaN( sipOptions.expire ) || sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { // Safe guard just in case sipOptions.expire = 300 } diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index 1fc046a2f2..d4cbb7a39d 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -6,7 +6,6 @@ export class BticinoStorageSettings { private storageSettings constructor(camera : BticinoSipCamera) { - this.storageSettings = new StorageSettings( camera, { sipfrom: { title: 'SIP From: URI', @@ -48,6 +47,15 @@ export class BticinoStorageSettings { description: 'Enable voicemail alerts', placeholder: 'true or false', }, + doorbellWebhookUrl: { + title: 'Doorbell Sensor Webhook', + type: 'string', + readonly: true, + mapGet: () => { + return camera.webhookUrl; + }, + description: 'Incoming doorbell sensor webhook url.', + } }); } From 733c311cdcc37af87cd40375360d28382ab75df7 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Thu, 23 Feb 2023 09:38:49 +0100 Subject: [PATCH 19/24] * parse record route correctly --- plugins/bticino/package-lock.json | 4 +-- plugins/bticino/package.json | 2 +- plugins/bticino/src/bticino-camera.ts | 30 ++++++++++++++----- plugins/bticino/src/bticino-inviteHandler.ts | 10 ++----- plugins/bticino/src/main.ts | 3 +- plugins/bticino/src/persistent-sip-manager.ts | 7 ++--- plugins/bticino/src/storage-settings.ts | 6 ++-- plugins/sip/src/sip-call-session.ts | 8 +++-- plugins/sip/src/sip-manager.ts | 19 ++++++++---- 9 files changed, 56 insertions(+), 33 deletions(-) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 89e987f889..11c1fca6f2 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.5", + "version": "0.0.6-beta.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.6-beta.5", + "version": "0.0.6-beta.6", "dependencies": { "sdp": "^3.0.3", "sip": "0.0.6", diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index 735f058232..97cca3cc7f 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.5", + "version": "0.0.6-beta.6", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 574f3345cd..9ed7d8d954 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -46,9 +46,8 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) this.persistentSipManager = new PersistentSipManager( this ); (async() => { - this.webhookUrl = await this.getMotionDetectedWebhookUrl() + this.webhookUrl = await this.buttonPressedDetectedWebhookUrl() })(); - } sipUnlock(): Promise { @@ -238,8 +237,12 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid rtsp = new RtspServer(client, sdp, true); const parsedSdp = parseSdp(rtsp.sdp); - const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video').control - const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio').control + const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video')?.control + const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio')?.control + + if( !videoTrack || !audioTrack ) + throw new Error("Video track and/or audio track not found") + if( sipOptions.debugSip ) { rtsp.console = this.console } @@ -295,6 +298,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } } catch (e) { + this.console.error(e) sip?.stop() throw e; } @@ -345,17 +349,29 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid async releaseDevice(id: string, nativeId: string): Promise { } - private async getMotionDetectedWebhookUrl(): Promise { + reset() { + this.console.log("Reset the incoming call request") + this.incomingCallRequest = undefined + this.binaryState = false + } + + private async buttonPressedDetectedWebhookUrl(): Promise { let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId, { insecure: false }); - webhookUrl += "doorbellDetected"; + webhookUrl += "buttonPressed"; this.console.log( webhookUrl ) return `${webhookUrl}`; } public async onRequest(request: HttpRequest, response: HttpResponse): Promise { - if (request.url.endsWith('/motionDetected')) { + if (request.url.endsWith('/buttonPressed')) { this.binaryState = true; + setTimeout( () => { + // Remove duplicate code + // Assumption that flexisip only holds this call active for 20 seconds ... might be revised + this.reset() + }, 20 * 1000 ) + response.send('Success', { code: 200, }); diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index dad7055282..1a43490129 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -11,7 +11,7 @@ export class InviteHandler extends SipRequestHandler { //TODO: restrict this to call from:c300x@ AND to:alluser@ ? if( request.method == 'CANCEL' ) { this.sipCamera.console.log('CANCEL voice call from: ' + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) ) - this.reset() + this.sipCamera?.reset() } if( request.method === 'INVITE' ) { this.sipCamera.console.log("INCOMING voice call from: " + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) ) @@ -21,14 +21,10 @@ export class InviteHandler extends SipRequestHandler { setTimeout( () => { // Assumption that flexisip only holds this call active for 20 seconds ... might be revised - this.reset() + this.sipCamera?.reset() }, 20 * 1000 ) } } - reset() { - this.sipCamera.console.log("Reset the incoming call request") - this.sipCamera.incomingCallRequest = undefined - this.sipCamera.binaryState = false - } + } \ No newline at end of file diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 9ddab9c54a..6943400931 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -67,7 +67,8 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid ScryptedInterface.Settings, ScryptedInterface.Intercom, ScryptedInterface.BinarySensor, - ScryptedDeviceType.DeviceProvider + ScryptedDeviceType.DeviceProvider, + ScryptedInterface.HttpRequestHandler ], type: ScryptedDeviceType.Doorbell, }) diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts index 775df9f164..e6b3826567 100644 --- a/plugins/bticino/src/persistent-sip-manager.ts +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -32,11 +32,10 @@ export class PersistentSipManager { let now = Date.now() try { let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) - if( Number.isNaN( sipOptions.expire ) || sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { + if( Number.isNaN( sipOptions.expire ) || sipOptions.expire <= 0 || sipOptions.expire > 3600 ) { + sipOptions.expire = 300 + } if( this.expireInterval == 0 ) { - // Safe guard just in case - sipOptions.expire = 300 - } this.expireInterval = (sipOptions.expire * 1000) - 10000 } diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index d4cbb7a39d..65ce16f904 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -46,7 +46,8 @@ export class BticinoStorageSettings { type: 'boolean', description: 'Enable voicemail alerts', placeholder: 'true or false', - }, + }, + /* doorbellWebhookUrl: { title: 'Doorbell Sensor Webhook', type: 'string', @@ -55,7 +56,8 @@ export class BticinoStorageSettings { return camera.webhookUrl; }, description: 'Incoming doorbell sensor webhook url.', - } + } + */ }); } diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index 8cdaeb00f5..e03fb154f4 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -75,7 +75,9 @@ export class SipCallSession extends Subscribed { )) this.addSubscriptions( - call.onEndedByRemote.subscribe(() => this.callEnded(false)) + call.onEndedByRemote.subscribe(() => { + this.callEnded(false) + } ) ) return this.sipManager @@ -173,12 +175,12 @@ export class SipCallSession extends Subscribed { if (this.hasCallEnded) { return } + this.hasCallEnded = true - if (sendBye) { await this.sipManager.sendBye().catch(this.console.error) } - + // clean up this.console.log("sip-call-session callEnded") this.onCallEndedSubject.next(null) diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index 658ca09574..7966592b73 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -229,7 +229,7 @@ export class SipManager { m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri + ":" + remote.port + ";transport=" + remote.protocol + m.headers.contact[0].uri = m.headers.contact[0].uri + ";transport=" + remote.protocol } } } @@ -237,7 +237,13 @@ export class SipManager { if( sipOptions.debugSip ) { console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - console.log(stringify( m )); + if( m.uri ) { + console.log(stringify( m )); + } else { + m.uri = ''; + console.log( stringify( m ) ) + } + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); } }, @@ -249,7 +255,7 @@ export class SipManager { this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) if (this.destroyed) { - this.onEndedByRemote.next(null) + this.onEndedByRemote.next(null) } } else if( request.method === 'MESSAGE' && sipOptions.sipRequestHandler ) { sipOptions.sipRequestHandler.handle( request ) @@ -394,9 +400,10 @@ export class SipManager { .filter((l) => l) .join('\r\n')) + '\r\n' } ) - callResponse.headers["record-route"] = '<' + stringifyUri( incomingCallRequest.headers["record-route"] ) + '>' + if( incomingCallRequest.headers["record-route"] ) + callResponse.headers["record-route"] = incomingCallRequest.headers["record-route"]; callResponse.headers.to.params.tag = '2pAx2dBB' //whatever, just some value - callResponse.headers.contact = [{ uri: incomingCallRequest.headers.to.uri }] + callResponse.headers.contact = [{ uri: from }] await this.sipStack.send(callResponse) return parseRtpDescription(this.console, incomingCallRequest) @@ -436,7 +443,7 @@ export class SipManager { */ async register() : Promise { const { from } = this.sipOptions; - await timeoutPromise( 1000, + await timeoutPromise( 2500, this.request({ method: 'REGISTER', headers: { From 95194df36ceb61e8b5abf23e7c7d8d3f288d4edd Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Wed, 8 Mar 2023 20:40:21 +0100 Subject: [PATCH 20/24] * Add gruu and use a custom fork of sip.js which supports keepAlive SIP clients (and dropped Websocket) * use cross-env in package.json --- plugins/bticino/package.json | 7 +- plugins/bticino/src/bticino-camera.ts | 17 +++- plugins/bticino/src/bticino-inviteHandler.ts | 3 +- plugins/bticino/src/sip-helper.ts | 3 +- plugins/sip/package.json | 7 +- plugins/sip/src/sip-call-session.ts | 6 ++ plugins/sip/src/sip-manager.ts | 93 ++++++++++++++------ 7 files changed, 99 insertions(+), 37 deletions(-) diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index 97cca3cc7f..ce28ae1ccf 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,11 +1,11 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.6", + "version": "0.0.6-beta.7", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", "build": "scrypted-webpack", - "prepublishOnly": "NODE_ENV=production scrypted-webpack", + "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", "prescrypted-vscode-launch": "scrypted-webpack", "scrypted-vscode-launch": "scrypted-deploy-debug", "scrypted-deploy-debug": "scrypted-deploy-debug", @@ -33,8 +33,8 @@ ] }, "dependencies": { + "@slyoldfox/sip": "^0.0.6-1", "sdp": "^3.0.3", - "sip": "0.0.6", "stun": "^2.1.0", "uuid": "^8.3.2" }, @@ -43,6 +43,7 @@ "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3", "ts-node": "^10.9.1" } } diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 9ed7d8d954..96b061c9f4 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -17,12 +17,14 @@ import { BticinoSipLock } from './bticino-lock'; import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; import { PersistentSipManager } from './persistent-sip-manager'; import { InviteHandler } from './bticino-inviteHandler'; -import { SipManager, SipRequest } from '../../sip/src/sip-manager'; +import { SipRequest } from '../../sip/src/sip-manager'; +import crypto from 'crypto'; const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; -export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler { +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { + private session: SipCallSession private remoteRtpDescription: RtpDescription private audioOutForwarder: dgram.Socket @@ -40,9 +42,11 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) private persistentSipManager : PersistentSipManager public webhookUrl : string + private md5 constructor(nativeId: string, public provider: BticinoSipPlugin) { super(nativeId) + this.md5 = crypto.createHash('md5').update( nativeId ).digest("hex") this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) this.persistentSipManager = new PersistentSipManager( this ); (async() => { @@ -224,10 +228,15 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid 'a=recvonly' ] }, this.incomingCallRequest ); + + this.incomingCallRequest = undefined + if( sipOptions.debugSip ) this.log.d('SIP: Received remote SDP:\n' + this.remoteRtpDescription.sdp) let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) + //sdp = sdp.replaceAll(/a=crypto\:1.*/g, '') + //sdp = sdp.replaceAll('\r\n\r\n', '\r\n') sdp = addTrackControls(sdp) sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') if( sipOptions.debugSip ) @@ -362,6 +371,10 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid return `${webhookUrl}`; } + getGruuInstanceId(): string { + return this.md5.substring(0, 8) + '-' + this.md5.substring(8, 12) + '-' + this.md5.substring(12,16) + '-' + this.md5.substring(16, 32); + } + public async onRequest(request: HttpRequest, response: HttpResponse): Promise { if (request.url.endsWith('/buttonPressed')) { this.binaryState = true; diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index 1a43490129..6953cfefe4 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -1,10 +1,11 @@ import { SipRequestHandler, SipRequest } from "../../sip/src/sip-manager" import { BticinoSipCamera } from "./bticino-camera" -import { stringifyUri } from 'sip/sip' +import { stringifyUri } from '@slyoldfox/sip' export class InviteHandler extends SipRequestHandler { constructor( private sipCamera : BticinoSipCamera ) { super() + this.sipCamera.binaryState = false } handle(request: SipRequest) { diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index 5d79426845..e3ffd84eb3 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -32,8 +32,9 @@ export class SipHelper { localPort, shouldRegister: true, debugSip: sipdebug, + gruuInstanceId: camera.getGruuInstanceId(), useTcp: true, sipRequestHandler: camera.requestHandlers } - } + } } \ No newline at end of file diff --git a/plugins/sip/package.json b/plugins/sip/package.json index 6c6f9e5d16..efe80bb799 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -5,7 +5,7 @@ "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", "build": "scrypted-webpack", - "prepublishOnly": "NODE_ENV=production scrypted-webpack", + "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", "prescrypted-vscode-launch": "scrypted-webpack", "scrypted-vscode-launch": "scrypted-deploy-debug", "scrypted-deploy-debug": "scrypted-deploy-debug", @@ -34,8 +34,8 @@ }, "dependencies": { "@homebridge/camera-utils": "^2.0.4", + "@slyoldfox/sip": "^0.0.6-1", "sdp": "^3.0.3", - "sip": "0.0.6", "stun": "^2.1.0", "uuid": "^8.3.2" }, @@ -43,6 +43,7 @@ "@scrypted/common": "file:../../common", "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4" + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3" } } diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index e03fb154f4..0fcd42aa2d 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -27,6 +27,12 @@ export class SipCallSession extends Subscribed { private sipManager: SipManager ) { super() + //TODO: make this more clean + this.addSubscriptions( this.sipManager.onEndedByRemote.subscribe(() => { + this.callEnded(false) + } )) + + sipManager.sipOptions = sipOptions } static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index 7966592b73..555b7824cc 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -2,10 +2,11 @@ import { noop, Subject } from 'rxjs' import { randomInteger, randomString } from './util' import { RtpDescription, RtpOptions, RtpStreamDescription } from './rtp-utils' import { decodeSrtpOptions } from '../../ring/src/srtp-utils' -import { stringify, stringifyUri } from 'sip/sip' +import { stringify, stringifyUri } from '@slyoldfox/sip' import { timeoutPromise } from '@scrypted/common/src/promise-utils'; +import sdp from 'sdp' -const sip = require('sip'), sdp = require('sdp') +const sip = require('@slyoldfox/sip') export interface SipOptions { to: string @@ -16,6 +17,7 @@ export interface SipOptions { localPort: number debugSip?: boolean useTcp?: boolean + gruuInstanceId?: string sipRequestHandler?: SipRequestHandler shouldRegister?: boolean } @@ -159,13 +161,11 @@ export class SipManager { constructor( console: Console, - private sipOptions: SipOptions, + public sipOptions: SipOptions, ) { this.console = console; - const host = this.sipOptions.localIp, - port = this.sipOptions.localPort, - contactId = randomInteger() + port = this.sipOptions.localPort this.sipStack = { makeResponse: sip.makeResponse, @@ -183,7 +183,7 @@ export class SipManager { ws: false, logger: { recv: function(m, remote) { - if( m.status == '200' && m.reason =='Ok' && m.headers.contact ) { + if( (m.status == '200' || m.method === 'INVITE' ) && m.headers && m.headers.cseq && m.headers.cseq.method === 'INVITE' && m.headers.contact && m.headers.contact[0] ) { // ACK for INVITE and BYE must use the registrar contact uri this.registrarContact = m.headers.contact[0].uri; } @@ -193,6 +193,19 @@ export class SipManager { console.log("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") } }, + appendGruu: function( contact, gruuUrn ) { + if( sipOptions.gruuInstanceId ) { + if( contact && contact[0] ) { + if( !contact[0].params ) { + contact[0].params = {} + } + contact[0].params['+sip.instance'] = '""' + if( gruuUrn ) { + contact[0].uri = contact[0].uri + ';gr=urn:uuid:' + sipOptions.gruuInstanceId + } + } + } + }, send: function(m, remote) { /* Some door bells run an embedded SIP server with an unresolvable public domain @@ -200,14 +213,15 @@ export class SipManager { just before they get sent to the SIP server. */ if( sipOptions.domain && sipOptions.domain.length > 0 ) { - // Bticino CX300 specific: runs on an internet 2048362.bs.iotleg.com domain - // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain - let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() - let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() + // Bticino CX300 specific: runs on an internet 2048362.bs.iotleg.com domain + // While underlying UDP socket is bound to the IP, the header is rewritten to match the domain + let toWithDomain: string = (sipOptions.to.split('@')[0] + '@' + sipOptions.domain).trim() + let fromWithDomain: string = (sipOptions.from.split('@')[0] + '@' + sipOptions.domain).trim() if( m.method == 'REGISTER' ) { m.uri = "sip:" + sipOptions.domain m.headers.to.uri = fromWithDomain + this.appendGruu( m.headers.contact ) } else if( m.method == 'INVITE' || m.method == 'MESSAGE' ) { m.uri = toWithDomain m.headers.to.uri = toWithDomain @@ -218,6 +232,11 @@ export class SipManager { m.headers.to.uri = toWithDomain m.uri = this.registrarContact } else if( (m.method == undefined && m.status) && m.headers.cseq ) { + if( m.status == '200' ) { + // Response on invite + this.appendGruu( m.headers.contact, true ) + } + // 183, 200, OK, CSeq: INVITE } else { console.error("Error: Method construct for uri not implemented: " + m.method) @@ -226,7 +245,6 @@ export class SipManager { if( m.method ) { m.headers.from.uri = fromWithDomain if( m.headers.contact && m.headers.contact[0].uri.split('@')[0].lastIndexOf('-') < 0 ) { - m.headers.contact[0].uri = m.headers.contact[0].uri.replace("@", "-" + contactId + "@"); // Also a bug in SIP.js ? append the transport for the contact if the transport is udp (according to RFC) if( remote.protocol != 'udp' && m.headers.contact[0].uri.indexOf( "transport=" ) < 0 ) { m.headers.contact[0].uri = m.headers.contact[0].uri + ";transport=" + remote.protocol @@ -254,19 +272,27 @@ export class SipManager { this.console.info('received BYE from remote end') this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) - if (this.destroyed) { - this.onEndedByRemote.next(null) + if (!this.destroyed) { + this.onEndedByRemote.next(null) } } else if( request.method === 'MESSAGE' && sipOptions.sipRequestHandler ) { sipOptions.sipRequestHandler.handle( request ) this.sipStack.send(this.sipStack.makeResponse(request, 200, 'Ok')) } else if( request.method === 'INVITE' && sipOptions.sipRequestHandler ) { - let ringResponse = this.sipStack.makeResponse(request, 180, 'Ringing') - ringResponse.headers["Supported"] = "replaces, outbound, gruu" - ringResponse.headers.to.params.tag = '2pAx2dBB' - this.sipStack.send(ringResponse) - sipOptions.sipRequestHandler.handle( request ) - } else if( request.method === 'CANCEL' ) { + //let tryingResponse = this.sipStack.makeResponse( request, 100, 'Trying' ) + //this.sipStack.send(tryingResponse) + //TODO: sporadic re-INVITEs are possible and should reply with 486 Busy here if already being handled + let ringResponse = this.sipStack.makeResponse(request, 180, 'Ringing') + this.toParams.tag = getRandomId() + ringResponse.headers.to.params.tag = this.toParams.tag + ringResponse.headers["record-route"] = request.headers["record-route"]; + ringResponse.headers["supported"] = "replaces, outbound, gruu" + // Can include SDP and could send 183 here for early media + this.sipStack.send(ringResponse) + + sipOptions.sipRequestHandler.handle( request ) + // }, 100 ) + } else if( request.method === 'CANCEL' || request.method === 'ACK' ) { sipOptions.sipRequestHandler.handle( request ) } else { if( sipOptions.debugSip ) { @@ -322,6 +348,10 @@ export class SipManager { if (response.headers.to.params && response.headers.to.params.tag) { this.toParams.tag = response.headers.to.params.tag } + + if (response.headers.from.params && response.headers.from.params.tag) { + this.fromParams.tag = response.headers.from.params.tag + } if (response.status >= 300) { if (response.status !== 408 || method !== 'BYE') { @@ -383,7 +413,7 @@ export class SipManager { if( incomingCallRequest ) { let callResponse = this.sipStack.makeResponse(incomingCallRequest, 200, 'Ok', { headers: { - supported: 'replaces, outbound', + supported: 'replaces, outbound, gruu', allow: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', 'content-type': 'application/sdp', @@ -402,8 +432,14 @@ export class SipManager { } ) if( incomingCallRequest.headers["record-route"] ) callResponse.headers["record-route"] = incomingCallRequest.headers["record-route"]; - callResponse.headers.to.params.tag = '2pAx2dBB' //whatever, just some value - callResponse.headers.contact = [{ uri: from }] + let fromWithDomain: string = (from.split('@')[0] + '@' + this.sipOptions.domain).trim() + callResponse.headers.contact = [{ uri: fromWithDomain }] + + // Invert the params if the request comes from the server + this.fromParams.tag = incomingCallRequest.headers.to.params.tag + this.toParams.tag = incomingCallRequest.headers.from.params.tag + this.callId = incomingCallRequest.headers["call-id"] + await this.sipStack.send(callResponse) return parseRtpDescription(this.console, incomingCallRequest) @@ -411,11 +447,11 @@ export class SipManager { if( this.sipOptions.to.toLocaleLowerCase().indexOf('c300x') >= 0 ) { // Needed for bt_answering_machine (bticino specific) audio.unshift('a=DEVADDR:20') - } + } let inviteResponse = await this.request({ method: 'INVITE', headers: { - supported: 'replaces, outbound', + supported: 'replaces, outbound, gruu', allow: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', 'content-type': 'application/sdp', @@ -447,10 +483,11 @@ export class SipManager { this.request({ method: 'REGISTER', headers: { - //supported: 'replaces, outbound', + supported: 'replaces, outbound, gruu', allow: 'INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO, UPDATE', - contact: [{ uri: from, params: { expires: this.sipOptions.expire } }], + contact: [{ uri: from }], + expires: this.sipOptions.expire // as seen in tcpdump for Door Entry app }, }).catch(noop)); } @@ -488,3 +525,5 @@ export class SipManager { this.console.debug("detroying sip-manager: done") } } + + From 4814470564fce1314d1f03e19b0fd980d3de630e Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Sun, 12 Mar 2023 15:58:33 +0100 Subject: [PATCH 21/24] Added webhook urls for faster handling of events --- plugins/bticino/src/bticino-camera.ts | 83 ++++++++++++------- plugins/bticino/src/bticino-inviteHandler.ts | 3 +- plugins/bticino/src/bticino-lock.ts | 32 ++++++- plugins/bticino/src/main.ts | 2 +- plugins/bticino/src/persistent-sip-manager.ts | 11 ++- plugins/bticino/src/sip-helper.ts | 1 - plugins/bticino/src/storage-settings.ts | 15 +++- plugins/sip/src/sip-call-session.ts | 9 +- plugins/sip/src/sip-manager.ts | 9 +- 9 files changed, 114 insertions(+), 51 deletions(-) diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 96b061c9f4..bdc3b8acde 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -23,7 +23,7 @@ import crypto from 'crypto'; const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; -export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor { +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler { private session: SipCallSession private remoteRtpDescription: RtpDescription @@ -41,7 +41,8 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) private persistentSipManager : PersistentSipManager - public webhookUrl : string + public doorbellWebhookUrl : string + public doorbellLockWebhookUrl : string private md5 constructor(nativeId: string, public provider: BticinoSipPlugin) { @@ -50,7 +51,8 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) this.persistentSipManager = new PersistentSipManager( this ); (async() => { - this.webhookUrl = await this.buttonPressedDetectedWebhookUrl() + this.doorbellWebhookUrl = await this.doorbellWebhookEndpoint() + this.doorbellLockWebhookUrl = await this.doorbellLockWebhookEndpoint() })(); } @@ -144,6 +146,10 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT) } + hasActiveCall() { + return this.session; + } + stopSession() { if (this.session) { this.log.d('ending sip session') @@ -201,9 +207,6 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid let sipOptions = SipHelper.sipOptions( this ) - // A normal call session doesn't require registering - sipOptions.shouldRegister = false - sip = await this.persistentSipManager.session( sipOptions ); // Validate this sooner if( !sip ) return Promise.reject("Cannot create session") @@ -214,19 +217,27 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.remoteRtpDescription = await sip.callOrAcceptInvite( ( audio ) => { return [ - + //TODO: Payload types are hardcoded `m=audio ${audio.port} RTP/SAVP 97`, `a=rtpmap:97 speex/8000`, `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, ] }, ( video ) => { - return [ - `m=video ${video.port} RTP/SAVP 97`, - `a=rtpmap:97 H264/90000`, - `a=fmtp:97 profile-level-id=42801F`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, - 'a=recvonly' - ] + if( false ) { + //TODO: implement later + return [ + `m=video 0 RTP/SAVP 0` + ] + } else { + return [ + //TODO: Payload types are hardcoded + `m=video ${video.port} RTP/SAVP 97`, + `a=rtpmap:97 H264/90000`, + `a=fmtp:97 profile-level-id=42801F`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, + 'a=recvonly' + ] + } }, this.incomingCallRequest ); this.incomingCallRequest = undefined @@ -235,7 +246,9 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.log.d('SIP: Received remote SDP:\n' + this.remoteRtpDescription.sdp) let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) + //sdp = sdp.replaceAll(/a=crypto\:1.*/g, '') + //sdp = sdp.replaceAll(/RTP\/SAVP/g, 'RTP\/AVP') //sdp = sdp.replaceAll('\r\n\r\n', '\r\n') sdp = addTrackControls(sdp) sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') @@ -364,27 +377,13 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.binaryState = false } - private async buttonPressedDetectedWebhookUrl(): Promise { - let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId, { insecure: false }); - webhookUrl += "buttonPressed"; - this.console.log( webhookUrl ) - return `${webhookUrl}`; - } - - getGruuInstanceId(): string { - return this.md5.substring(0, 8) + '-' + this.md5.substring(8, 12) + '-' + this.md5.substring(12,16) + '-' + this.md5.substring(16, 32); - } - public async onRequest(request: HttpRequest, response: HttpResponse): Promise { - if (request.url.endsWith('/buttonPressed')) { - this.binaryState = true; - + if (request.url.endsWith('/pressed')) { + this.binaryState = true setTimeout( () => { - // Remove duplicate code // Assumption that flexisip only holds this call active for 20 seconds ... might be revised this.reset() - }, 20 * 1000 ) - + }, 20 * 1000 ) response.send('Success', { code: 200, }); @@ -393,5 +392,25 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid code: 400, }); } - } + } + + private async doorbellWebhookEndpoint(): Promise { + let webhookUrl = await sdk.endpointManager.getLocalEndpoint( this.nativeId, { insecure: false, public: true }); + let endpoints = ["/pressed"] + this.console.log( webhookUrl + " , endpoints: " + endpoints.join(' - ') ) + return `${webhookUrl}`; + } + + private async doorbellLockWebhookEndpoint(): Promise { + let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId + '-lock', { insecure: false, public: true }); + let endpoints = ["/lock", "/unlock", "/unlocked", "/locked"] + this.console.log( webhookUrl + " -> endpoints: " + endpoints.join(' - ') ) + return `${webhookUrl}`; + } + + + + getGruuInstanceId(): string { + return this.md5.substring(0, 8) + '-' + this.md5.substring(8, 12) + '-' + this.md5.substring(12,16) + '-' + this.md5.substring(16, 32); + } } \ No newline at end of file diff --git a/plugins/bticino/src/bticino-inviteHandler.ts b/plugins/bticino/src/bticino-inviteHandler.ts index 6953cfefe4..cf13131c6a 100644 --- a/plugins/bticino/src/bticino-inviteHandler.ts +++ b/plugins/bticino/src/bticino-inviteHandler.ts @@ -11,7 +11,8 @@ export class InviteHandler extends SipRequestHandler { handle(request: SipRequest) { //TODO: restrict this to call from:c300x@ AND to:alluser@ ? if( request.method == 'CANCEL' ) { - this.sipCamera.console.log('CANCEL voice call from: ' + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) ) + let reason = request.headers["reason"] ? ( ' - ' + request.headers["reason"] ) : '' + this.sipCamera.console.log('CANCEL voice call from: ' + stringifyUri( request.headers.from.uri ) + ' to: ' + stringifyUri( request.headers.to.uri ) + reason ) this.sipCamera?.reset() } if( request.method === 'INVITE' ) { diff --git a/plugins/bticino/src/bticino-lock.ts b/plugins/bticino/src/bticino-lock.ts index 0ef6dd5566..da31b41d2c 100644 --- a/plugins/bticino/src/bticino-lock.ts +++ b/plugins/bticino/src/bticino-lock.ts @@ -1,7 +1,7 @@ -import { ScryptedDeviceBase, Lock, LockState } from "@scrypted/sdk"; +import sdk, { ScryptedDeviceBase, Lock, LockState, HttpRequest, HttpResponse, HttpRequestHandler } from "@scrypted/sdk"; import { BticinoSipCamera } from "./bticino-camera"; -export class BticinoSipLock extends ScryptedDeviceBase implements Lock { +export class BticinoSipLock extends ScryptedDeviceBase implements Lock, HttpRequestHandler { private timeout : NodeJS.Timeout constructor(public camera: BticinoSipCamera) { @@ -25,4 +25,32 @@ export class BticinoSipLock extends ScryptedDeviceBase implements Lock { this.lock() return this.camera.sipUnlock() } + + public async onRequest(request: HttpRequest, response: HttpResponse): Promise { + if (request.url.endsWith('/unlocked')) { + this.lockState = LockState.Unlocked + response.send('Success', { + code: 200, + }); + } else if( request.url.endsWith('/locked') ) { + this.lockState = LockState.Locked + response.send('Success', { + code: 200, + }); + } else if( request.url.endsWith('/lock') ) { + this.lock(); + response.send('Success', { + code: 200, + }); + } else if( request.url.endsWith('/unlock') ) { + this.unlock(); + response.send('Success', { + code: 200, + }); + } else { + response.send('Unsupported operation', { + code: 400, + }); + } + } } \ No newline at end of file diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 6943400931..5c84db72e3 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -34,7 +34,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid nativeId: nativeId + '-lock', name: name + ' Lock', type: ScryptedDeviceType.Lock, - interfaces: [ScryptedInterface.Lock], + interfaces: [ScryptedInterface.Lock, ScryptedInterface.HttpRequestHandler], } const ret = await deviceManager.onDevicesChanged({ diff --git a/plugins/bticino/src/persistent-sip-manager.ts b/plugins/bticino/src/persistent-sip-manager.ts index e6b3826567..814de967f6 100644 --- a/plugins/bticino/src/persistent-sip-manager.ts +++ b/plugins/bticino/src/persistent-sip-manager.ts @@ -17,7 +17,7 @@ export class PersistentSipManager { constructor( private camera : BticinoSipCamera ) { // Give it a second and run in seperate thread to avoid failure on creation for from/to/domain check - setTimeout( () => this.register(), CHECK_INTERVAL ) + setTimeout( () => this.enable() , CHECK_INTERVAL ) } async enable() : Promise { @@ -39,7 +39,7 @@ export class PersistentSipManager { this.expireInterval = (sipOptions.expire * 1000) - 10000 } - if( now - this.lastRegistration >= this.expireInterval ) { + if( !this.camera.hasActiveCall() && now - this.lastRegistration >= this.expireInterval ) { let sipOptions : SipOptions = SipHelper.sipOptions( this.camera ) this.sipManager?.destroy() @@ -61,6 +61,11 @@ export class PersistentSipManager { } async session( sipOptions: SipOptions ) : Promise { - return SipCallSession.createCallSession(this.camera.console, "Bticino", sipOptions, this.sipManager ) + let sm = await this.enable() + return SipCallSession.createCallSession(this.camera.console, "Bticino", sipOptions, sm ) + } + + reloadSipOptions() { + this.sipManager?.setSipOptions( null ) } } \ No newline at end of file diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index e3ffd84eb3..18d16bf167 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -30,7 +30,6 @@ export class SipHelper { expire: Number.parseInt( expiration ), localIp, localPort, - shouldRegister: true, debugSip: sipdebug, gruuInstanceId: camera.getGruuInstanceId(), useTcp: true, diff --git a/plugins/bticino/src/storage-settings.ts b/plugins/bticino/src/storage-settings.ts index 65ce16f904..09987260ad 100644 --- a/plugins/bticino/src/storage-settings.ts +++ b/plugins/bticino/src/storage-settings.ts @@ -47,17 +47,24 @@ export class BticinoStorageSettings { description: 'Enable voicemail alerts', placeholder: 'true or false', }, - /* doorbellWebhookUrl: { title: 'Doorbell Sensor Webhook', type: 'string', readonly: true, mapGet: () => { - return camera.webhookUrl; + return camera.doorbellWebhookUrl; }, description: 'Incoming doorbell sensor webhook url.', - } - */ + }, + doorbellLockWebhookUrl: { + title: 'Doorbell Lock Webhook', + type: 'string', + readonly: true, + mapGet: () => { + return camera.doorbellLockWebhookUrl; + }, + description: 'Incoming doorbell sensor webhook url.', + } }); } diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index 0fcd42aa2d..bd132be9cc 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -27,15 +27,18 @@ export class SipCallSession extends Subscribed { private sipManager: SipManager ) { super() + if( !sipManager ) { + this.sipManager = this.createSipManager( sipOptions ) + } //TODO: make this more clean this.addSubscriptions( this.sipManager.onEndedByRemote.subscribe(() => { this.callEnded(false) } )) - sipManager.sipOptions = sipOptions + sipManager.setSipOptions( sipOptions ) } - static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { + static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager: SipManager ) { const audioSplitter = await createBindZero(), audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), videoSplitter = await createBindZero(), @@ -106,8 +109,6 @@ export class SipCallSession extends Subscribed { } try { - if( this.sipOptions.shouldRegister ) - await this.sipManager.register() const rtpDescription : RtpDescription = await this.sipManager.invite( this.rtpOptions, audioSection, videoSection, incomingCallRequest ), sendStunRequests = () => { sendStunBindingRequest({ diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index 555b7824cc..b7e846072d 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -19,7 +19,6 @@ export interface SipOptions { useTcp?: boolean gruuInstanceId?: string sipRequestHandler?: SipRequestHandler - shouldRegister?: boolean } /** @@ -161,7 +160,7 @@ export class SipManager { constructor( console: Console, - public sipOptions: SipOptions, + private sipOptions: SipOptions, ) { this.console = console; const host = this.sipOptions.localIp, @@ -390,6 +389,10 @@ export class SipManager { }).catch(noop) } + public setSipOptions( sipOptions : SipOptions ) { + this.sipOptions = sipOptions + } + sendDtmf(key: string) { return this.request({ method: 'INFO', @@ -479,7 +482,7 @@ export class SipManager { */ async register() : Promise { const { from } = this.sipOptions; - await timeoutPromise( 2500, + await timeoutPromise( 3000, this.request({ method: 'REGISTER', headers: { From fdad93b974c22bc9c86c0ac8617cbe829860d51e Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Thu, 16 Mar 2023 19:46:34 +0100 Subject: [PATCH 22/24] Added videoclips --- plugins/bticino/package-lock.json | 198 ++++++++++++++++++++------ plugins/bticino/package.json | 2 +- plugins/bticino/src/bticino-camera.ts | 165 +++++++++++---------- plugins/bticino/src/main.ts | 5 +- plugins/bticino/src/sip-helper.ts | 3 + plugins/sip/package-lock.json | 96 ++++++------- plugins/sip/src/sip-manager.ts | 1 + 7 files changed, 290 insertions(+), 180 deletions(-) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 11c1fca6f2..06a9660e27 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,15 +1,15 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.6", + "version": "0.0.7-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/bticino", - "version": "0.0.6-beta.6", + "version": "0.0.7-beta.0", "dependencies": { + "@slyoldfox/sip": "^0.0.6-1", "sdp": "^3.0.3", - "sip": "0.0.6", "stun": "^2.1.0", "uuid": "^8.3.2" }, @@ -18,10 +18,12 @@ "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3", "ts-node": "^10.9.1" } }, "../../common": { + "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -37,7 +39,8 @@ } }, "../../sdk": { - "version": "0.2.55", + "name": "@scrypted/sdk", + "version": "0.2.82", "dev": true, "license": "ISC", "dependencies": { @@ -57,11 +60,11 @@ "webpack-bundle-analyzer": "^4.5.0" }, "bin": { + "scrypted-changelog": "bin/scrypted-changelog.js", "scrypted-debug": "bin/scrypted-debug.js", "scrypted-deploy": "bin/scrypted-deploy.js", "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", "scrypted-package-json": "bin/scrypted-package-json.js", - "scrypted-readme": "bin/scrypted-readme.js", "scrypted-setup-project": "bin/scrypted-setup-project.js", "scrypted-webpack": "bin/scrypted-webpack.js" }, @@ -118,6 +121,14 @@ "resolved": "../../sdk", "link": true }, + "node_modules/@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", + "engines": { + "node": ">=0.2.2" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -197,11 +208,6 @@ "node": ">=0.10.0" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "node_modules/binary-data": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", @@ -261,6 +267,38 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -503,6 +541,12 @@ "node": ">=4" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -706,6 +750,15 @@ "node": ">=4" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -859,6 +912,27 @@ "semver": "bin/semver" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -877,17 +951,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/sip": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", - "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", - "dependencies": { - "ws": "^6.1.0" - }, - "engines": { - "node": ">=0.2.2" - } - }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1086,12 +1149,19 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { - "async-limiter": "~1.0.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/yargs-parser": { @@ -1179,6 +1249,11 @@ "webpack-bundle-analyzer": "^4.5.0" } }, + "@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" + }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -1243,11 +1318,6 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "binary-data": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", @@ -1295,6 +1365,26 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1476,6 +1566,12 @@ "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -1634,6 +1730,12 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -1734,6 +1836,21 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -1749,14 +1866,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "sip": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", - "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", - "requires": { - "ws": "^6.1.0" - } - }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1890,12 +1999,13 @@ "spdx-expression-parse": "^3.0.0" } }, - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { - "async-limiter": "~1.0.0" + "isexe": "^2.0.0" } }, "yargs-parser": { diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index ce28ae1ccf..8f11a5fa35 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/bticino", - "version": "0.0.6-beta.7", + "version": "0.0.7-beta.0", "scripts": { "scrypted-setup-project": "scrypted-setup-project", "prescrypted-setup-project": "scrypted-package-json", diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index bdc3b8acde..5a06f20449 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -2,7 +2,7 @@ import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/co import { sleep } from '@scrypted/common/src/sleep'; import { RtspServer } from '@scrypted/common/src/rtsp-server'; import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from '@scrypted/sdk'; +import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk'; import { SipCallSession } from '../../sip/src/sip-call-session'; import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, RtpDescription } from '../../sip/src/rtp-utils'; import { VoicemailHandler } from './bticino-voicemailHandler'; @@ -11,6 +11,7 @@ import { decodeSrtpOptions, encodeSrtpOptions, SrtpOptions } from '../../ring/sr import { SipHelper } from './sip-helper'; import child_process, { ChildProcess } from 'child_process'; import dgram from 'dgram'; +import fs from 'fs' import { BticinoStorageSettings } from './storage-settings'; import { BticinoSipPlugin } from './main'; import { BticinoSipLock } from './bticino-lock'; @@ -19,11 +20,12 @@ import { PersistentSipManager } from './persistent-sip-manager'; import { InviteHandler } from './bticino-inviteHandler'; import { SipRequest } from '../../sip/src/sip-manager'; import crypto from 'crypto'; +import { get } from 'http' const STREAM_TIMEOUT = 65000; const { mediaManager } = sdk; -export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler { +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips { private session: SipCallSession private remoteRtpDescription: RtpDescription @@ -55,6 +57,51 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.doorbellLockWebhookUrl = await this.doorbellLockWebhookEndpoint() })(); } + getVideoClips(options?: VideoClipOptions): Promise { + return new Promise( (resolve,reject ) => { + let c300x = SipHelper.sipOptions(this).deviceIp + get(`http://${c300x}:8080/videoclips?raw=true&startTime=${options.startTime/1000}&endTime=${options.endTime/1000}`, (res) => { + let rawData = ''; + res.on('data', (chunk) => { rawData += chunk; }); + res.on('end', () => { + try { + const parsedData : [] = JSON.parse(rawData); + let videoClips : VideoClip[] = [] + parsedData.forEach( (item) => { + let videoClip : VideoClip = { + id: item['file'], + startTime: parseInt(item['info']['UnixTime']) * 1000, + duration: item['info']['Duration'] * 1000, + //description: item['info']['Date'], + thumbnailId: item['file'] + + } + videoClips.push( videoClip ) + } ) + return resolve(videoClips) + } catch (e) { + reject(e.message) + console.error(e.message); + } + }) + }); + }); + } + + getVideoClip(videoId: string): Promise { + let c300x = SipHelper.sipOptions(this).deviceIp + const url = `http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`; + return mediaManager.createMediaObjectFromUrl(url); + } + getVideoClipThumbnail(thumbnailId: string): Promise { + let c300x = SipHelper.sipOptions(this).deviceIp + const url = `http://${c300x}:8080/voicemail?msg=${thumbnailId}/aswm.jpg&raw=true`; + return mediaManager.createMediaObjectFromUrl(url); + } + removeVideoClips(...videoClipIds: string[]): Promise { + //TODO + throw new Error('Method not implemented.') + } sipUnlock(): Promise { this.log.i("unlocking C300X door ") @@ -97,12 +144,11 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString()); - const rtpOptions = this.remoteRtpDescription const audioOutForwarder = await createBindZero() this.audioOutForwarder = audioOutForwarder.server audioOutForwarder.server.on('message', message => { if( this.session ) - this.session.audioSplitter.send(message, rtpOptions.audio.port, rtpOptions.address) + this.session.audioSplitter.send(message, 40004, this.remoteRtpDescription.address) return null }); @@ -114,9 +160,9 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid '-ac', '1', '-ar', '8k', '-f', 'rtp', - '-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', - '-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions), - `srtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`, + //'-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', + //'-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions), + `rtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`, ); this.console.log("===========================================") @@ -182,7 +228,7 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient() - const playbackUrl = `rtsp://127.0.0.1:${playbackPort}` + const playbackUrl = clientUrl playbackPromise.then(async (client) => { client.setKeepAlive(true, 10000) @@ -218,8 +264,8 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid ( audio ) => { return [ //TODO: Payload types are hardcoded - `m=audio ${audio.port} RTP/SAVP 97`, - `a=rtpmap:97 speex/8000`, + `m=audio 65000 RTP/SAVP 110`, + `a=rtpmap:110 speex/8000`, `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, ] }, ( video ) => { @@ -231,9 +277,9 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid } else { return [ //TODO: Payload types are hardcoded - `m=video ${video.port} RTP/SAVP 97`, - `a=rtpmap:97 H264/90000`, - `a=fmtp:97 profile-level-id=42801F`, + `m=video 65002 RTP/SAVP 96`, + `a=rtpmap:96 H264/90000`, + `a=fmtp:96 profile-level-id=42801F`, `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, 'a=recvonly' ] @@ -242,11 +288,16 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid this.incomingCallRequest = undefined - if( sipOptions.debugSip ) - this.log.d('SIP: Received remote SDP:\n' + this.remoteRtpDescription.sdp) - - let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) - + //let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) + let sdp : string = [ + "v=0", + "m=audio 5000 RTP/AVP 110", + "c=IN IP4 172.21.0.134", + "a=rtpmap:110 speex/8000/1", + "m=video 5002 RTP/AVP 96", + "c=IN IP4 172.21.0.134", + "a=rtpmap:96 H264/90000", + ].join('\r\n') //sdp = sdp.replaceAll(/a=crypto\:1.*/g, '') //sdp = sdp.replaceAll(/RTP\/SAVP/g, 'RTP\/AVP') //sdp = sdp.replaceAll('\r\n\r\n', '\r\n') @@ -255,69 +306,10 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid if( sipOptions.debugSip ) this.log.d('SIP: Updated SDP:\n' + sdp); - let vseq = 0, vseen = 0, vlost = 0, aseq = 0, aseen = 0, alost = 0; - - rtsp = new RtspServer(client, sdp, true); - const parsedSdp = parseSdp(rtsp.sdp); - const videoTrack = parsedSdp.msections.find(msection => msection.type === 'video')?.control - const audioTrack = parsedSdp.msections.find(msection => msection.type === 'audio')?.control - - if( !videoTrack || !audioTrack ) - throw new Error("Video track and/or audio track not found") - - if( sipOptions.debugSip ) { - rtsp.console = this.console - } - - await rtsp.handlePlayback(); - sip.videoSplitter.on('message', message => { - if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)) - if (!isRtpMessage) - return - vseen++; - rtsp.sendTrack(videoTrack, message, !isRtpMessage) - const seq = getSequenceNumber(message) - if (seq !== (vseq + 1) % 0x0FFFF) - vlost++ - vseq = seq - } - }); - - sip.videoRtcpSplitter.on('message', message => { - rtsp.sendTrack(videoTrack, message, true) - }); - - sip.audioSplitter.on('message', message => { - if (!isStunMessage(message)) { - const isRtpMessage = isRtpMessagePayloadType(getPayloadType(message)) - if (!isRtpMessage) - return; - aseen++; - rtsp.sendTrack(audioTrack, message, !isRtpMessage) - const seq = getSequenceNumber(message) - if (seq !== (aseq + 1) % 0x0FFFF) - alost++; - aseq = seq - } - }); - - sip.audioRtcpSplitter.on('message', message => { - rtsp.sendTrack(audioTrack, message, true) - }); + client.write(sdp) + client.end() this.session = sip - - try { - await rtsp.handleTeardown() - this.log.d('rtsp client ended') - } - catch (e) { - this.log.e('rtsp client ended ungracefully' + e); - } - finally { - cleanup() - } } catch (e) { this.console.error(e) @@ -332,14 +324,19 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid refreshAt: Date.now() + STREAM_TIMEOUT, }); - const mediaStreamUrl: MediaStreamUrl = { - url: playbackUrl, + const ffmpegInput: FFmpegInput = { + url: undefined, + container: 'sdp', mediaStreamOptions, + inputArguments: [ + '-f', 'sdp', + '-i', playbackUrl, + ], }; - this.currentMedia = mediaStreamUrl; - this.currentMediaMimeType = ScryptedMimeTypes.MediaStreamUrl; + this.currentMedia = ffmpegInput; + this.currentMediaMimeType = ScryptedMimeTypes.FFmpegInput; - return mediaManager.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl); + return mediaManager.createFFmpegMediaObject(ffmpegInput); } getSipMediaStreamOptions(): ResponseMediaStreamOptions { diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index 5c84db72e3..f8ce97d83f 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -1,4 +1,4 @@ -import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting } from '@scrypted/sdk' +import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, MediaObject, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk' import { randomBytes } from 'crypto' import { BticinoSipCamera } from './bticino-camera' @@ -68,7 +68,8 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid ScryptedInterface.Intercom, ScryptedInterface.BinarySensor, ScryptedDeviceType.DeviceProvider, - ScryptedInterface.HttpRequestHandler + ScryptedInterface.HttpRequestHandler, + ScryptedInterface.VideoClips ], type: ScryptedDeviceType.Doorbell, }) diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index 18d16bf167..7cadac22b0 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -10,6 +10,7 @@ export class SipHelper { const from = camera.storage.getItem('sipfrom')?.trim() const to = camera.storage.getItem('sipto')?.trim() const localIp = from?.split(':')[0].split('@')[1] + const deviceIp = to.split('@')[1] // Although this might not occur directly, each camera should run on its own port // Might need to use a random free port here (?) const localPort = parseInt(from?.split(':')[1]) || 5060 @@ -23,6 +24,7 @@ export class SipHelper { } return { + deviceIp: deviceIp, from: "sip:" + from, //TCP is more reliable for large messages, also see useTcp=true below to: "sip:" + to + ";transport=tcp", @@ -34,6 +36,7 @@ export class SipHelper { gruuInstanceId: camera.getGruuInstanceId(), useTcp: true, sipRequestHandler: camera.requestHandlers + } } } \ No newline at end of file diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index 462f1a8dfd..aa389a61cc 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -9,8 +9,8 @@ "version": "0.0.6-beta.4", "dependencies": { "@homebridge/camera-utils": "^2.0.4", + "@slyoldfox/sip": "^0.0.6-1", "sdp": "^3.0.3", - "sip": "0.0.6", "stun": "^2.1.0", "uuid": "^8.3.2" }, @@ -18,10 +18,12 @@ "@scrypted/common": "file:../../common", "@scrypted/sdk": "file:../../sdk", "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4" + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3" } }, "../../common": { + "name": "@scrypted/common", "version": "1.0.1", "dev": true, "license": "ISC", @@ -37,7 +39,8 @@ } }, "../../sdk": { - "version": "0.2.55", + "name": "@scrypted/sdk", + "version": "0.2.82", "dev": true, "license": "ISC", "dependencies": { @@ -57,11 +60,11 @@ "webpack-bundle-analyzer": "^4.5.0" }, "bin": { + "scrypted-changelog": "bin/scrypted-changelog.js", "scrypted-debug": "bin/scrypted-debug.js", "scrypted-deploy": "bin/scrypted-deploy.js", "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", "scrypted-package-json": "bin/scrypted-package-json.js", - "scrypted-readme": "bin/scrypted-readme.js", "scrypted-setup-project": "bin/scrypted-setup-project.js", "scrypted-webpack": "bin/scrypted-webpack.js" }, @@ -93,6 +96,14 @@ "resolved": "../../sdk", "link": true }, + "node_modules/@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", + "engines": { + "node": ">=0.2.2" + } + }, "node_modules/@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -121,11 +132,6 @@ "node": ">=0.10.0" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "node_modules/binary-data": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", @@ -187,6 +193,24 @@ "node": ">=10" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1110,17 +1134,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/sip": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", - "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", - "dependencies": { - "ws": "^6.1.0" - }, - "engines": { - "node": ">=0.2.2" - } - }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1329,14 +1342,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -1399,6 +1404,11 @@ "webpack-bundle-analyzer": "^4.5.0" } }, + "@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" + }, "@types/node": { "version": "16.18.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", @@ -1421,11 +1431,6 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "binary-data": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", @@ -1472,6 +1477,15 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2127,14 +2141,6 @@ "simple-concat": "^1.0.0" } }, - "sip": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/sip/-/sip-0.0.6.tgz", - "integrity": "sha512-t+FYic4EQ25GTsIRWFVvsq+GmVkoZhrcoghANlnN6CsWMHGcfjPDYMD+nTBNrHR/WnRykF4nqx4i+gahAnW5NA==", - "requires": { - "ws": "^6.1.0" - } - }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2275,14 +2281,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "requires": { - "async-limiter": "~1.0.0" - } - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index b7e846072d..341843fd7d 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -19,6 +19,7 @@ export interface SipOptions { useTcp?: boolean gruuInstanceId?: string sipRequestHandler?: SipRequestHandler + deviceIp: string } /** From 47853bdba51cd23788b25339de40ddce5211b58e Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Mon, 20 Mar 2023 12:59:31 +0100 Subject: [PATCH 23/24] plugins/sip 0.0.6 --- plugins/sip/package-lock.json | 4590 +++++++++++++-------------- plugins/sip/package.json | 98 +- plugins/sip/src/sip-call-session.ts | 2 +- plugins/sip/src/sip-manager.ts | 1 - 4 files changed, 2342 insertions(+), 2349 deletions(-) diff --git a/plugins/sip/package-lock.json b/plugins/sip/package-lock.json index aa389a61cc..6a3e2fc834 100644 --- a/plugins/sip/package-lock.json +++ b/plugins/sip/package-lock.json @@ -1,2298 +1,2292 @@ -{ - "name": "@scrypted/sip", - "version": "0.0.6-beta.4", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "@scrypted/sip", - "version": "0.0.6-beta.4", - "dependencies": { - "@homebridge/camera-utils": "^2.0.4", - "@slyoldfox/sip": "^0.0.6-1", - "sdp": "^3.0.3", - "stun": "^2.1.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4", - "cross-env": "^7.0.3" - } - }, - "../../common": { - "name": "@scrypted/common", - "version": "1.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "@scrypted/sdk": "file:../sdk", - "@scrypted/server": "file:../server", - "http-auth-utils": "^3.0.2", - "node-fetch-commonjs": "^3.1.1", - "typescript": "^4.4.3" - }, - "devDependencies": { - "@types/node": "^16.9.0" - } - }, - "../../sdk": { - "name": "@scrypted/sdk", - "version": "0.2.82", - "dev": true, - "license": "ISC", - "dependencies": { - "@babel/preset-typescript": "^7.18.6", - "adm-zip": "^0.4.13", - "axios": "^0.21.4", - "babel-loader": "^9.1.0", - "babel-plugin-const-enum": "^1.1.0", - "esbuild": "^0.15.9", - "ncp": "^2.0.0", - "raw-loader": "^4.0.2", - "rimraf": "^3.0.2", - "tmp": "^0.2.1", - "ts-loader": "^9.4.2", - "typescript": "^4.9.4", - "webpack": "^5.75.0", - "webpack-bundle-analyzer": "^4.5.0" - }, - "bin": { - "scrypted-changelog": "bin/scrypted-changelog.js", - "scrypted-debug": "bin/scrypted-debug.js", - "scrypted-deploy": "bin/scrypted-deploy.js", - "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", - "scrypted-package-json": "bin/scrypted-package-json.js", - "scrypted-setup-project": "bin/scrypted-setup-project.js", - "scrypted-webpack": "bin/scrypted-webpack.js" - }, - "devDependencies": { - "@types/node": "^18.11.18", - "@types/stringify-object": "^4.0.0", - "stringify-object": "^3.3.0", - "ts-node": "^10.4.0", - "typedoc": "^0.23.21" - } - }, - "node_modules/@homebridge/camera-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", - "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", - "dependencies": { - "execa": "^5.1.1", - "ffmpeg-for-homebridge": "^0.1.4", - "pick-port": "^1.0.1", - "rxjs": "^7.5.6", - "systeminformation": "^5.12.1" - } - }, - "node_modules/@scrypted/common": { - "resolved": "../../common", - "link": true - }, - "node_modules/@scrypted/sdk": { - "resolved": "../../sdk", - "link": true - }, - "node_modules/@slyoldfox/sip": { - "version": "0.0.6-1", - "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", - "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", - "engines": { - "node": ">=0.2.2" - } - }, - "node_modules/@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "dev": true - }, - "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/binary-data": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", - "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", - "dependencies": { - "generate-function": "^2.3.1", - "is-plain-object": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "dependencies": { - "safe-buffer": "^5.1.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", - "dependencies": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/ffmpeg-for-homebridge": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", - "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", - "hasInstallScript": true, - "dependencies": { - "detect-libc": "^2.0.1", - "dotenv": "^16.0.1", - "simple-get": "^4.0.1", - "tar": "^6.1.11" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, - "node_modules/ip2buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", - "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "dependencies": { - "protocols": "^2.0.1" - } - }, - "node_modules/is-ssh/node_modules/protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stun": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", - "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", - "dependencies": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "dependencies": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/minipass": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", - "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "node_modules/parse-url": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", - "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", - "dependencies": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pick-port": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", - "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", - "dependencies": { - "debug": "^4.3.1" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", - "dependencies": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/sdp": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.0.3.tgz", - "integrity": "sha512-8EkfckS+XZQaPLyChu4ey7PghrdcraCVNpJe2Gfdi2ON1ylQ7OasuKX+b37R9slnRChwIAiQgt+oj8xXGD8x+A==" - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/stun": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", - "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", - "dependencies": { - "binary-data": "^0.6.0", - "buffer-xor": "^2.0.2", - "debug": "^4.1.1", - "ip": "^1.1.5", - "ip2buf": "^2.0.0", - "is-stun": "^2.0.0", - "meow": "^5.0.0", - "parse-url": "^5.0.1", - "turbo-crc32": "^1.0.0", - "universalify": "^0.1.2" - }, - "bin": { - "stun": "src/cli.js" - }, - "engines": { - "node": ">=8.3" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/systeminformation": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", - "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==", - "os": [ - "darwin", - "linux", - "win32", - "freebsd", - "openbsd", - "netbsd", - "sunos", - "android" - ], - "bin": { - "systeminformation": "lib/cli.js" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "Buy me a coffee", - "url": "https://www.buymeacoffee.com/systeminfo" - } - }, - "node_modules/tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "node_modules/turbo-crc32": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", - "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dependencies": { - "camelcase": "^4.1.0" - } - } - }, - "dependencies": { - "@homebridge/camera-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", - "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", - "requires": { - "execa": "^5.1.1", - "ffmpeg-for-homebridge": "^0.1.4", - "pick-port": "^1.0.1", - "rxjs": "^7.5.6", - "systeminformation": "^5.12.1" - } - }, - "@scrypted/common": { - "version": "file:../../common", - "requires": { - "@scrypted/sdk": "file:../sdk", - "@scrypted/server": "file:../server", - "@types/node": "^16.9.0", - "http-auth-utils": "^3.0.2", - "node-fetch-commonjs": "^3.1.1", - "typescript": "^4.4.3" - } - }, - "@scrypted/sdk": { - "version": "file:../../sdk", - "requires": { - "@babel/preset-typescript": "^7.18.6", - "@types/node": "^18.11.18", - "@types/stringify-object": "^4.0.0", - "adm-zip": "^0.4.13", - "axios": "^0.21.4", - "babel-loader": "^9.1.0", - "babel-plugin-const-enum": "^1.1.0", - "esbuild": "^0.15.9", - "ncp": "^2.0.0", - "raw-loader": "^4.0.2", - "rimraf": "^3.0.2", - "stringify-object": "^3.3.0", - "tmp": "^0.2.1", - "ts-loader": "^9.4.2", - "ts-node": "^10.4.0", - "typedoc": "^0.23.21", - "typescript": "^4.9.4", - "webpack": "^5.75.0", - "webpack-bundle-analyzer": "^4.5.0" - } - }, - "@slyoldfox/sip": { - "version": "0.0.6-1", - "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", - "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" - }, - "@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "dev": true - }, - "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" - }, - "binary-data": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", - "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", - "requires": { - "generate-function": "^2.3.1", - "is-plain-object": "^2.0.3" - } - }, - "buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "requires": { - "array-find-index": "^1.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" - }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" - } - } - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - } - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "ffmpeg-for-homebridge": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", - "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", - "requires": { - "detect-libc": "^2.0.1", - "dotenv": "^16.0.1", - "simple-get": "^4.0.1", - "tar": "^6.1.11" - } - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "requires": { - "locate-path": "^2.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "requires": { - "is-property": "^1.0.2" - } - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" - }, - "ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, - "ip2buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", - "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "requires": { - "has": "^1.0.3" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "requires": { - "protocols": "^2.0.1" - }, - "dependencies": { - "protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" - } - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-stun": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", - "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==" - }, - "meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - } - }, - "minipass": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", - "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "parse-url": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", - "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", - "requires": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - } - }, - "pick-port": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", - "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", - "requires": { - "debug": "^4.3.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" - }, - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "sdp": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.0.3.tgz", - "integrity": "sha512-8EkfckS+XZQaPLyChu4ey7PghrdcraCVNpJe2Gfdi2ON1ylQ7OasuKX+b37R9slnRChwIAiQgt+oj8xXGD8x+A==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==" - }, - "stun": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", - "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", - "requires": { - "binary-data": "^0.6.0", - "buffer-xor": "^2.0.2", - "debug": "^4.1.1", - "ip": "^1.1.5", - "ip2buf": "^2.0.0", - "is-stun": "^2.0.0", - "meow": "^5.0.0", - "parse-url": "^5.0.1", - "turbo-crc32": "^1.0.0", - "universalify": "^0.1.2" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "systeminformation": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.3.tgz", - "integrity": "sha512-IAmnUJdeFUWqY+YneAWJ9rceTdRRIaTiwspvd1B6SG7yhqpxLrSosHgGZKiE8lcaBlBYpLQpY3BRLtus4n8PNQ==" - }, - "tar": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", - "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^4.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==" - }, - "tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" - }, - "turbo-crc32": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", - "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "requires": { - "camelcase": "^4.1.0" - } - } - } -} +{ + "name": "@scrypted/sip", + "version": "0.0.6", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@scrypted/sip", + "version": "0.0.6", + "dependencies": { + "@homebridge/camera-utils": "^2.0.4", + "@slyoldfox/sip": "^0.0.6-1", + "sdp": "^3.0.3", + "stun": "^2.1.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@scrypted/common": "file:../../common", + "@scrypted/sdk": "file:../../sdk", + "@types/node": "^16.9.6", + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3" + } + }, + "../../common": { + "name": "@scrypted/common", + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@scrypted/sdk": "file:../sdk", + "@scrypted/server": "file:../server", + "http-auth-utils": "^3.0.2", + "node-fetch-commonjs": "^3.1.1", + "typescript": "^4.4.3" + }, + "devDependencies": { + "@types/node": "^16.9.0" + } + }, + "../../sdk": { + "name": "@scrypted/sdk", + "version": "0.2.85", + "dev": true, + "license": "ISC", + "dependencies": { + "@babel/preset-typescript": "^7.18.6", + "adm-zip": "^0.4.13", + "axios": "^0.21.4", + "babel-loader": "^9.1.0", + "babel-plugin-const-enum": "^1.1.0", + "esbuild": "^0.15.9", + "ncp": "^2.0.0", + "raw-loader": "^4.0.2", + "rimraf": "^3.0.2", + "tmp": "^0.2.1", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", + "webpack-bundle-analyzer": "^4.5.0" + }, + "bin": { + "scrypted-changelog": "bin/scrypted-changelog.js", + "scrypted-debug": "bin/scrypted-debug.js", + "scrypted-deploy": "bin/scrypted-deploy.js", + "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", + "scrypted-package-json": "bin/scrypted-package-json.js", + "scrypted-setup-project": "bin/scrypted-setup-project.js", + "scrypted-webpack": "bin/scrypted-webpack.js" + }, + "devDependencies": { + "@types/node": "^18.11.18", + "@types/stringify-object": "^4.0.0", + "stringify-object": "^3.3.0", + "ts-node": "^10.4.0", + "typedoc": "^0.23.21" + } + }, + "node_modules/@homebridge/camera-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", + "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", + "dependencies": { + "execa": "^5.1.1", + "ffmpeg-for-homebridge": "^0.1.4", + "pick-port": "^1.0.1", + "rxjs": "^7.5.6", + "systeminformation": "^5.12.1" + } + }, + "node_modules/@scrypted/common": { + "resolved": "../../common", + "link": true + }, + "node_modules/@scrypted/sdk": { + "resolved": "../../sdk", + "link": true + }, + "node_modules/@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", + "engines": { + "node": ">=0.2.2" + } + }, + "node_modules/@types/node": { + "version": "16.18.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", + "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binary-data": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", + "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", + "dependencies": { + "generate-function": "^2.3.1", + "is-plain-object": "^2.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/buffer-xor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", + "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", + "dependencies": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/ffmpeg-for-homebridge": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", + "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^2.0.1", + "dotenv": "^16.0.1", + "simple-get": "^4.0.1", + "tar": "^6.1.11" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, + "node_modules/ip2buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", + "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-ssh/node_modules/protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stun": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", + "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dependencies": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-path": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/parse-url": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", + "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", + "dependencies": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.4", + "protocols": "^1.4.0" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pick-port": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", + "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", + "dependencies": { + "debug": "^4.3.1" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" + }, + "node_modules/qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", + "dependencies": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/stun": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", + "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", + "dependencies": { + "binary-data": "^0.6.0", + "buffer-xor": "^2.0.2", + "debug": "^4.1.1", + "ip": "^1.1.5", + "ip2buf": "^2.0.0", + "is-stun": "^2.0.0", + "meow": "^5.0.0", + "parse-url": "^5.0.1", + "turbo-crc32": "^1.0.0", + "universalify": "^0.1.2" + }, + "bin": { + "stun": "src/cli.js" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/systeminformation": { + "version": "5.17.12", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.12.tgz", + "integrity": "sha512-I3pfMW2vue53u+X08BNxaJieaHkRoMMKjWetY9lbYJeWFaeWPO6P4FkNc4XOCX8F9vbQ0HqQ25RJoz3U/B7liw==", + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/turbo-crc32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", + "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dependencies": { + "camelcase": "^4.1.0" + } + } + }, + "dependencies": { + "@homebridge/camera-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@homebridge/camera-utils/-/camera-utils-2.2.0.tgz", + "integrity": "sha512-G3C4qS1FpJysQG07WSOHyClgBBvASNNh/ZBtGo7A5Y80sCAVElEHBmRumUs5B9QPcUuSHyXYfnlu2NdxCHQlSA==", + "requires": { + "execa": "^5.1.1", + "ffmpeg-for-homebridge": "^0.1.4", + "pick-port": "^1.0.1", + "rxjs": "^7.5.6", + "systeminformation": "^5.12.1" + } + }, + "@scrypted/common": { + "version": "file:../../common", + "requires": { + "@scrypted/sdk": "file:../sdk", + "@scrypted/server": "file:../server", + "@types/node": "^16.9.0", + "http-auth-utils": "^3.0.2", + "node-fetch-commonjs": "^3.1.1", + "typescript": "^4.4.3" + } + }, + "@scrypted/sdk": { + "version": "file:../../sdk", + "requires": { + "@babel/preset-typescript": "^7.18.6", + "@types/node": "^18.11.18", + "@types/stringify-object": "^4.0.0", + "adm-zip": "^0.4.13", + "axios": "^0.21.4", + "babel-loader": "^9.1.0", + "babel-plugin-const-enum": "^1.1.0", + "esbuild": "^0.15.9", + "ncp": "^2.0.0", + "raw-loader": "^4.0.2", + "rimraf": "^3.0.2", + "stringify-object": "^3.3.0", + "tmp": "^0.2.1", + "ts-loader": "^9.4.2", + "ts-node": "^10.4.0", + "typedoc": "^0.23.21", + "typescript": "^4.9.4", + "webpack": "^5.75.0", + "webpack-bundle-analyzer": "^4.5.0" + } + }, + "@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" + }, + "@types/node": { + "version": "16.18.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", + "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" + }, + "binary-data": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", + "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", + "requires": { + "generate-function": "^2.3.1", + "is-plain-object": "^2.0.3" + } + }, + "buffer-xor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", + "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" + } + } + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + } + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "ffmpeg-for-homebridge": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ffmpeg-for-homebridge/-/ffmpeg-for-homebridge-0.1.4.tgz", + "integrity": "sha512-2bM5ZMtbhSehq3+RPPDoJ3P4Mld/DeNPccn14tTxwCL9MH1HmncHxvej8cZvSgwuWu1hCT0DLdC0Z5Nv/Q1lfA==", + "requires": { + "detect-libc": "^2.0.1", + "dotenv": "^16.0.1", + "simple-get": "^4.0.1", + "tar": "^6.1.11" + } + }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "requires": { + "locate-path": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" + }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, + "ip2buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", + "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "requires": { + "protocols": "^2.0.1" + }, + "dependencies": { + "protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + } + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-stun": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", + "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==" + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-path": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "parse-url": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", + "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.4", + "protocols": "^1.4.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pick-port": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pick-port/-/pick-port-1.0.1.tgz", + "integrity": "sha512-JzjRIkfG/4pG3tYLl1LwdmFtnlW+Rsxe200DevHZzZLYDUgfnx8LuOFnLwy5Dt59JY1HIN3JXyMXRbUvERkh/g==", + "requires": { + "debug": "^4.3.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + }, + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" + }, + "qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==" + }, + "stun": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", + "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", + "requires": { + "binary-data": "^0.6.0", + "buffer-xor": "^2.0.2", + "debug": "^4.1.1", + "ip": "^1.1.5", + "ip2buf": "^2.0.0", + "is-stun": "^2.0.0", + "meow": "^5.0.0", + "parse-url": "^5.0.1", + "turbo-crc32": "^1.0.0", + "universalify": "^0.1.2" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "systeminformation": { + "version": "5.17.12", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.17.12.tgz", + "integrity": "sha512-I3pfMW2vue53u+X08BNxaJieaHkRoMMKjWetY9lbYJeWFaeWPO6P4FkNc4XOCX8F9vbQ0HqQ25RJoz3U/B7liw==" + }, + "tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==" + }, + "tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "turbo-crc32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", + "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/plugins/sip/package.json b/plugins/sip/package.json index efe80bb799..429c8f9cef 100644 --- a/plugins/sip/package.json +++ b/plugins/sip/package.json @@ -1,49 +1,49 @@ -{ - "name": "@scrypted/sip", - "version": "0.0.6-beta.4", - "scripts": { - "scrypted-setup-project": "scrypted-setup-project", - "prescrypted-setup-project": "scrypted-package-json", - "build": "scrypted-webpack", - "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", - "prescrypted-vscode-launch": "scrypted-webpack", - "scrypted-vscode-launch": "scrypted-deploy-debug", - "scrypted-deploy-debug": "scrypted-deploy-debug", - "scrypted-debug": "scrypted-debug", - "scrypted-deploy": "scrypted-deploy", - "scrypted-readme": "scrypted-readme", - "scrypted-package-json": "scrypted-package-json" - }, - "keywords": [ - "scrypted", - "plugin", - "sip" - ], - "scrypted": { - "name": "SIP Plugin", - "type": "DeviceProvider", - "interfaces": [ - "DeviceProvider", - "DeviceCreator" - ], - "pluginDependencies": [ - "@scrypted/prebuffer-mixin", - "@scrypted/pam-diff", - "@scrypted/snapshot" - ] - }, - "dependencies": { - "@homebridge/camera-utils": "^2.0.4", - "@slyoldfox/sip": "^0.0.6-1", - "sdp": "^3.0.3", - "stun": "^2.1.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4", - "cross-env": "^7.0.3" - } -} +{ + "name": "@scrypted/sip", + "version": "0.0.6", + "scripts": { + "scrypted-setup-project": "scrypted-setup-project", + "prescrypted-setup-project": "scrypted-package-json", + "build": "scrypted-webpack", + "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", + "prescrypted-vscode-launch": "scrypted-webpack", + "scrypted-vscode-launch": "scrypted-deploy-debug", + "scrypted-deploy-debug": "scrypted-deploy-debug", + "scrypted-debug": "scrypted-debug", + "scrypted-deploy": "scrypted-deploy", + "scrypted-readme": "scrypted-readme", + "scrypted-package-json": "scrypted-package-json" + }, + "keywords": [ + "scrypted", + "plugin", + "sip" + ], + "scrypted": { + "name": "SIP Plugin", + "type": "DeviceProvider", + "interfaces": [ + "DeviceProvider", + "DeviceCreator" + ], + "pluginDependencies": [ + "@scrypted/prebuffer-mixin", + "@scrypted/pam-diff", + "@scrypted/snapshot" + ] + }, + "dependencies": { + "@homebridge/camera-utils": "^2.0.4", + "@slyoldfox/sip": "^0.0.6-1", + "sdp": "^3.0.3", + "stun": "^2.1.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@scrypted/common": "file:../../common", + "@scrypted/sdk": "file:../../sdk", + "@types/node": "^16.9.6", + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3" + } +} diff --git a/plugins/sip/src/sip-call-session.ts b/plugins/sip/src/sip-call-session.ts index bd132be9cc..d256724051 100644 --- a/plugins/sip/src/sip-call-session.ts +++ b/plugins/sip/src/sip-call-session.ts @@ -38,7 +38,7 @@ export class SipCallSession extends Subscribed { sipManager.setSipOptions( sipOptions ) } - static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager: SipManager ) { + static async createCallSession(console: Console, cameraName: string, sipOptions: SipOptions, sipManager?: SipManager ) { const audioSplitter = await createBindZero(), audioRtcpSplitter = await createBindUdp(audioSplitter.port + 1), videoSplitter = await createBindZero(), diff --git a/plugins/sip/src/sip-manager.ts b/plugins/sip/src/sip-manager.ts index 341843fd7d..b7e846072d 100644 --- a/plugins/sip/src/sip-manager.ts +++ b/plugins/sip/src/sip-manager.ts @@ -19,7 +19,6 @@ export interface SipOptions { useTcp?: boolean gruuInstanceId?: string sipRequestHandler?: SipRequestHandler - deviceIp: string } /** From 45a463aa8020a6e90504ddf0d170bb3a1e3535d3 Mon Sep 17 00:00:00 2001 From: slyoldfox Date: Mon, 20 Mar 2023 14:07:15 +0100 Subject: [PATCH 24/24] plugins/bticino 0.0.7 --- plugins/bticino/package-lock.json | 4052 ++++++++++++------------- plugins/bticino/package.json | 98 +- plugins/bticino/src/bticino-camera.ts | 818 +++-- plugins/bticino/src/main.ts | 192 +- plugins/bticino/src/sip-helper.ts | 99 +- 5 files changed, 2635 insertions(+), 2624 deletions(-) diff --git a/plugins/bticino/package-lock.json b/plugins/bticino/package-lock.json index 06a9660e27..5d2adb88a2 100644 --- a/plugins/bticino/package-lock.json +++ b/plugins/bticino/package-lock.json @@ -1,2026 +1,2026 @@ -{ - "name": "@scrypted/bticino", - "version": "0.0.7-beta.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "@scrypted/bticino", - "version": "0.0.7-beta.0", - "dependencies": { - "@slyoldfox/sip": "^0.0.6-1", - "sdp": "^3.0.3", - "stun": "^2.1.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4", - "cross-env": "^7.0.3", - "ts-node": "^10.9.1" - } - }, - "../../common": { - "name": "@scrypted/common", - "version": "1.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "@scrypted/sdk": "file:../sdk", - "@scrypted/server": "file:../server", - "http-auth-utils": "^3.0.2", - "node-fetch-commonjs": "^3.1.1", - "typescript": "^4.4.3" - }, - "devDependencies": { - "@types/node": "^16.9.0" - } - }, - "../../sdk": { - "name": "@scrypted/sdk", - "version": "0.2.82", - "dev": true, - "license": "ISC", - "dependencies": { - "@babel/preset-typescript": "^7.18.6", - "adm-zip": "^0.4.13", - "axios": "^0.21.4", - "babel-loader": "^9.1.0", - "babel-plugin-const-enum": "^1.1.0", - "esbuild": "^0.15.9", - "ncp": "^2.0.0", - "raw-loader": "^4.0.2", - "rimraf": "^3.0.2", - "tmp": "^0.2.1", - "ts-loader": "^9.4.2", - "typescript": "^4.9.4", - "webpack": "^5.75.0", - "webpack-bundle-analyzer": "^4.5.0" - }, - "bin": { - "scrypted-changelog": "bin/scrypted-changelog.js", - "scrypted-debug": "bin/scrypted-debug.js", - "scrypted-deploy": "bin/scrypted-deploy.js", - "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", - "scrypted-package-json": "bin/scrypted-package-json.js", - "scrypted-setup-project": "bin/scrypted-setup-project.js", - "scrypted-webpack": "bin/scrypted-webpack.js" - }, - "devDependencies": { - "@types/node": "^18.11.18", - "@types/stringify-object": "^4.0.0", - "stringify-object": "^3.3.0", - "ts-node": "^10.4.0", - "typedoc": "^0.23.21" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@scrypted/common": { - "resolved": "../../common", - "link": true - }, - "node_modules/@scrypted/sdk": { - "resolved": "../../sdk", - "link": true - }, - "node_modules/@slyoldfox/sip": { - "version": "0.0.6-1", - "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", - "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", - "engines": { - "node": ">=0.2.2" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "dev": true - }, - "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/binary-data": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", - "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", - "dependencies": { - "generate-function": "^2.3.1", - "is-plain-object": "^2.0.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "dependencies": { - "safe-buffer": "^5.1.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", - "dependencies": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, - "node_modules/ip2buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", - "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "dependencies": { - "protocols": "^2.0.1" - } - }, - "node_modules/is-ssh/node_modules/protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" - }, - "node_modules/is-stun": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", - "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", - "dependencies": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "dependencies": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "node_modules/parse-url": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", - "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", - "dependencies": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", - "dependencies": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/sdp": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.0.3.tgz", - "integrity": "sha512-8EkfckS+XZQaPLyChu4ey7PghrdcraCVNpJe2Gfdi2ON1ylQ7OasuKX+b37R9slnRChwIAiQgt+oj8xXGD8x+A==" - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/stun": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", - "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", - "dependencies": { - "binary-data": "^0.6.0", - "buffer-xor": "^2.0.2", - "debug": "^4.1.1", - "ip": "^1.1.5", - "ip2buf": "^2.0.0", - "is-stun": "^2.0.0", - "meow": "^5.0.0", - "parse-url": "^5.0.1", - "turbo-crc32": "^1.0.0", - "universalify": "^0.1.2" - }, - "bin": { - "stun": "src/cli.js" - }, - "engines": { - "node": ">=8.3" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/turbo-crc32": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", - "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dependencies": { - "camelcase": "^4.1.0" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@scrypted/common": { - "version": "file:../../common", - "requires": { - "@scrypted/sdk": "file:../sdk", - "@scrypted/server": "file:../server", - "@types/node": "^16.9.0", - "http-auth-utils": "^3.0.2", - "node-fetch-commonjs": "^3.1.1", - "typescript": "^4.4.3" - } - }, - "@scrypted/sdk": { - "version": "file:../../sdk", - "requires": { - "@babel/preset-typescript": "^7.18.6", - "@types/node": "^18.11.18", - "@types/stringify-object": "^4.0.0", - "adm-zip": "^0.4.13", - "axios": "^0.21.4", - "babel-loader": "^9.1.0", - "babel-plugin-const-enum": "^1.1.0", - "esbuild": "^0.15.9", - "ncp": "^2.0.0", - "raw-loader": "^4.0.2", - "rimraf": "^3.0.2", - "stringify-object": "^3.3.0", - "tmp": "^0.2.1", - "ts-loader": "^9.4.2", - "ts-node": "^10.4.0", - "typedoc": "^0.23.21", - "typescript": "^4.9.4", - "webpack": "^5.75.0", - "webpack-bundle-analyzer": "^4.5.0" - } - }, - "@slyoldfox/sip": { - "version": "0.0.6-1", - "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", - "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/node": { - "version": "16.18.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.11.tgz", - "integrity": "sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==", - "dev": true - }, - "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" - }, - "binary-data": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", - "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", - "requires": { - "generate-function": "^2.3.1", - "is-plain-object": "^2.0.3" - } - }, - "buffer-xor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", - "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" - }, - "camelcase-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", - "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", - "requires": { - "camelcase": "^4.1.0", - "map-obj": "^2.0.0", - "quick-lru": "^1.0.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "requires": { - "array-find-index": "^1.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" - }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" - } - } - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "requires": { - "locate-path": "^2.0.0" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "requires": { - "is-property": "^1.0.2" - } - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" - }, - "ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, - "ip2buf": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", - "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "requires": { - "has": "^1.0.3" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "requires": { - "protocols": "^2.0.1" - }, - "dependencies": { - "protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" - } - } - }, - "is-stun": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", - "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", - "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==" - }, - "meow": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", - "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", - "requires": { - "camelcase-keys": "^4.0.0", - "decamelize-keys": "^1.0.0", - "loud-rejection": "^1.0.0", - "minimist-options": "^3.0.1", - "normalize-package-data": "^2.3.4", - "read-pkg-up": "^3.0.0", - "redent": "^2.0.0", - "trim-newlines": "^2.0.0", - "yargs-parser": "^10.0.0" - } - }, - "minimist-options": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", - "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parse-path": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", - "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "parse-url": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", - "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", - "requires": { - "is-ssh": "^1.3.0", - "normalize-url": "^6.1.0", - "parse-path": "^4.0.4", - "protocols": "^1.4.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" - }, - "protocols": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", - "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", - "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "quick-lru": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", - "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "redent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", - "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", - "requires": { - "indent-string": "^3.0.0", - "strip-indent": "^2.0.0" - } - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "sdp": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.0.3.tgz", - "integrity": "sha512-8EkfckS+XZQaPLyChu4ey7PghrdcraCVNpJe2Gfdi2ON1ylQ7OasuKX+b37R9slnRChwIAiQgt+oj8xXGD8x+A==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" - }, - "split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "strip-indent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", - "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==" - }, - "stun": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", - "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", - "requires": { - "binary-data": "^0.6.0", - "buffer-xor": "^2.0.2", - "debug": "^4.1.1", - "ip": "^1.1.5", - "ip2buf": "^2.0.0", - "is-stun": "^2.0.0", - "meow": "^5.0.0", - "parse-url": "^5.0.1", - "turbo-crc32": "^1.0.0", - "universalify": "^0.1.2" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "trim-newlines": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", - "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==" - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "turbo-crc32": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", - "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" - }, - "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "dev": true, - "peer": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "requires": { - "camelcase": "^4.1.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} +{ + "name": "@scrypted/bticino", + "version": "0.0.7", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@scrypted/bticino", + "version": "0.0.7", + "dependencies": { + "@slyoldfox/sip": "^0.0.6-1", + "sdp": "^3.0.3", + "stun": "^2.1.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@scrypted/common": "file:../../common", + "@scrypted/sdk": "file:../../sdk", + "@types/node": "^16.9.6", + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3", + "ts-node": "^10.9.1" + } + }, + "../../common": { + "name": "@scrypted/common", + "version": "1.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "@scrypted/sdk": "file:../sdk", + "@scrypted/server": "file:../server", + "http-auth-utils": "^3.0.2", + "node-fetch-commonjs": "^3.1.1", + "typescript": "^4.4.3" + }, + "devDependencies": { + "@types/node": "^16.9.0" + } + }, + "../../sdk": { + "name": "@scrypted/sdk", + "version": "0.2.85", + "dev": true, + "license": "ISC", + "dependencies": { + "@babel/preset-typescript": "^7.18.6", + "adm-zip": "^0.4.13", + "axios": "^0.21.4", + "babel-loader": "^9.1.0", + "babel-plugin-const-enum": "^1.1.0", + "esbuild": "^0.15.9", + "ncp": "^2.0.0", + "raw-loader": "^4.0.2", + "rimraf": "^3.0.2", + "tmp": "^0.2.1", + "ts-loader": "^9.4.2", + "typescript": "^4.9.4", + "webpack": "^5.75.0", + "webpack-bundle-analyzer": "^4.5.0" + }, + "bin": { + "scrypted-changelog": "bin/scrypted-changelog.js", + "scrypted-debug": "bin/scrypted-debug.js", + "scrypted-deploy": "bin/scrypted-deploy.js", + "scrypted-deploy-debug": "bin/scrypted-deploy-debug.js", + "scrypted-package-json": "bin/scrypted-package-json.js", + "scrypted-setup-project": "bin/scrypted-setup-project.js", + "scrypted-webpack": "bin/scrypted-webpack.js" + }, + "devDependencies": { + "@types/node": "^18.11.18", + "@types/stringify-object": "^4.0.0", + "stringify-object": "^3.3.0", + "ts-node": "^10.4.0", + "typedoc": "^0.23.21" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@scrypted/common": { + "resolved": "../../common", + "link": true + }, + "node_modules/@scrypted/sdk": { + "resolved": "../../sdk", + "link": true + }, + "node_modules/@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==", + "engines": { + "node": ">=0.2.2" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.18.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", + "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binary-data": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", + "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", + "dependencies": { + "generate-function": "^2.3.1", + "is-plain-object": "^2.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/buffer-xor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", + "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", + "dependencies": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, + "node_modules/ip2buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", + "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-ssh/node_modules/protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + }, + "node_modules/is-stun": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", + "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dependencies": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-path": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/parse-url": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", + "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", + "dependencies": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.4", + "protocols": "^1.4.0" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" + }, + "node_modules/qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", + "dependencies": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/stun": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", + "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", + "dependencies": { + "binary-data": "^0.6.0", + "buffer-xor": "^2.0.2", + "debug": "^4.1.1", + "ip": "^1.1.5", + "ip2buf": "^2.0.0", + "is-stun": "^2.0.0", + "meow": "^5.0.0", + "parse-url": "^5.0.1", + "turbo-crc32": "^1.0.0", + "universalify": "^0.1.2" + }, + "bin": { + "stun": "src/cli.js" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/turbo-crc32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", + "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dependencies": { + "camelcase": "^4.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@scrypted/common": { + "version": "file:../../common", + "requires": { + "@scrypted/sdk": "file:../sdk", + "@scrypted/server": "file:../server", + "@types/node": "^16.9.0", + "http-auth-utils": "^3.0.2", + "node-fetch-commonjs": "^3.1.1", + "typescript": "^4.4.3" + } + }, + "@scrypted/sdk": { + "version": "file:../../sdk", + "requires": { + "@babel/preset-typescript": "^7.18.6", + "@types/node": "^18.11.18", + "@types/stringify-object": "^4.0.0", + "adm-zip": "^0.4.13", + "axios": "^0.21.4", + "babel-loader": "^9.1.0", + "babel-plugin-const-enum": "^1.1.0", + "esbuild": "^0.15.9", + "ncp": "^2.0.0", + "raw-loader": "^4.0.2", + "rimraf": "^3.0.2", + "stringify-object": "^3.3.0", + "tmp": "^0.2.1", + "ts-loader": "^9.4.2", + "ts-node": "^10.4.0", + "typedoc": "^0.23.21", + "typescript": "^4.9.4", + "webpack": "^5.75.0", + "webpack-bundle-analyzer": "^4.5.0" + } + }, + "@slyoldfox/sip": { + "version": "0.0.6-1", + "resolved": "https://registry.npmjs.org/@slyoldfox/sip/-/sip-0.0.6-1.tgz", + "integrity": "sha512-PJBIAKS3aMsFTHeQLfAtVpZOduAqGNZZAEH6Kb15htGUcSJWHZ9r2LAjxm3fD4yWT9plYlO0CthcEVnlrrwQLA==" + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/node": { + "version": "16.18.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", + "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" + }, + "binary-data": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/binary-data/-/binary-data-0.6.0.tgz", + "integrity": "sha512-HGiT0ir03tS1u7iWdW5xjJfbPpvxH2qJbPFxXW0I3P5iOzkbjN/cJy5GlpAwmjHW5CiayGOxZ/ytLzXmYgdgqQ==", + "requires": { + "generate-function": "^2.3.1", + "is-plain-object": "^2.0.3" + } + }, + "buffer-xor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-2.0.2.tgz", + "integrity": "sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" + } + } + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "requires": { + "locate-path": "^2.0.0" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "requires": { + "is-property": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==" + }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + }, + "ip2buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip2buf/-/ip2buf-2.0.0.tgz", + "integrity": "sha512-ezW62UW6IPwpuS3mpsvOS3/3Jgx7aaNZT+uJo/+xVBxHCq7EA1ryuhzZw2MyC5GuGd1sAp3RDx7e4+nJCGt9vA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, + "is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "requires": { + "protocols": "^2.0.1" + }, + "dependencies": { + "protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + } + } + }, + "is-stun": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stun/-/is-stun-2.0.0.tgz", + "integrity": "sha512-3d3CI8nLmh2ATbjfvi5TkVcimMgXtFH7PGoXeT1prGguVK8eaO3CzynLbdFY8Ez9khVpLpP8HHFPxneqn0QYPw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==" + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + } + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-path": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.4.tgz", + "integrity": "sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==", + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "parse-url": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.8.tgz", + "integrity": "sha512-KFg5QvyiOKJGQSwUT7c5A4ELs0TJ33gmx/NBjK0FvZUD6aonFuXHUVa3SIa2XpbYVkYU8VlDrD3oCbX1ufy0zg==", + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^6.1.0", + "parse-path": "^4.0.4", + "protocols": "^1.4.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==" + }, + "protocols": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz", + "integrity": "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==" + }, + "qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz", + "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==", + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==" + }, + "stun": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stun/-/stun-2.1.0.tgz", + "integrity": "sha512-p+kY8/qzyZ9Sx+ZvlYd71hKcUDkbYhRDqjGKHrWvOdrc89vrpa0B6uHkWjYklWNQr3nQWKWHkYKDEgGBB2fiPg==", + "requires": { + "binary-data": "^0.6.0", + "buffer-xor": "^2.0.2", + "debug": "^4.1.1", + "ip": "^1.1.5", + "ip2buf": "^2.0.0", + "is-stun": "^2.0.0", + "meow": "^5.0.0", + "parse-url": "^5.0.1", + "turbo-crc32": "^1.0.0", + "universalify": "^0.1.2" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==" + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "turbo-crc32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/turbo-crc32/-/turbo-crc32-1.0.1.tgz", + "integrity": "sha512-8yyRd1ZdNp+AQLGqi3lTaA2k81JjlIZOyFQEsi7GQWBgirnQOxjqVtDEbYHM2Z4yFdJ5AQw0fxBLLnDCl6RXoQ==" + }, + "typescript": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "dev": true, + "peer": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/plugins/bticino/package.json b/plugins/bticino/package.json index 8f11a5fa35..c575d462e3 100644 --- a/plugins/bticino/package.json +++ b/plugins/bticino/package.json @@ -1,49 +1,49 @@ -{ - "name": "@scrypted/bticino", - "version": "0.0.7-beta.0", - "scripts": { - "scrypted-setup-project": "scrypted-setup-project", - "prescrypted-setup-project": "scrypted-package-json", - "build": "scrypted-webpack", - "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", - "prescrypted-vscode-launch": "scrypted-webpack", - "scrypted-vscode-launch": "scrypted-deploy-debug", - "scrypted-deploy-debug": "scrypted-deploy-debug", - "scrypted-debug": "scrypted-debug", - "scrypted-deploy": "scrypted-deploy", - "scrypted-readme": "scrypted-readme", - "scrypted-package-json": "scrypted-package-json" - }, - "keywords": [ - "scrypted", - "plugin", - "sip" - ], - "scrypted": { - "name": "BTicino SIP Plugin", - "type": "DeviceProvider", - "interfaces": [ - "DeviceProvider", - "DeviceCreator" - ], - "pluginDependencies": [ - "@scrypted/prebuffer-mixin", - "@scrypted/pam-diff", - "@scrypted/snapshot" - ] - }, - "dependencies": { - "@slyoldfox/sip": "^0.0.6-1", - "sdp": "^3.0.3", - "stun": "^2.1.0", - "uuid": "^8.3.2" - }, - "devDependencies": { - "@scrypted/common": "file:../../common", - "@scrypted/sdk": "file:../../sdk", - "@types/node": "^16.9.6", - "@types/uuid": "^8.3.4", - "cross-env": "^7.0.3", - "ts-node": "^10.9.1" - } -} +{ + "name": "@scrypted/bticino", + "version": "0.0.7", + "scripts": { + "scrypted-setup-project": "scrypted-setup-project", + "prescrypted-setup-project": "scrypted-package-json", + "build": "scrypted-webpack", + "prepublishOnly": "cross-env NODE_ENV=production scrypted-webpack", + "prescrypted-vscode-launch": "scrypted-webpack", + "scrypted-vscode-launch": "scrypted-deploy-debug", + "scrypted-deploy-debug": "scrypted-deploy-debug", + "scrypted-debug": "scrypted-debug", + "scrypted-deploy": "scrypted-deploy", + "scrypted-readme": "scrypted-readme", + "scrypted-package-json": "scrypted-package-json" + }, + "keywords": [ + "scrypted", + "plugin", + "sip" + ], + "scrypted": { + "name": "BTicino SIP Plugin", + "type": "DeviceProvider", + "interfaces": [ + "DeviceProvider", + "DeviceCreator" + ], + "pluginDependencies": [ + "@scrypted/prebuffer-mixin", + "@scrypted/pam-diff", + "@scrypted/snapshot" + ] + }, + "dependencies": { + "@slyoldfox/sip": "^0.0.6-1", + "sdp": "^3.0.3", + "stun": "^2.1.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@scrypted/common": "file:../../common", + "@scrypted/sdk": "file:../../sdk", + "@types/node": "^16.9.6", + "@types/uuid": "^8.3.4", + "cross-env": "^7.0.3", + "ts-node": "^10.9.1" + } +} diff --git a/plugins/bticino/src/bticino-camera.ts b/plugins/bticino/src/bticino-camera.ts index 5a06f20449..d20572f167 100644 --- a/plugins/bticino/src/bticino-camera.ts +++ b/plugins/bticino/src/bticino-camera.ts @@ -1,413 +1,407 @@ -import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; -import { sleep } from '@scrypted/common/src/sleep'; -import { RtspServer } from '@scrypted/common/src/rtsp-server'; -import { addTrackControls, parseSdp, replacePorts } from '@scrypted/common/src/sdp-utils'; -import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk'; -import { SipCallSession } from '../../sip/src/sip-call-session'; -import { isStunMessage, getPayloadType, getSequenceNumber, isRtpMessagePayloadType, RtpDescription } from '../../sip/src/rtp-utils'; -import { VoicemailHandler } from './bticino-voicemailHandler'; -import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; -import { decodeSrtpOptions, encodeSrtpOptions, SrtpOptions } from '../../ring/src/srtp-utils' -import { SipHelper } from './sip-helper'; -import child_process, { ChildProcess } from 'child_process'; -import dgram from 'dgram'; -import fs from 'fs' -import { BticinoStorageSettings } from './storage-settings'; -import { BticinoSipPlugin } from './main'; -import { BticinoSipLock } from './bticino-lock'; -import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; -import { PersistentSipManager } from './persistent-sip-manager'; -import { InviteHandler } from './bticino-inviteHandler'; -import { SipRequest } from '../../sip/src/sip-manager'; -import crypto from 'crypto'; -import { get } from 'http' - -const STREAM_TIMEOUT = 65000; -const { mediaManager } = sdk; - -export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips { - - private session: SipCallSession - private remoteRtpDescription: RtpDescription - private audioOutForwarder: dgram.Socket - private audioOutProcess: ChildProcess - private currentMedia: FFmpegInput | MediaStreamUrl - private currentMediaMimeType: string - private refreshTimeout: NodeJS.Timeout - public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler() - public incomingCallRequest : SipRequest - private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) - public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) - private inviteHandler : InviteHandler = new InviteHandler(this) - //TODO: randomize this - private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" - private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) - private persistentSipManager : PersistentSipManager - public doorbellWebhookUrl : string - public doorbellLockWebhookUrl : string - private md5 - - constructor(nativeId: string, public provider: BticinoSipPlugin) { - super(nativeId) - this.md5 = crypto.createHash('md5').update( nativeId ).digest("hex") - this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) - this.persistentSipManager = new PersistentSipManager( this ); - (async() => { - this.doorbellWebhookUrl = await this.doorbellWebhookEndpoint() - this.doorbellLockWebhookUrl = await this.doorbellLockWebhookEndpoint() - })(); - } - getVideoClips(options?: VideoClipOptions): Promise { - return new Promise( (resolve,reject ) => { - let c300x = SipHelper.sipOptions(this).deviceIp - get(`http://${c300x}:8080/videoclips?raw=true&startTime=${options.startTime/1000}&endTime=${options.endTime/1000}`, (res) => { - let rawData = ''; - res.on('data', (chunk) => { rawData += chunk; }); - res.on('end', () => { - try { - const parsedData : [] = JSON.parse(rawData); - let videoClips : VideoClip[] = [] - parsedData.forEach( (item) => { - let videoClip : VideoClip = { - id: item['file'], - startTime: parseInt(item['info']['UnixTime']) * 1000, - duration: item['info']['Duration'] * 1000, - //description: item['info']['Date'], - thumbnailId: item['file'] - - } - videoClips.push( videoClip ) - } ) - return resolve(videoClips) - } catch (e) { - reject(e.message) - console.error(e.message); - } - }) - }); - }); - } - - getVideoClip(videoId: string): Promise { - let c300x = SipHelper.sipOptions(this).deviceIp - const url = `http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`; - return mediaManager.createMediaObjectFromUrl(url); - } - getVideoClipThumbnail(thumbnailId: string): Promise { - let c300x = SipHelper.sipOptions(this).deviceIp - const url = `http://${c300x}:8080/voicemail?msg=${thumbnailId}/aswm.jpg&raw=true`; - return mediaManager.createMediaObjectFromUrl(url); - } - removeVideoClips(...videoClipIds: string[]): Promise { - //TODO - throw new Error('Method not implemented.') - } - - sipUnlock(): Promise { - this.log.i("unlocking C300X door ") - return this.persistentSipManager.enable().then( (sipCall) => { - sipCall.message( '*8*19*20##' ) - .then( () => - sleep(1000) - .then( () => sipCall.message( '*8*20*20##' ) ) - ) - } ) - } - - getAswmStatus() : Promise { - return this.persistentSipManager.enable().then( (sipCall) => { - sipCall.message( "GetAswmStatus!" ) - } ) - } - - async takePicture(option?: PictureOptions): Promise { - throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL."); - } - - async getPictureOptions(): Promise { - return - } - - getSettings(): Promise { - return this.settingsStorage.getSettings() - } - - putSetting(key: string, value: SettingValue): Promise { - return this.settingsStorage.putSetting(key, value) - } - - async startIntercom(media: MediaObject): Promise { - if (!this.session) - throw new Error("not in call"); - - this.stopIntercom(); - - const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString()); - - const audioOutForwarder = await createBindZero() - this.audioOutForwarder = audioOutForwarder.server - audioOutForwarder.server.on('message', message => { - if( this.session ) - this.session.audioSplitter.send(message, 40004, this.remoteRtpDescription.address) - return null - }); - - const args = ffmpegInput.inputArguments.slice(); - args.push( - '-vn', '-dn', '-sn', - '-acodec', 'speex', - '-flags', '+global_header', - '-ac', '1', - '-ar', '8k', - '-f', 'rtp', - //'-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', - //'-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions), - `rtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`, - ); - - this.console.log("===========================================") - safePrintFFmpegArguments( this.console, args ) - this.console.log("===========================================") - - const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args); - ffmpegLogInitialOutput(this.console, cp) - this.audioOutProcess = cp; - cp.on('exit', () => this.console.log('two way audio ended')); - this.session.onCallEnded.subscribe(() => { - closeQuiet(audioOutForwarder.server); - safeKillFFmpeg(cp) - }); - } - - async stopIntercom(): Promise { - closeQuiet(this.audioOutForwarder) - this.audioOutProcess?.kill('SIGKILL') - this.audioOutProcess = undefined - this.audioOutForwarder = undefined - } - - resetStreamTimeout() { - this.log.d('starting/refreshing stream') - clearTimeout(this.refreshTimeout) - this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT) - } - - hasActiveCall() { - return this.session; - } - - stopSession() { - if (this.session) { - this.log.d('ending sip session') - this.session.stop() - this.session = undefined - } - } - - async getVideoStream(options?: ResponseMediaStreamOptions): Promise { - if( !SipHelper.sipOptions( this ) ) { - // Bail out fast when no options are set and someone enables prebuffering - throw new Error('Please configure from/to/domain settings') - } - - if (options?.metadata?.refreshAt) { - if (!this.currentMedia?.mediaStreamOptions) - throw new Error("no stream to refresh"); - - const currentMedia = this.currentMedia - currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT; - currentMedia.mediaStreamOptions.metadata = { - refreshAt: currentMedia.mediaStreamOptions.refreshAt - }; - this.resetStreamTimeout() - return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType) - } - - this.stopSession(); - - - const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient() - - const playbackUrl = clientUrl - - playbackPromise.then(async (client) => { - client.setKeepAlive(true, 10000) - let sip: SipCallSession - try { - let rtsp: RtspServer; - const cleanup = () => { - client.destroy(); - if (this.session === sip) - this.session = undefined - try { - this.log.d('cleanup(): stopping sip session.') - sip.stop() - } - catch (e) { - } - rtsp?.destroy() - } - - client.on('close', cleanup) - client.on('error', cleanup) - - let sipOptions = SipHelper.sipOptions( this ) - - sip = await this.persistentSipManager.session( sipOptions ); - // Validate this sooner - if( !sip ) return Promise.reject("Cannot create session") - - sip.onCallEnded.subscribe(cleanup) - - // Call the C300X - this.remoteRtpDescription = await sip.callOrAcceptInvite( - ( audio ) => { - return [ - //TODO: Payload types are hardcoded - `m=audio 65000 RTP/SAVP 110`, - `a=rtpmap:110 speex/8000`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, - ] - }, ( video ) => { - if( false ) { - //TODO: implement later - return [ - `m=video 0 RTP/SAVP 0` - ] - } else { - return [ - //TODO: Payload types are hardcoded - `m=video 65002 RTP/SAVP 96`, - `a=rtpmap:96 H264/90000`, - `a=fmtp:96 profile-level-id=42801F`, - `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, - 'a=recvonly' - ] - } - }, this.incomingCallRequest ); - - this.incomingCallRequest = undefined - - //let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) - let sdp : string = [ - "v=0", - "m=audio 5000 RTP/AVP 110", - "c=IN IP4 172.21.0.134", - "a=rtpmap:110 speex/8000/1", - "m=video 5002 RTP/AVP 96", - "c=IN IP4 172.21.0.134", - "a=rtpmap:96 H264/90000", - ].join('\r\n') - //sdp = sdp.replaceAll(/a=crypto\:1.*/g, '') - //sdp = sdp.replaceAll(/RTP\/SAVP/g, 'RTP\/AVP') - //sdp = sdp.replaceAll('\r\n\r\n', '\r\n') - sdp = addTrackControls(sdp) - sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') - if( sipOptions.debugSip ) - this.log.d('SIP: Updated SDP:\n' + sdp); - - client.write(sdp) - client.end() - - this.session = sip - } - catch (e) { - this.console.error(e) - sip?.stop() - throw e; - } - }); - - this.resetStreamTimeout(); - - const mediaStreamOptions = Object.assign(this.getSipMediaStreamOptions(), { - refreshAt: Date.now() + STREAM_TIMEOUT, - }); - - const ffmpegInput: FFmpegInput = { - url: undefined, - container: 'sdp', - mediaStreamOptions, - inputArguments: [ - '-f', 'sdp', - '-i', playbackUrl, - ], - }; - this.currentMedia = ffmpegInput; - this.currentMediaMimeType = ScryptedMimeTypes.FFmpegInput; - - return mediaManager.createFFmpegMediaObject(ffmpegInput); - } - - getSipMediaStreamOptions(): ResponseMediaStreamOptions { - return { - id: 'sip', - name: 'SIP', - // this stream is NOT scrypted blessed due to wackiness in the h264 stream. - // tool: "scrypted", - container: 'sdp', - audio: { - // this is a hint to let homekit, et al, know that it's speex audio and needs transcoding. - codec: 'speex', - }, - source: 'cloud', // to disable prebuffering - userConfigurable: false, - }; - } - - async getVideoStreamOptions(): Promise { - return [ - this.getSipMediaStreamOptions(), - ] - } - - async getDevice(nativeId: string) : Promise { - return new BticinoSipLock(this) - } - - async releaseDevice(id: string, nativeId: string): Promise { - } - - reset() { - this.console.log("Reset the incoming call request") - this.incomingCallRequest = undefined - this.binaryState = false - } - - public async onRequest(request: HttpRequest, response: HttpResponse): Promise { - if (request.url.endsWith('/pressed')) { - this.binaryState = true - setTimeout( () => { - // Assumption that flexisip only holds this call active for 20 seconds ... might be revised - this.reset() - }, 20 * 1000 ) - response.send('Success', { - code: 200, - }); - } else { - response.send('Unsupported operation', { - code: 400, - }); - } - } - - private async doorbellWebhookEndpoint(): Promise { - let webhookUrl = await sdk.endpointManager.getLocalEndpoint( this.nativeId, { insecure: false, public: true }); - let endpoints = ["/pressed"] - this.console.log( webhookUrl + " , endpoints: " + endpoints.join(' - ') ) - return `${webhookUrl}`; - } - - private async doorbellLockWebhookEndpoint(): Promise { - let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId + '-lock', { insecure: false, public: true }); - let endpoints = ["/lock", "/unlock", "/unlocked", "/locked"] - this.console.log( webhookUrl + " -> endpoints: " + endpoints.join(' - ') ) - return `${webhookUrl}`; - } - - - - getGruuInstanceId(): string { - return this.md5.substring(0, 8) + '-' + this.md5.substring(8, 12) + '-' + this.md5.substring(12,16) + '-' + this.md5.substring(16, 32); - } +import { closeQuiet, createBindZero, listenZeroSingleClient } from '@scrypted/common/src/listen-cluster'; +import { sleep } from '@scrypted/common/src/sleep'; +import { RtspServer } from '@scrypted/common/src/rtsp-server'; +import { addTrackControls } from '@scrypted/common/src/sdp-utils'; +import sdk, { BinarySensor, Camera, DeviceProvider, FFmpegInput, HttpRequest, HttpRequestHandler, HttpResponse, Intercom, MediaObject, MediaStreamUrl, PictureOptions, ResponseMediaStreamOptions, ScryptedDevice, ScryptedDeviceBase, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk'; +import { SipCallSession } from '../../sip/src/sip-call-session'; +import { RtpDescription } from '../../sip/src/rtp-utils'; +import { VoicemailHandler } from './bticino-voicemailHandler'; +import { CompositeSipMessageHandler } from '../../sip/src/compositeSipMessageHandler'; +import { SipHelper } from './sip-helper'; +import child_process, { ChildProcess } from 'child_process'; +import dgram from 'dgram'; +import { BticinoStorageSettings } from './storage-settings'; +import { BticinoSipPlugin } from './main'; +import { BticinoSipLock } from './bticino-lock'; +import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from '@scrypted/common/src/media-helpers'; +import { PersistentSipManager } from './persistent-sip-manager'; +import { InviteHandler } from './bticino-inviteHandler'; +import { SipRequest } from '../../sip/src/sip-manager'; + +import { get } from 'http' + +const STREAM_TIMEOUT = 65000; +const { mediaManager } = sdk; + +export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvider, Intercom, Camera, VideoCamera, Settings, BinarySensor, HttpRequestHandler, VideoClips { + + private session: SipCallSession + private remoteRtpDescription: RtpDescription + private audioOutForwarder: dgram.Socket + private audioOutProcess: ChildProcess + private currentMedia: FFmpegInput | MediaStreamUrl + private currentMediaMimeType: string + private refreshTimeout: NodeJS.Timeout + public requestHandlers: CompositeSipMessageHandler = new CompositeSipMessageHandler() + public incomingCallRequest : SipRequest + private settingsStorage: BticinoStorageSettings = new BticinoStorageSettings( this ) + public voicemailHandler : VoicemailHandler = new VoicemailHandler(this) + private inviteHandler : InviteHandler = new InviteHandler(this) + //TODO: randomize this + private keyAndSalt : string = "/qE7OPGKp9hVGALG2KcvKWyFEZfSSvm7bYVDjT8X" + //private decodedSrtpOptions : SrtpOptions = decodeSrtpOptions( this.keyAndSalt ) + private persistentSipManager : PersistentSipManager + public doorbellWebhookUrl : string + public doorbellLockWebhookUrl : string + + constructor(nativeId: string, public provider: BticinoSipPlugin) { + super(nativeId) + + this.requestHandlers.add( this.voicemailHandler ).add( this.inviteHandler ) + this.persistentSipManager = new PersistentSipManager( this ); + (async() => { + this.doorbellWebhookUrl = await this.doorbellWebhookEndpoint() + this.doorbellLockWebhookUrl = await this.doorbellLockWebhookEndpoint() + })(); + } + + getVideoClips(options?: VideoClipOptions): Promise { + return new Promise( (resolve,reject ) => { + let c300x = SipHelper.getIntercomIp(this) + if( !c300x ) return [] + get(`http://${c300x}:8080/videoclips?raw=true&startTime=${options.startTime/1000}&endTime=${options.endTime/1000}`, (res) => { + let rawData = ''; + res.on('data', (chunk) => { rawData += chunk; }); + res.on('end', () => { + try { + const parsedData : [] = JSON.parse(rawData); + let videoClips : VideoClip[] = [] + parsedData.forEach( (item) => { + let videoClip : VideoClip = { + id: item['file'], + startTime: parseInt(item['info']['UnixTime']) * 1000, + duration: item['info']['Duration'] * 1000, + //description: item['info']['Date'], + thumbnailId: item['file'] + + } + videoClips.push( videoClip ) + } ) + return resolve(videoClips) + } catch (e) { + reject(e.message) + console.error(e.message); + } + }) + }); + }); + } + + getVideoClip(videoId: string): Promise { + let c300x = SipHelper.getIntercomIp(this) + const url = `http://${c300x}:8080/voicemail?msg=${videoId}/aswm.avi&raw=true`; + return mediaManager.createMediaObjectFromUrl(url); + } + getVideoClipThumbnail(thumbnailId: string): Promise { + let c300x = SipHelper.sipOptions(this) + const url = `http://${c300x}:8080/voicemail?msg=${thumbnailId}/aswm.jpg&raw=true`; + return mediaManager.createMediaObjectFromUrl(url); + } + + removeVideoClips(...videoClipIds: string[]): Promise { + //TODO + throw new Error('Method not implemented.') + } + + sipUnlock(): Promise { + this.log.i("unlocking C300X door ") + return this.persistentSipManager.enable().then( (sipCall) => { + sipCall.message( '*8*19*20##' ) + .then( () => + sleep(1000) + .then( () => sipCall.message( '*8*20*20##' ) ) + ) + } ) + } + + getAswmStatus() : Promise { + return this.persistentSipManager.enable().then( (sipCall) => { + sipCall.message( "GetAswmStatus!" ) + } ) + } + + async takePicture(option?: PictureOptions): Promise { + throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL."); + } + + async getPictureOptions(): Promise { + return + } + + getSettings(): Promise { + return this.settingsStorage.getSettings() + } + + putSetting(key: string, value: SettingValue): Promise { + return this.settingsStorage.putSetting(key, value) + } + + async startIntercom(media: MediaObject): Promise { + if (!this.session) + throw new Error("not in call"); + + this.stopIntercom(); + + const ffmpegInput: FFmpegInput = JSON.parse((await mediaManager.convertMediaObjectToBuffer(media, ScryptedMimeTypes.FFmpegInput)).toString()); + + const audioOutForwarder = await createBindZero() + this.audioOutForwarder = audioOutForwarder.server + audioOutForwarder.server.on('message', message => { + if( this.session ) + this.session.audioSplitter.send(message, 40004, this.remoteRtpDescription.address) + return null + }); + + const args = ffmpegInput.inputArguments.slice(); + args.push( + '-vn', '-dn', '-sn', + '-acodec', 'speex', + '-flags', '+global_header', + '-ac', '1', + '-ar', '8k', + '-f', 'rtp', + //'-srtp_out_suite', 'AES_CM_128_HMAC_SHA1_80', + //'-srtp_out_params', encodeSrtpOptions(this.decodedSrtpOptions), + `rtp://127.0.0.1:${audioOutForwarder.port}?pkt_size=188`, + ); + + this.console.log("===========================================") + safePrintFFmpegArguments( this.console, args ) + this.console.log("===========================================") + + const cp = child_process.spawn(await mediaManager.getFFmpegPath(), args); + ffmpegLogInitialOutput(this.console, cp) + this.audioOutProcess = cp; + cp.on('exit', () => this.console.log('two way audio ended')); + this.session.onCallEnded.subscribe(() => { + closeQuiet(audioOutForwarder.server); + safeKillFFmpeg(cp) + }); + } + + async stopIntercom(): Promise { + closeQuiet(this.audioOutForwarder) + this.audioOutProcess?.kill('SIGKILL') + this.audioOutProcess = undefined + this.audioOutForwarder = undefined + } + + resetStreamTimeout() { + this.log.d('starting/refreshing stream') + clearTimeout(this.refreshTimeout) + this.refreshTimeout = setTimeout(() => this.stopSession(), STREAM_TIMEOUT) + } + + hasActiveCall() { + return this.session; + } + + stopSession() { + if (this.session) { + this.log.d('ending sip session') + this.session.stop() + this.session = undefined + } + } + + async getVideoStream(options?: ResponseMediaStreamOptions): Promise { + if( !SipHelper.sipOptions( this ) ) { + // Bail out fast when no options are set and someone enables prebuffering + throw new Error('Please configure from/to/domain settings') + } + + if (options?.metadata?.refreshAt) { + if (!this.currentMedia?.mediaStreamOptions) + throw new Error("no stream to refresh"); + + const currentMedia = this.currentMedia + currentMedia.mediaStreamOptions.refreshAt = Date.now() + STREAM_TIMEOUT; + currentMedia.mediaStreamOptions.metadata = { + refreshAt: currentMedia.mediaStreamOptions.refreshAt + }; + this.resetStreamTimeout() + return mediaManager.createMediaObject(currentMedia, this.currentMediaMimeType) + } + + this.stopSession(); + + + const { clientPromise: playbackPromise, port: playbackPort, url: clientUrl } = await listenZeroSingleClient() + + const playbackUrl = clientUrl + + playbackPromise.then(async (client) => { + client.setKeepAlive(true, 10000) + let sip: SipCallSession + try { + let rtsp: RtspServer; + const cleanup = () => { + client.destroy(); + if (this.session === sip) + this.session = undefined + try { + this.log.d('cleanup(): stopping sip session.') + sip.stop() + } + catch (e) { + } + rtsp?.destroy() + } + + client.on('close', cleanup) + client.on('error', cleanup) + + let sipOptions = SipHelper.sipOptions( this ) + + sip = await this.persistentSipManager.session( sipOptions ); + // Validate this sooner + if( !sip ) return Promise.reject("Cannot create session") + + sip.onCallEnded.subscribe(cleanup) + + // Call the C300X + this.remoteRtpDescription = await sip.callOrAcceptInvite( + ( audio ) => { + return [ + //TODO: Payload types are hardcoded + `m=audio 65000 RTP/SAVP 110`, + `a=rtpmap:110 speex/8000`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, + ] + }, ( video ) => { + if( false ) { + //TODO: implement later + return [ + `m=video 0 RTP/SAVP 0` + ] + } else { + return [ + //TODO: Payload types are hardcoded + `m=video 65002 RTP/SAVP 96`, + `a=rtpmap:96 H264/90000`, + `a=fmtp:96 profile-level-id=42801F`, + `a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${this.keyAndSalt}`, + 'a=recvonly' + ] + } + }, this.incomingCallRequest ); + + this.incomingCallRequest = undefined + + //let sdp: string = replacePorts(this.remoteRtpDescription.sdp, 0, 0 ) + let sdp : string = [ + "v=0", + "m=audio 5000 RTP/AVP 110", + "c=IN IP4 127.0.0.1", + "a=rtpmap:110 speex/8000/1", + "m=video 5002 RTP/AVP 96", + "c=IN IP4 127.0.0.1", + "a=rtpmap:96 H264/90000", + ].join('\r\n') + //sdp = sdp.replaceAll(/a=crypto\:1.*/g, '') + //sdp = sdp.replaceAll(/RTP\/SAVP/g, 'RTP\/AVP') + //sdp = sdp.replaceAll('\r\n\r\n', '\r\n') + sdp = addTrackControls(sdp) + sdp = sdp.split('\n').filter(line => !line.includes('a=rtcp-mux')).join('\n') + if( sipOptions.debugSip ) + this.log.d('SIP: Updated SDP:\n' + sdp); + + client.write(sdp) + client.end() + + this.session = sip + } + catch (e) { + this.console.error(e) + sip?.stop() + throw e; + } + }); + + this.resetStreamTimeout(); + + const mediaStreamOptions = Object.assign(this.getSipMediaStreamOptions(), { + refreshAt: Date.now() + STREAM_TIMEOUT, + }); + + const ffmpegInput: FFmpegInput = { + url: undefined, + container: 'sdp', + mediaStreamOptions, + inputArguments: [ + '-f', 'sdp', + '-i', playbackUrl, + ], + }; + this.currentMedia = ffmpegInput; + this.currentMediaMimeType = ScryptedMimeTypes.FFmpegInput; + + return mediaManager.createFFmpegMediaObject(ffmpegInput); + } + + getSipMediaStreamOptions(): ResponseMediaStreamOptions { + return { + id: 'sip', + name: 'SIP', + // this stream is NOT scrypted blessed due to wackiness in the h264 stream. + // tool: "scrypted", + container: 'sdp', + audio: { + // this is a hint to let homekit, et al, know that it's speex audio and needs transcoding. + codec: 'speex', + }, + source: 'cloud', // to disable prebuffering + userConfigurable: false, + }; + } + + async getVideoStreamOptions(): Promise { + return [ + this.getSipMediaStreamOptions(), + ] + } + + async getDevice(nativeId: string) : Promise { + return new BticinoSipLock(this) + } + + async releaseDevice(id: string, nativeId: string): Promise { + } + + reset() { + this.console.log("Reset the incoming call request") + this.incomingCallRequest = undefined + this.binaryState = false + } + + public async onRequest(request: HttpRequest, response: HttpResponse): Promise { + if (request.url.endsWith('/pressed')) { + this.binaryState = true + setTimeout( () => { + // Assumption that flexisip only holds this call active for 20 seconds ... might be revised + this.reset() + }, 20 * 1000 ) + response.send('Success', { + code: 200, + }); + } else { + response.send('Unsupported operation', { + code: 400, + }); + } + } + + private async doorbellWebhookEndpoint(): Promise { + let webhookUrl = await sdk.endpointManager.getLocalEndpoint( this.nativeId, { insecure: false, public: true }); + let endpoints = ["/pressed"] + this.console.log( webhookUrl + " , endpoints: " + endpoints.join(' - ') ) + return `${webhookUrl}`; + } + + private async doorbellLockWebhookEndpoint(): Promise { + let webhookUrl = await sdk.endpointManager.getLocalEndpoint(this.nativeId + '-lock', { insecure: false, public: true }); + let endpoints = ["/lock", "/unlock", "/unlocked", "/locked"] + this.console.log( webhookUrl + " -> endpoints: " + endpoints.join(' - ') ) + return `${webhookUrl}`; + } } \ No newline at end of file diff --git a/plugins/bticino/src/main.ts b/plugins/bticino/src/main.ts index f8ce97d83f..5cc80af628 100644 --- a/plugins/bticino/src/main.ts +++ b/plugins/bticino/src/main.ts @@ -1,97 +1,97 @@ -import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, MediaObject, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, Setting, VideoClip, VideoClipOptions, VideoClips } from '@scrypted/sdk' -import { randomBytes } from 'crypto' -import { BticinoSipCamera } from './bticino-camera' - -const { systemManager, deviceManager } = sdk - -export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { - - devices = new Map() - - async getCreateDeviceSettings(): Promise { - return [ - { - key: 'newCamera', - title: 'Add Camera', - placeholder: 'Camera name, e.g.: Back Yard Camera, Baby Camera, etc', - } - ] - } - - async createDevice(settings: DeviceCreatorSettings): Promise { - const nativeId = randomBytes(4).toString('hex') - const name = settings.newCamera?.toString() - const camera = await this.updateDevice(nativeId, name) - - const device: Device = { - providerNativeId: nativeId, - info: { - //model: `${camera.model} (${camera.data.kind})`, - manufacturer: 'BticinoPlugin', - //firmware: camera.data.firmware_version, - //serialNumber: camera.data.device_id - }, - nativeId: nativeId + '-lock', - name: name + ' Lock', - type: ScryptedDeviceType.Lock, - interfaces: [ScryptedInterface.Lock, ScryptedInterface.HttpRequestHandler], - } - - const ret = await deviceManager.onDevicesChanged({ - providerNativeId: nativeId, - devices: [device], - }) - - let sipCamera : BticinoSipCamera = await this.getDevice(nativeId) - let foo : BticinoSipCamera = systemManager.getDeviceById(sipCamera.id) - - let lock = await sipCamera.getDevice(undefined) - lock.lockState = LockState.Locked - - return nativeId - } - - updateDevice(nativeId: string, name: string) { - return deviceManager.onDeviceDiscovered({ - nativeId, - info: { - //model: `${camera.model} (${camera.data.kind})`, - manufacturer: 'BticinoSipPlugin', - //firmware: camera.data.firmware_version, - //serialNumber: camera.data.device_id - }, - name, - interfaces: [ - ScryptedInterface.Camera, - ScryptedInterface.VideoCamera, - ScryptedInterface.Settings, - ScryptedInterface.Intercom, - ScryptedInterface.BinarySensor, - ScryptedDeviceType.DeviceProvider, - ScryptedInterface.HttpRequestHandler, - ScryptedInterface.VideoClips - ], - type: ScryptedDeviceType.Doorbell, - }) - } - - async getDevice(nativeId: string): Promise { - if (!this.devices.has(nativeId)) { - const camera = new BticinoSipCamera(nativeId, this) - this.devices.set(nativeId, camera) - } - return this.devices.get(nativeId) - } - - async releaseDevice(id: string, nativeId: string): Promise { - let camera = this.devices.get(nativeId) - if( camera ) { - camera.voicemailHandler.cancelVoicemailCheck() - if( this.devices.delete( nativeId ) ) { - this.console.log("Removed device from list: " + id + " / " + nativeId ) - } - } - } -} - +import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, LockState, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting } from '@scrypted/sdk' +import { randomBytes } from 'crypto' +import { BticinoSipCamera } from './bticino-camera' + +const { systemManager, deviceManager } = sdk + +export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator { + + devices = new Map() + + async getCreateDeviceSettings(): Promise { + return [ + { + key: 'newCamera', + title: 'Add Camera', + placeholder: 'Camera name, e.g.: Back Yard Camera, Baby Camera, etc', + } + ] + } + + async createDevice(settings: DeviceCreatorSettings): Promise { + const nativeId = randomBytes(4).toString('hex') + const name = settings.newCamera?.toString() + const camera = await this.updateDevice(nativeId, name) + + const device: Device = { + providerNativeId: nativeId, + info: { + //model: `${camera.model} (${camera.data.kind})`, + manufacturer: 'BticinoPlugin', + //firmware: camera.data.firmware_version, + //serialNumber: camera.data.device_id + }, + nativeId: nativeId + '-lock', + name: name + ' Lock', + type: ScryptedDeviceType.Lock, + interfaces: [ScryptedInterface.Lock, ScryptedInterface.HttpRequestHandler], + } + + const ret = await deviceManager.onDevicesChanged({ + providerNativeId: nativeId, + devices: [device], + }) + + let sipCamera : BticinoSipCamera = await this.getDevice(nativeId) + let foo : BticinoSipCamera = systemManager.getDeviceById(sipCamera.id) + + let lock = await sipCamera.getDevice(undefined) + lock.lockState = LockState.Locked + + return nativeId + } + + updateDevice(nativeId: string, name: string) { + return deviceManager.onDeviceDiscovered({ + nativeId, + info: { + //model: `${camera.model} (${camera.data.kind})`, + manufacturer: 'BticinoSipPlugin', + //firmware: camera.data.firmware_version, + //serialNumber: camera.data.device_id + }, + name, + interfaces: [ + ScryptedInterface.Camera, + ScryptedInterface.VideoCamera, + ScryptedInterface.Settings, + ScryptedInterface.Intercom, + ScryptedInterface.BinarySensor, + ScryptedDeviceType.DeviceProvider, + ScryptedInterface.HttpRequestHandler, + ScryptedInterface.VideoClips + ], + type: ScryptedDeviceType.Doorbell, + }) + } + + async getDevice(nativeId: string): Promise { + if (!this.devices.has(nativeId)) { + const camera = new BticinoSipCamera(nativeId, this) + this.devices.set(nativeId, camera) + } + return this.devices.get(nativeId) + } + + async releaseDevice(id: string, nativeId: string): Promise { + let camera = this.devices.get(nativeId) + if( camera ) { + camera.voicemailHandler.cancelVoicemailCheck() + if( this.devices.delete( nativeId ) ) { + this.console.log("Removed device from list: " + id + " / " + nativeId ) + } + } + } +} + export default new BticinoSipPlugin() \ No newline at end of file diff --git a/plugins/bticino/src/sip-helper.ts b/plugins/bticino/src/sip-helper.ts index 7cadac22b0..c005e78f04 100644 --- a/plugins/bticino/src/sip-helper.ts +++ b/plugins/bticino/src/sip-helper.ts @@ -1,42 +1,59 @@ -import { SipOptions } from "../../sip/src/sip-manager"; -import { BticinoSipCamera } from "./bticino-camera"; - -export class SipHelper { - public static sipOptions( camera : BticinoSipCamera ) : SipOptions { - // Might be removed soon? - if( camera.storage.getItem('sipto') && camera.storage.getItem('sipto').toString().indexOf(';') > 0 ) { - camera.storage.setItem('sipto', camera.storage.getItem('sipto').toString().split(';')[0] ) - } - const from = camera.storage.getItem('sipfrom')?.trim() - const to = camera.storage.getItem('sipto')?.trim() - const localIp = from?.split(':')[0].split('@')[1] - const deviceIp = to.split('@')[1] - // Although this might not occur directly, each camera should run on its own port - // Might need to use a random free port here (?) - const localPort = parseInt(from?.split(':')[1]) || 5060 - const domain = camera.storage.getItem('sipdomain')?.trim() - const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '600' - const sipdebug : boolean = camera.storage.getItem('sipdebug')?.toLocaleLowerCase() === 'true' || false - - if (!from || !to || !localIp || !localPort || !domain || !expiration ) { - camera.log.e('Error: SIP From/To/Domain URIs not specified!') - throw new Error('SIP From/To/Domain URIs not specified!') - } - - return { - deviceIp: deviceIp, - from: "sip:" + from, - //TCP is more reliable for large messages, also see useTcp=true below - to: "sip:" + to + ";transport=tcp", - domain: domain, - expire: Number.parseInt( expiration ), - localIp, - localPort, - debugSip: sipdebug, - gruuInstanceId: camera.getGruuInstanceId(), - useTcp: true, - sipRequestHandler: camera.requestHandlers - - } - } +import { SipOptions } from "../../sip/src/sip-manager"; +import { BticinoSipCamera } from "./bticino-camera"; +import crypto from 'crypto'; + +export class SipHelper { + public static sipOptions( camera : BticinoSipCamera ) : SipOptions { + // Might be removed soon? + if( camera.storage.getItem('sipto') && camera.storage.getItem('sipto').toString().indexOf(';') > 0 ) { + camera.storage.setItem('sipto', camera.storage.getItem('sipto').toString().split(';')[0] ) + } + const from = camera.storage.getItem('sipfrom')?.trim() + const to = camera.storage.getItem('sipto')?.trim() + const localIp = from?.split(':')[0].split('@')[1] + // Although this might not occur directly, each camera should run on its own port + // Might need to use a random free port here (?) + const localPort = parseInt(from?.split(':')[1]) || 5060 + const domain = camera.storage.getItem('sipdomain')?.trim() + const expiration : string = camera.storage.getItem('sipexpiration')?.trim() || '600' + const sipdebug : boolean = camera.storage.getItem('sipdebug')?.toLocaleLowerCase() === 'true' || false + + if (!from || !to || !localIp || !localPort || !domain || !expiration ) { + camera.log.e('Error: SIP From/To/Domain URIs not specified!') + throw new Error('SIP From/To/Domain URIs not specified!') + } + + return { + from: "sip:" + from, + //TCP is more reliable for large messages, also see useTcp=true below + to: "sip:" + to + ";transport=tcp", + domain: domain, + expire: Number.parseInt( expiration ), + localIp, + localPort, + debugSip: sipdebug, + gruuInstanceId: SipHelper.getGruuInstanceId(camera), + useTcp: true, + sipRequestHandler: camera.requestHandlers + + } + } + + public static getIntercomIp( camera : BticinoSipCamera ): string { + let to = camera.storage.getItem('sipto')?.trim(); + if( to ) { + return to.split('@')[1]; + } + return + } + + public static getGruuInstanceId( camera : BticinoSipCamera ): string { + let md5 = camera.storage.getItem('md5hash') + if( !md5 ) { + md5 = crypto.createHash('md5').update( camera.nativeId ).digest("hex") + md5 = md5.substring(0, 8) + '-' + md5.substring(8, 12) + '-' + md5.substring(12,16) + '-' + md5.substring(16, 32) + camera.storage.setItem('md5has', md5) + } + return md5 + } } \ No newline at end of file