Skip to content

Commit

Permalink
core: fix new script creation
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Jan 2, 2024
1 parent 6e10172 commit 184f293
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 35 deletions.
2 changes: 1 addition & 1 deletion plugins/core/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"scrypted.debugHost": "scrypted-server",
"scrypted.debugHost": "127.0.0.1",
}
4 changes: 2 additions & 2 deletions plugins/core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugins/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/core",
"version": "0.2.2",
"version": "0.2.3",
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
"author": "Scrypted",
"license": "Apache-2.0",
Expand Down
54 changes: 43 additions & 11 deletions plugins/core/src/script-core.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting } from "@scrypted/sdk";
import { Script } from "./script";
import sdk from '@scrypted/sdk';
import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting } from '@scrypted/sdk';
import { randomBytes } from "crypto";
import fs from 'fs';
import path from "path/posix";
import { Worker } from "worker_threads";
import { Script } from "./script";

const { deviceManager } = sdk;
export const ScriptCoreNativeId = 'scriptcore';
Expand Down Expand Up @@ -42,6 +41,10 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
const nativeId = 'script:' + randomBytes(8).toString('hex');
await this.reportScript(nativeId, name?.toString());
const script = new Script(nativeId);
this.scripts.set(nativeId, {
script,
worker: undefined,
});
if (template) {
try {
await script.saveScript({
Expand Down Expand Up @@ -76,20 +79,39 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De

async getDevice(nativeId: string) {
const e = this.scripts.get(nativeId);
if (e)
return e;
if (e) {
if (e.script)
return e.script;
e.worker?.terminate();
this.scripts.delete(nativeId);
}

let script = new Script(nativeId);
let worker: Worker;

const triggerDeviceDiscover = async (name: string, type: ScryptedDeviceType, interfaces: string[]) => {
const e = this.scripts.get(nativeId);
if (e?.script == script)
e.script = undefined;

const device: Device = {
providerNativeId: this.nativeId,
name,
nativeId,
type,
interfaces,
refresh: true,
};
return await deviceManager.onDeviceDiscovered(device);
};

if (script.providedInterfaces.length > 2) {
const fork = sdk.fork<{
newScript: typeof newScript,
}>();
worker = fork.worker;
try {
script = await (await fork.result).newScript(nativeId);
await script.run();
script = await (await fork.result).newScript(nativeId, triggerDeviceDiscover);
}
catch (e) {
worker.terminate();
Expand All @@ -98,8 +120,14 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
}

worker?.on('exit', () => {
if (this.scripts.get(nativeId)?.worker === worker)
if (this.scripts.get(nativeId)?.worker === worker) {
this.scripts.delete(nativeId);
// notify the system that the device needs to be refreshed.
if (deviceManager.getNativeIds().includes(nativeId)) {
const script = new Script(nativeId);
triggerDeviceDiscover(script.providedName, script.providedType, script.providedInterfaces);
}
}
});

this.scripts.set(nativeId, {
Expand All @@ -110,10 +138,14 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
}

async releaseDevice(id: string, nativeId: string): Promise<void> {
this.scripts.get(nativeId)?.worker?.terminate();
const worker = this.scripts.get(nativeId)?.worker;
this.scripts.delete(nativeId);
worker?.terminate();
}
}

export async function newScript(nativeId: ScryptedNativeId) {
return new Script(nativeId);
export async function newScript(nativeId: ScryptedNativeId, triggerDeviceDiscover: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
const script = new Script(nativeId, triggerDeviceDiscover);
await script.run();
return script;
}
32 changes: 12 additions & 20 deletions plugins/core/src/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { PluginAPIProxy } from "../../../server/src/plugin/plugin-api";
const { deviceManager } = sdk;

export class Script extends ScryptedDeviceBase implements Scriptable, Program, ScriptDeviceImpl {
apiProxy: PluginAPIProxy;

constructor(nativeId: string) {
constructor(nativeId: string, public triggerDeviceDiscover?: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
super(nativeId);
}

async saveScript(source: ScriptSource): Promise<void> {
this.storage.setItem('data', JSON.stringify({
'script.ts': source.script,
}));

this.triggerDeviceDiscover?.(this.providedName, this.providedType, this.providedInterfaces);
}

async loadScripts(): Promise<{ [filename: string]: ScriptSource; }> {
Expand Down Expand Up @@ -70,46 +70,38 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program, S
}

prepareScript() {
this.apiProxy?.removeListeners();

Object.assign(this, createScriptDevice([
ScryptedInterface.Scriptable,
ScryptedInterface.Program,
]));
}

async run(variables?: { [name: string]: any; }): Promise<any> {
async runInternal(script: string, variables?: { [name: string]: any; }): Promise<any> {
this.prepareScript();

try {
const data = JSON.parse(this.storage.getItem('data'));

const { value, defaultExport, apiProxy } = await scryptedEval(this, data['script.ts'], Object.assign({
const { value, defaultExport } = await scryptedEval(this, script, Object.assign({
device: this,
}, variables));

this.apiProxy = apiProxy;

await this.postRunScript(defaultExport);
return value;
}
catch (e) {
this.console.error('error loading script', e);
this.console.error('error evaluating script', e);
throw e;
}
}

async eval(source: ScriptSource, variables?: { [name: string]: any }) {
this.prepareScript();

const { value, defaultExport, apiProxy } = await scryptedEval(this, source.script, Object.assign({
device: this,
}, variables));

this.apiProxy = apiProxy;
async run(variables?: { [name: string]: any; }): Promise<any> {
const data = JSON.parse(this.storage.getItem('data'));
return this.runInternal(data['script.ts'], variables)
}

await this.postRunScript(defaultExport);
return value;
async eval(source: ScriptSource, variables?: { [name: string]: any }) {
return this.runInternal(source.script, variables);
}

// will be done at runtime
Expand Down

0 comments on commit 184f293

Please sign in to comment.