mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 06:03:27 +00:00
core/mqtt: scripts refactor
This commit is contained in:
1
common/fs/@types/node
Symbolic link
1
common/fs/@types/node
Symbolic link
@@ -0,0 +1 @@
|
||||
../../node_modules/@types/node
|
||||
1
common/fs/@types/sdk/index.d.ts
vendored
Symbolic link
1
common/fs/@types/sdk/index.d.ts
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../sdk/index.d.ts
|
||||
1
common/fs/@types/sdk/types.d.ts
vendored
Symbolic link
1
common/fs/@types/sdk/types.d.ts
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../sdk/types/index.d.ts
|
||||
1
common/fs/sdk/index.d.ts
vendored
1
common/fs/sdk/index.d.ts
vendored
@@ -1 +0,0 @@
|
||||
../../../sdk/index.d.ts
|
||||
1
common/fs/sdk/types.d.ts
vendored
1
common/fs/sdk/types.d.ts
vendored
@@ -1 +0,0 @@
|
||||
../../../sdk/types/index.d.ts
|
||||
112
common/package-lock.json
generated
112
common/package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@koush/werift": "file:../external/werift/packages/webrtc",
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -118,6 +119,73 @@
|
||||
"integrity": "sha512-nmP+VR4oT0pJUPFbKE4SXj3Yb4Q/kz3M9dSAO1GGMebRKWHQxLfDNmU/yh3xxCJha3N60nQ/JwXWwOE/ZSEVag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz",
|
||||
"integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch-commonjs": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.1.1.tgz",
|
||||
"integrity": "sha512-TgkdVJdiEaauzWwB9NoD4TvHZFtG6KKEffvotWf9WNIyoRZHsCFjGfb3bhkIXrMt3YFgFi8ZApbwWoe1h3XTpA==",
|
||||
"dependencies": {
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"web-streams-polyfill": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||
@@ -129,6 +197,14 @@
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -206,10 +282,46 @@
|
||||
"integrity": "sha512-nmP+VR4oT0pJUPFbKE4SXj3Yb4Q/kz3M9dSAO1GGMebRKWHQxLfDNmU/yh3xxCJha3N60nQ/JwXWwOE/ZSEVag==",
|
||||
"dev": true
|
||||
},
|
||||
"fetch-blob": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz",
|
||||
"integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==",
|
||||
"requires": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"requires": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
}
|
||||
},
|
||||
"node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
|
||||
},
|
||||
"node-fetch-commonjs": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.1.1.tgz",
|
||||
"integrity": "sha512-TgkdVJdiEaauzWwB9NoD4TvHZFtG6KKEffvotWf9WNIyoRZHsCFjGfb3bhkIXrMt3YFgFi8ZApbwWoe1h3XTpA==",
|
||||
"requires": {
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"web-streams-polyfill": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz",
|
||||
"integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA=="
|
||||
},
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@koush/werift": "file:../external/werift/packages/webrtc",
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
4
common/src/eval/monaco/script-device.ts
Normal file
4
common/src/eval/monaco/script-device.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface ScriptDevice {
|
||||
handle<T>(handler?: T & object): void;
|
||||
handleTypes(...interfaces: string[]): void;
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { TranspileOptions } from "typescript";
|
||||
import sdk, { ScryptedDeviceBase, ScryptedInterface, ScryptedDeviceType } from "@scrypted/sdk";
|
||||
import sdk, { ScryptedDeviceBase, MixinDeviceBase, ScryptedInterface, ScryptedDeviceType } from "@scrypted/sdk";
|
||||
import vm from "vm";
|
||||
import fs from 'fs';
|
||||
import { newThread } from '../../server/src/threading';
|
||||
import { newThread } from '../../../server/src/threading';
|
||||
import { ScriptDevice } from "./monaco/script-device";
|
||||
import { ScryptedInterfaceDescriptors } from "@scrypted/sdk/types";
|
||||
import fetch from 'node-fetch-commonjs';
|
||||
|
||||
const { systemManager, deviceManager, mediaManager, endpointManager } = sdk;
|
||||
|
||||
@@ -44,8 +47,8 @@ async function tsCompileThread(source: string, options: TranspileOptions = null)
|
||||
}
|
||||
|
||||
function getTypeDefs() {
|
||||
const scryptedTypesDefs = fs.readFileSync('sdk/types.d.ts').toString();
|
||||
const scryptedIndexDefs = fs.readFileSync('sdk/index.d.ts').toString();
|
||||
const scryptedTypesDefs = fs.readFileSync('@types/sdk/types.d.ts').toString();
|
||||
const scryptedIndexDefs = fs.readFileSync('@types/sdk/index.d.ts').toString();
|
||||
return {
|
||||
scryptedIndexDefs,
|
||||
scryptedTypesDefs,
|
||||
@@ -53,59 +56,71 @@ function getTypeDefs() {
|
||||
}
|
||||
|
||||
export async function scryptedEval(device: ScryptedDeviceBase, script: string, extraLibs: { [lib: string]: string }, params: { [name: string]: any }) {
|
||||
const libs = Object.assign({
|
||||
types: getTypeDefs().scryptedTypesDefs,
|
||||
}, extraLibs);
|
||||
const allScripts = Object.values(libs).join('\n').toString() + script;
|
||||
let compiled: string;
|
||||
try {
|
||||
const libs = Object.assign({
|
||||
types: getTypeDefs().scryptedTypesDefs,
|
||||
}, extraLibs);
|
||||
const allScripts = Object.values(libs).join('\n').toString() + script;
|
||||
const compiled = await tsCompileThread(allScripts);
|
||||
|
||||
const allParams = Object.assign({}, params, {
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
log: device.log,
|
||||
console: device.console,
|
||||
localStorage: device.storage,
|
||||
device,
|
||||
exports: {},
|
||||
ScryptedInterface,
|
||||
ScryptedDeviceType,
|
||||
});
|
||||
|
||||
try {
|
||||
const asyncWrappedCompiled = `(async function() {\n${compiled}\n})()`;
|
||||
const f = vm.compileFunction(asyncWrappedCompiled, Object.keys(allParams), {
|
||||
filename: 'script.js',
|
||||
});
|
||||
|
||||
try {
|
||||
return await f(...Object.values(allParams));
|
||||
}
|
||||
catch (e) {
|
||||
device.log.e('Error running script.');
|
||||
device.console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
device.log.e('Error evaluating script.');
|
||||
device.console.error(e);
|
||||
throw e;
|
||||
}
|
||||
compiled = await tsCompileThread(allScripts);
|
||||
}
|
||||
catch (e) {
|
||||
device.log.e('Error compiling script.');
|
||||
device.log.e('Error compiling typescript.');
|
||||
device.console.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
const allParams = Object.assign({}, params, {
|
||||
fetch,
|
||||
ScryptedDeviceBase,
|
||||
MixinDeviceBase,
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
log: device.log,
|
||||
console: device.console,
|
||||
localStorage: device.storage,
|
||||
device,
|
||||
exports: {},
|
||||
ScryptedInterface,
|
||||
ScryptedDeviceType,
|
||||
});
|
||||
|
||||
const asyncWrappedCompiled = `return (async function() {\n${compiled}\n})`;
|
||||
let asyncFunction: any;
|
||||
try {
|
||||
const functionGenerator = vm.compileFunction(asyncWrappedCompiled, Object.keys(allParams), {
|
||||
filename: 'script.js',
|
||||
});
|
||||
asyncFunction = functionGenerator(...Object.values(allParams));
|
||||
}
|
||||
catch (e) {
|
||||
device.log.e('Error evaluating javascript.');
|
||||
device.console.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return await asyncFunction();
|
||||
}
|
||||
catch (e) {
|
||||
device.log.e('Error running script.');
|
||||
device.console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
|
||||
const bufferTypeDefs = fs.readFileSync('@types/node/buffer.d.ts').toString();
|
||||
|
||||
const safeLibs = {
|
||||
bufferTypeDefs,
|
||||
};
|
||||
|
||||
const libs = Object.assign(getTypeDefs(), extraLibs);
|
||||
|
||||
function monacoEvalDefaultsFunction(monaco, libs) {
|
||||
function monacoEvalDefaultsFunction(monaco, safeLibs, libs) {
|
||||
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions(
|
||||
Object.assign(
|
||||
{},
|
||||
@@ -156,14 +171,55 @@ export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
|
||||
libs['sdk'],
|
||||
"node_modules/@types/scrypted__sdk/index.d.ts"
|
||||
);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
safeLibs.bufferTypeDefs,
|
||||
"node_modules/@types/node/buffer.d.ts"
|
||||
);
|
||||
}
|
||||
|
||||
return `(function() {
|
||||
const safeLibs = ${JSON.stringify(safeLibs)};
|
||||
const libs = ${JSON.stringify(libs)};
|
||||
|
||||
return (monaco) => {
|
||||
(${monacoEvalDefaultsFunction})(monaco, libs);
|
||||
(${monacoEvalDefaultsFunction})(monaco, safeLibs, libs);
|
||||
}
|
||||
})();
|
||||
`;
|
||||
}
|
||||
|
||||
export interface ScriptDeviceImpl extends ScriptDevice {
|
||||
mergeHandler(device: ScryptedDeviceBase): string[];
|
||||
}
|
||||
|
||||
const methodInterfaces: { [method: string]: string } = {};
|
||||
for (const desc of Object.values(ScryptedInterfaceDescriptors)) {
|
||||
for (const method of desc.methods) {
|
||||
methodInterfaces[method] = desc.name;
|
||||
}
|
||||
}
|
||||
|
||||
export function createScriptDevice(baseInterfaces: string[]): ScriptDeviceImpl {
|
||||
let scriptHandler: any;
|
||||
const allInterfaces = baseInterfaces.slice();
|
||||
|
||||
return {
|
||||
handle: <T>(handler?: T & object) => {
|
||||
scriptHandler = handler;
|
||||
},
|
||||
handleTypes: (...interfaces: ScryptedInterface[]) => {
|
||||
allInterfaces.push(...interfaces);
|
||||
},
|
||||
mergeHandler: (device: ScryptedDeviceBase) => {
|
||||
const handler = scriptHandler || {};
|
||||
for (const method of Object.keys(handler)) {
|
||||
const iface = methodInterfaces[method];
|
||||
if (iface)
|
||||
allInterfaces.push(iface);
|
||||
}
|
||||
Object.assign(device, handler);
|
||||
return allInterfaces;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -3,11 +3,12 @@ import { ChildProcess } from 'child_process';
|
||||
import sdk from "@scrypted/sdk";
|
||||
import { ffmpegLogInitialOutput } from './media-helpers';
|
||||
import { MP4Atom, parseFragmentedMP4 } from './stream-parser';
|
||||
import { Readable } from 'stream';
|
||||
import { Duplex, Readable } from 'stream';
|
||||
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
export interface FFMpegFragmentedMP4Session {
|
||||
socket: Duplex;
|
||||
cp: ChildProcess;
|
||||
generator: AsyncGenerator<MP4Atom>;
|
||||
}
|
||||
@@ -32,6 +33,7 @@ export async function startFFMPegFragmetedMP4Session(inputArguments: string[], a
|
||||
ffmpegLogInitialOutput(console, cp);
|
||||
|
||||
return {
|
||||
socket: undefined,
|
||||
cp,
|
||||
generator: parseFragmentedMP4(cp.stdio[3] as Readable),
|
||||
};
|
||||
|
||||
1
plugins/core/fs/@types
Symbolic link
1
plugins/core/fs/@types
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../common/fs/@types
|
||||
@@ -1 +0,0 @@
|
||||
../../../common/fs/sdk
|
||||
4
plugins/core/package-lock.json
generated
4
plugins/core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.0.197",
|
||||
"version": "0.0.200",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.0.197",
|
||||
"version": "0.0.200",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@koush/wrtc": "^0.5.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.0.197",
|
||||
"version": "0.0.200",
|
||||
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
4
plugins/core/src/api/util.ts
Normal file
4
plugins/core/src/api/util.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import type { ScriptDevice } from "@scrypted/common/src/eval/monaco/script-device";
|
||||
import type { ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
|
||||
declare const device: ScryptedDeviceBase & ScriptDevice;
|
||||
@@ -63,7 +63,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
mediaCore: MediaCore;
|
||||
automations = new Map<string, Automation>();
|
||||
aggregate = new Map<string, AggregateDevice>();
|
||||
scripts = new Map<string, Script>();
|
||||
scripts = new Map<string, Promise<Script>>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -93,8 +93,15 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
}
|
||||
else if (nativeId?.startsWith('script:')) {
|
||||
const script = new Script(nativeId);
|
||||
this.scripts.set(nativeId, script);
|
||||
reportScript(nativeId);
|
||||
this.scripts.set(nativeId, (async () => {
|
||||
if (script.providedInterfaces.length > 2) {
|
||||
await script.run();
|
||||
}
|
||||
else {
|
||||
reportScript(nativeId);
|
||||
}
|
||||
return script;
|
||||
})());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +131,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
const nativeId = `script:${Math.random()}`;
|
||||
await reportScript(nativeId);
|
||||
const script = new Script(nativeId);
|
||||
this.scripts.set(nativeId, script);
|
||||
this.scripts.set(nativeId, Promise.resolve(script));
|
||||
const { id } = script;
|
||||
sendJSON(res, {
|
||||
id,
|
||||
@@ -159,15 +166,16 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
});
|
||||
}
|
||||
|
||||
getDevice(nativeId: string) {
|
||||
async getDevice(nativeId: string) {
|
||||
if (nativeId === 'mediacore')
|
||||
return this.mediaCore;
|
||||
if (nativeId?.startsWith('automation:'))
|
||||
return this.automations.get(nativeId);
|
||||
if (nativeId?.startsWith('aggregate:'))
|
||||
return this.aggregate.get(nativeId);
|
||||
if (nativeId?.startsWith('script:'))
|
||||
if (nativeId?.startsWith('script:')) {
|
||||
return this.scripts.get(nativeId);
|
||||
}
|
||||
}
|
||||
|
||||
checkEngineIoEndpoint(request: HttpRequest, name: string) {
|
||||
@@ -203,7 +211,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
|
||||
}
|
||||
|
||||
if (this.checkEngineIoEndpoint(request, 'videocamera')) {
|
||||
const url = new URL(`http://localhost${request.url}`);
|
||||
const url = new URL(`http://localhost${request.url}`);
|
||||
const deviceId = url.searchParams.get('deviceId');
|
||||
const camera = systemManager.getDeviceById<VideoCamera & RTCSignalingChannel>(deviceId);
|
||||
if (!camera)
|
||||
|
||||
8
plugins/core/src/monaco.ts
Normal file
8
plugins/core/src/monaco.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createMonacoEvalDefaults } from "../../../common/src/eval/scrypted-eval";
|
||||
|
||||
const libs = {
|
||||
script: require("!!raw-loader!@scrypted/common/src/eval/monaco/script-device.ts").default,
|
||||
util: require("!!raw-loader!./api/util.ts").default,
|
||||
};
|
||||
|
||||
export const monacoEvalDefaults = createMonacoEvalDefaults(libs);
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Scriptable, Program, ScryptedDeviceBase, ScriptSource } from "@scrypted/sdk";
|
||||
import { createMonacoEvalDefaults } from "@scrypted/common/src/scrypted-eval";
|
||||
import sdk, { Scriptable, Program, ScryptedDeviceBase, ScriptSource, ScryptedInterface, ScryptedDeviceType } from "@scrypted/sdk";
|
||||
import { scryptedEval } from "./scrypted-eval";
|
||||
import { monacoEvalDefaults } from "./monaco";
|
||||
import { createScriptDevice, ScriptDeviceImpl } from "@scrypted/common/src/eval/scrypted-eval";
|
||||
|
||||
const monacoEvalDefaults = createMonacoEvalDefaults({});
|
||||
const { log, deviceManager, systemManager } = sdk;
|
||||
|
||||
export class Script extends ScryptedDeviceBase implements Scriptable, Program {
|
||||
export class Script extends ScryptedDeviceBase implements Scriptable, Program, ScriptDeviceImpl {
|
||||
constructor(nativeId: string) {
|
||||
super(nativeId);
|
||||
}
|
||||
@@ -40,18 +41,63 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program {
|
||||
}
|
||||
}
|
||||
|
||||
run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
async postRunScript() {
|
||||
const allInterfaces = this.mergeHandler(this);
|
||||
if (allInterfaces.length !== 2) {
|
||||
await deviceManager.onDeviceDiscovered({
|
||||
nativeId: this.nativeId,
|
||||
interfaces: allInterfaces,
|
||||
type: ScryptedDeviceType.Unknown,
|
||||
name: this.providedName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
prepareScript() {
|
||||
Object.assign(this, createScriptDevice([
|
||||
ScryptedInterface.Scriptable,
|
||||
ScryptedInterface.Program,
|
||||
]));
|
||||
}
|
||||
|
||||
async run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
this.prepareScript();
|
||||
|
||||
try {
|
||||
const data = JSON.parse(this.storage.getItem('data'));
|
||||
return scryptedEval(this, data['script.ts'], variables);
|
||||
|
||||
const ret = await scryptedEval(this, data['script.ts'], Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
await this.postRunScript();
|
||||
return ret;
|
||||
}
|
||||
catch (e) {
|
||||
this.log.e('error loading script');
|
||||
this.console.error(e);
|
||||
this.console.error('error loading script', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async eval(source: ScriptSource, variables: { [name: string]: any }) {
|
||||
return scryptedEval(this, source.script, variables);
|
||||
this.prepareScript();
|
||||
|
||||
const ret = await scryptedEval(this, source.script, Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
await this.postRunScript();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// will be done at runtime
|
||||
mergeHandler(device: ScryptedDeviceBase): string[] {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
handle<T>(handler?: T & object): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
handleTypes(...interfaces: string[]): void {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
import { scryptedEval as scryptedEvalBase } from "@scrypted/common/src/scrypted-eval";
|
||||
import { scryptedEval as scryptedEvalBase } from "@scrypted/common/src/eval/scrypted-eval";
|
||||
|
||||
export async function scryptedEval(device: ScryptedDeviceBase, script: string, params: { [name: string]: any }) {
|
||||
return scryptedEvalBase(device, script, {}, params);
|
||||
|
||||
@@ -45,6 +45,8 @@ export async function streamCamera(mediaManager: MediaManager, device: ScryptedD
|
||||
remoteAudio.play();
|
||||
console.log('received track', ev.track);
|
||||
};
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
export async function createBlobUrl(mediaManager: MediaManager, mediaObject: MediaObject): Promise<string> {
|
||||
|
||||
@@ -22,6 +22,7 @@ export async function getDeviceAvailableMixins(systemManager: SystemManager, dev
|
||||
const results = await Promise.all(getAllDevices<MixinProvider>(systemManager).map(async (check) => {
|
||||
try {
|
||||
if (check.interfaces.includes(ScryptedInterface.MixinProvider)) {
|
||||
console.time('mixin provider' + check.name);
|
||||
if (await check.canMixin(device.type, device.interfaces))
|
||||
return check;
|
||||
}
|
||||
@@ -29,6 +30,9 @@ export async function getDeviceAvailableMixins(systemManager: SystemManager, dev
|
||||
catch (e) {
|
||||
console.error("mixin check error", check.id, e);
|
||||
}
|
||||
finally {
|
||||
console.timeEnd('mixin provider' + check.name);
|
||||
}
|
||||
}));
|
||||
|
||||
return results.filter(result => !!result);
|
||||
|
||||
1
plugins/mqtt/fs
Symbolic link
1
plugins/mqtt/fs
Symbolic link
@@ -0,0 +1 @@
|
||||
../../common/fs
|
||||
74
plugins/mqtt/package-lock.json
generated
74
plugins/mqtt/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/mqtt",
|
||||
"version": "0.0.38",
|
||||
"version": "0.0.39",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/mqtt",
|
||||
"version": "0.0.38",
|
||||
"version": "0.0.39",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.6.1",
|
||||
"aedes": "^0.46.1",
|
||||
@@ -17,23 +17,32 @@
|
||||
"websocket-stream": "^5.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/nunjucks": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.0.134",
|
||||
"../../common": {
|
||||
"name": "@scrypted/common",
|
||||
"version": "1.0.1",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.14.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.15.4",
|
||||
"@babel/plugin-transform-typescript": "^7.15.8",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@koush/werift": "file:../external/werift/packages/webrtc",
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.9.0"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.0.173",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.4",
|
||||
"babel-loader": "^8.2.3",
|
||||
@@ -43,7 +52,6 @@
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.2.6",
|
||||
"webpack": "^5.59.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -60,12 +68,17 @@
|
||||
"stringify-object": "^3.3.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.22.8",
|
||||
"typescript-json-schema": "^0.50.1"
|
||||
"typescript-json-schema": "^0.50.1",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
"resolved": "../../common",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@scrypted/sdk": {
|
||||
"resolved": "../../sdk",
|
||||
"link": true
|
||||
@@ -344,9 +357,9 @@
|
||||
"integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
|
||||
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -828,16 +841,19 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/common": {
|
||||
"version": "file:../../common",
|
||||
"requires": {
|
||||
"@koush/werift": "file:../external/werift/packages/webrtc",
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@types/node": "^16.9.0",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
},
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../../sdk",
|
||||
"requires": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.14.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.15.4",
|
||||
"@babel/plugin-transform-typescript": "^7.15.8",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/preset-typescript": "^7.16.7",
|
||||
"@types/node": "^16.11.1",
|
||||
"@types/stringify-object": "^4.0.0",
|
||||
"adm-zip": "^0.4.13",
|
||||
@@ -850,11 +866,11 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"stringify-object": "^3.3.0",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.22.8",
|
||||
"typescript-json-schema": "^0.50.1",
|
||||
"webpack": "^5.59.0"
|
||||
"webpack": "^5.59.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
@@ -1079,9 +1095,9 @@
|
||||
"integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
|
||||
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||
},
|
||||
"from2": {
|
||||
"version": "2.3.0",
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@types/nunjucks": "^3.2.0"
|
||||
},
|
||||
"version": "0.0.38"
|
||||
"version": "0.0.39"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ScriptDevice } from '@scrypted/common/src/eval/monaco/script-device';
|
||||
|
||||
export interface MqttEvent {
|
||||
buffer?: Buffer;
|
||||
json?: any;
|
||||
@@ -8,9 +10,7 @@ export interface MqttSubscriptions {
|
||||
[topic: string]: (event: MqttEvent) => void;
|
||||
}
|
||||
|
||||
export interface MqttClient {
|
||||
export interface MqttClient extends ScriptDevice {
|
||||
subscribe(subscriptions: MqttSubscriptions, options?: any): void;
|
||||
handle<T>(handler?: T & object): void;
|
||||
handleTypes(...interfaces: string[]): void;
|
||||
publish(topic: string, value: any): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ScryptedDeviceBase, ScryptedInterface } from "@scrypted/sdk";
|
||||
import type { ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
import type { MqttClient, MqttEvent, MqttSubscriptions } from "./mqtt-client";
|
||||
|
||||
declare const device: ScryptedDeviceBase;
|
||||
|
||||
@@ -15,21 +15,13 @@ import { MqttAutoDiscoveryProvider } from './autodiscovery/autodiscovery';
|
||||
import { SettingsMixinDeviceBase } from "../../../common/src/settings-mixin";
|
||||
import { connect, Client } from 'mqtt';
|
||||
import { isPublishable } from './publishable-types';
|
||||
import { createScriptDevice, ScriptDeviceImpl } from '@scrypted/common/src/eval/scrypted-eval';
|
||||
|
||||
const loopbackLight = require("!!raw-loader!./examples/loopback-light.ts").default;
|
||||
|
||||
const methodInterfaces: { [method: string]: string } = {};
|
||||
for (const desc of Object.values(ScryptedInterfaceDescriptors)) {
|
||||
for (const method of desc.methods) {
|
||||
methodInterfaces[method] = desc.name;
|
||||
}
|
||||
}
|
||||
|
||||
const { log, deviceManager, systemManager } = sdk;
|
||||
|
||||
class MqttDevice extends MqttDeviceBase implements Scriptable {
|
||||
handler: any;
|
||||
|
||||
constructor(nativeId: string) {
|
||||
super(nativeId);
|
||||
|
||||
@@ -75,8 +67,6 @@ class MqttDevice extends MqttDeviceBase implements Scriptable {
|
||||
}): Promise<any> {
|
||||
const { script } = source;
|
||||
try {
|
||||
this.handler = undefined;
|
||||
|
||||
const client = this.connectClient();
|
||||
client.on('connect', () => this.console.log('mqtt client connected'));
|
||||
client.on('disconnect', () => this.console.log('mqtt client disconnected'));
|
||||
@@ -84,12 +74,7 @@ class MqttDevice extends MqttDeviceBase implements Scriptable {
|
||||
this.console.log('mqtt client error', e);
|
||||
});
|
||||
|
||||
const allInterfaces: string[] = [
|
||||
ScryptedInterface.Scriptable,
|
||||
ScryptedInterface.Settings,
|
||||
]
|
||||
|
||||
const mqtt: MqttClient = {
|
||||
const mqtt: MqttClient & ScriptDeviceImpl = {
|
||||
subscribe: (subscriptions: MqttSubscriptions, options?: any) => {
|
||||
for (const topic of Object.keys(subscriptions)) {
|
||||
const fullTopic = this.pathname + topic;
|
||||
@@ -122,32 +107,23 @@ class MqttDevice extends MqttDeviceBase implements Scriptable {
|
||||
});
|
||||
}
|
||||
},
|
||||
handle: <T>(handler?: T & object) => {
|
||||
this.handler = handler;
|
||||
},
|
||||
handleTypes: (...interfaces: ScryptedInterface[]) => {
|
||||
allInterfaces.push(...interfaces);
|
||||
},
|
||||
publish: async (topic: string, value: any) => {
|
||||
if (typeof value === 'object')
|
||||
value = JSON.stringify(value);
|
||||
if (value.constructor.name !== Buffer.name)
|
||||
value = value.toString();
|
||||
client.publish(this.pathname + topic, value);
|
||||
}
|
||||
},
|
||||
...createScriptDevice([
|
||||
ScryptedInterface.Scriptable,
|
||||
ScryptedInterface.Settings,
|
||||
])
|
||||
}
|
||||
await scryptedEval(this, script, {
|
||||
mqtt,
|
||||
});
|
||||
|
||||
const handler = this.handler || {};
|
||||
for (const method of Object.keys(handler)) {
|
||||
const iface = methodInterfaces[method];
|
||||
if (iface)
|
||||
allInterfaces.push(iface);
|
||||
}
|
||||
|
||||
Object.assign(this, handler);
|
||||
const allInterfaces = mqtt.mergeHandler(this);
|
||||
|
||||
await deviceManager.onDeviceDiscovered({
|
||||
nativeId: this.nativeId,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createMonacoEvalDefaults } from "../../../common/src/scrypted-eval";
|
||||
import { createMonacoEvalDefaults } from "@scrypted/common/src/eval/scrypted-eval";
|
||||
|
||||
const libs = {
|
||||
types: require("!!raw-loader!@scrypted/sdk/types/index.d.ts").default,
|
||||
sdk: require("!!raw-loader!@scrypted/sdk/index.d.ts").default,
|
||||
script: require("!!raw-loader!@scrypted/common/src/eval/monaco/script-device.ts").default,
|
||||
client: require("!!raw-loader!./api/mqtt-client.ts").default,
|
||||
frigate: require("!!raw-loader!./api/frigate.ts").default,
|
||||
util: require("!!raw-loader!./api/util.ts").default,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
import { scryptedEval as scryptedEvalBase } from "../../../common/src/scrypted-eval";
|
||||
import { scryptedEval as scryptedEvalBase } from "@scrypted/common/src/eval/scrypted-eval";
|
||||
|
||||
const util = require("!!raw-loader!./api/util.ts").default;
|
||||
const frigate = require("!!raw-loader!./api/frigate.ts").default;
|
||||
|
||||
Reference in New Issue
Block a user