import type { Device, DeviceManifest, EventDetails, EventListenerOptions, EventListenerRegister, MediaManager, MediaObject, ScryptedDevice, ScryptedInterfaceDescriptor, ScryptedInterfaceProperty, ScryptedNativeId, SystemDeviceState } from '@scrypted/types'; import type { AccessControls } from './acl'; export interface PluginLogger { log(level: string, message: string): Promise; clear(): Promise; clearAlert(message: string): Promise; clearAlerts(): Promise; } export interface PluginHostInfo { serverVersion: string; } export interface PluginAPI { setState(nativeId: ScryptedNativeId, key: string, value: any): Promise; onDevicesChanged(deviceManifest: DeviceManifest): Promise; onDeviceDiscovered(device: Device): Promise; onDeviceEvent(nativeId: ScryptedNativeId, eventInterface: string, eventData?: any): Promise; onMixinEvent(id: string, nativeId: ScryptedNativeId, eventInterface: string, eventData?: any): Promise; onDeviceRemoved(nativeId: string): Promise; setStorage(nativeId: string, storage: { [key: string]: any }): Promise; getDeviceById(id: string): Promise; setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise; removeDevice(id: string): Promise; listen(EventListener: (id: string, eventDetails: EventDetails, eventData: any) => void): Promise; listenDevice(id: string, event: string | EventListenerOptions, callback: (eventDetails: EventDetails, eventData: any) => void): Promise; getLogger(nativeId: ScryptedNativeId): Promise; getComponent(id: string): Promise; getMediaManager(): Promise; requestRestart(): Promise; setScryptedInterfaceDescriptors(typesVersion: string, descriptors: { [scryptedInterface: string]: ScryptedInterfaceDescriptor }): Promise; } class EventListenerRegisterProxy implements EventListenerRegister { __proxy_oneway_methods = [ 'removeListener', ]; removeListener() { this.listeners.delete(this); this.listener.removeListener(); } constructor(public listener: EventListenerRegister, public listeners: Set) { this.listeners.add(this); } } export class PluginAPIManagedListeners { listeners = new Set(); manageListener(listener: EventListenerRegister): EventListenerRegister { return new EventListenerRegisterProxy(listener, this.listeners); } removeListeners() { for (const l of [...this.listeners]) { l.removeListener(); } this.listeners.clear(); } } export class PluginAPIProxy extends PluginAPIManagedListeners implements PluginAPI { acl: AccessControls; constructor(public api: PluginAPI, public mediaManager?: MediaManager) { super(); } setScryptedInterfaceDescriptors(typesVersion: string, descriptors: { [scryptedInterface: string]: ScryptedInterfaceDescriptor }): Promise { this.acl?.deny(); return this.api.setScryptedInterfaceDescriptors(typesVersion, descriptors); } setState(nativeId: ScryptedNativeId, key: string, value: any): Promise { this.acl?.deny(); return this.api.setState(nativeId, key, value); } onDevicesChanged(deviceManifest: DeviceManifest): Promise { this.acl?.deny(); return this.api.onDevicesChanged(deviceManifest); } onDeviceDiscovered(device: Device): Promise { this.acl?.deny(); return this.api.onDeviceDiscovered(device); } onDeviceEvent(nativeId: ScryptedNativeId, eventInterface: any, eventData?: any): Promise { this.acl?.deny(); return this.api.onDeviceEvent(nativeId, eventInterface, eventData); } onMixinEvent(id: string, nativeId: ScryptedNativeId, eventInterface: string, eventData?: any): Promise { this.acl?.deny(); return this.api.onMixinEvent(id, nativeId, eventInterface, eventData); } onDeviceRemoved(nativeId: string): Promise { this.acl?.deny(); return this.api.onDeviceRemoved(nativeId); } setStorage(nativeId: ScryptedNativeId, storage: { [key: string]: any; }): Promise { this.acl?.deny(); return this.api.setStorage(nativeId, storage); } getDeviceById(id: string): Promise { if (this.acl?.shouldRejectDevice(id)) return; return this.api.getDeviceById(id); } setDeviceProperty(id: string, property: ScryptedInterfaceProperty, value: any): Promise { this.acl?.deny(); return this.api.setDeviceProperty(id, property, value); } removeDevice(id: string): Promise { this.acl?.deny(); return this.api.removeDevice(id); } async listen(callback: (id: string, eventDetails: EventDetails, eventData: any) => void): Promise { if (!this.acl) return this.manageListener(await this.api.listen(callback)); return this.manageListener(await this.api.listen((id, details, data) => { if (!this.acl.shouldRejectEvent(id, details)) callback(id, details, data); })); } async listenDevice(id: string, event: string | EventListenerOptions, callback: (eventDetails: EventDetails, eventData: any) => void): Promise { if (!this.acl) return this.manageListener(await this.api.listenDevice(id, event, callback)); return this.manageListener(await this.api.listenDevice(id, event, (details, data) => { if (!this.acl.shouldRejectEvent(id, details)) callback(details, data); })); } getLogger(nativeId: ScryptedNativeId): Promise { this.acl?.deny(); return this.api.getLogger(nativeId); } getComponent(id: string): Promise { this.acl?.deny(); return this.api.getComponent(id); } async getMediaManager(): Promise { return this.mediaManager; } async requestRestart() { this.acl?.deny(); return this.api.requestRestart(); } } export interface PluginRemoteLoadZipOptions { debug?: boolean; zipHash: string; fork?: boolean; main?: string; clusterId: string; clusterSecret: string; } export class PluginZipAPI { constructor( public getZip: () => Promise ) { } } export interface PluginRemote { loadZip(packageJson: any, zipAPI: PluginZipAPI, options: PluginRemoteLoadZipOptions): Promise; setSystemState(state: { [id: string]: { [property: string]: SystemDeviceState } }): Promise; setNativeId(nativeId: ScryptedNativeId, id: string, storage: { [key: string]: any }): Promise; updateDeviceState(id: string, state: { [property: string]: SystemDeviceState }): Promise; /** * @deprecated */ notify(id: string, eventTime: number, eventInterface: string, property: string | undefined, value: SystemDeviceState | any, changed?: boolean): Promise; notify(id: string, eventDetails: EventDetails, eventData: SystemDeviceState | any): Promise; ioEvent(id: string, event: string, message?: any): Promise; createDeviceState(id: string, setState: (property: string, value: any) => Promise): Promise; getServicePort(name: string, ...args: any[]): Promise<[number, string]>; } export interface MediaObjectRemote extends MediaObject { getData(): Promise; }