import { EventListenerOptions , EventDetails , EventListenerRegister , ScryptedDevice , ScryptedInterface , ScryptedInterfaceDescriptors , SystemDeviceState , SystemManager , ScryptedInterfaceProperty , ScryptedDeviceType , Logger } from "@scrypted/sdk/types" ; import { PluginAPI } from "./plugin-api" ; import { handleFunctionInvocations , PrimitiveProxyHandler , PROPERTY_PROXY_ONEWAY_METHODS } from '../rpc' ; import { EventRegistry } from "../event-registry" ; import { allInterfaceProperties , isValidInterfaceMethod } from "./descriptor" ; function newDeviceProxy ( id , systemManager ) { const handler = new DeviceProxyHandler ( id , systemManager ) ; return new Proxy ( handler , handler ) ; } class DeviceProxyHandler implements PrimitiveProxyHandler < any > , ScryptedDevice { device : Promise < ScryptedDevice > ; constructor ( public id , public systemManager ) { } toPrimitive ( ) { return `ScryptedDevice-${ this . id }` ; } get ( target , p , receiver ) { if ( p === 'id' ) return this . id ; const handled = handleFunctionInvocations ( this , target , p , receiver ) ; if ( handled ) return handled ; if ( allInterfaceProperties . includes ( p . toString ( ) ) ) return ( this . systemManager . state [ this . id ] as any ) ?. [ p ] ?. value ; const prop = p . toString ( ) ; if ( ! isValidInterfaceMethod ( this . systemManager . state [ this . id ] . interfaces . value , prop ) ) return ; if ( ScryptedInterfaceDescriptors [ ScryptedInterface . ScryptedDevice ] . methods . includes ( prop ) ) return ( this as any ) [ p ] . bind ( this ) ; return new Proxy ( ( ) => p , this ) ; } ensureDevice ( ) { if ( ! this . device ) this . device = this . systemManager . api . getDeviceById ( this . id ) ; return this . device ; } async apply ( target , thisArg , argArray ? ) { const method = target ( ) ; const device = await this . ensureDevice ( ) ; if ( false && method === 'refresh' ) { const name = this . systemManager . state [ this . id ] ?. [ ScryptedInterfaceProperty . name ] . value ; this . systemManager . log . i ( `requested refresh ${ name }` ) ; } return ( device as any ) [ method ] ( ... argArray ) ; } listen ( event , callback ) { return this . systemManager . listenDevice ( this . id , event , callback ) ; } async setName ( name ) { return this . systemManager . api . setDeviceProperty ( this . id , ScryptedInterfaceProperty . name , name ) ; } async setRoom ( room ) { return this . systemManager . api . setDeviceProperty ( this . id , ScryptedInterfaceProperty . room , room ) ; } async setType ( type ) { return this . systemManager . api . setDeviceProperty ( this . id , ScryptedInterfaceProperty . type , type ) ; } async probe ( ) { return this . apply ( ( ) => 'probe' , undefined , [ ] ) ; } } class EventListenerRegisterImpl implements EventListenerRegister { promise : Promise < EventListenerRegister > ; constructor ( promise ) { this . promise = promise ; } async removeListener ( ) { try { const register = await this . promise ; this . promise = undefined ; register ?. removeListener ( ) ; } catch ( e ) { console . error ( 'removeListener' , e ) ; } } } function makeOneWayCallback < T > ( input ) { const f = input ; const oneways = f [ PROPERTY_PROXY_ONEWAY_METHODS ] || [ ] ; if ( ! oneways . includes ( null ) ) oneways . push ( null ) ; f [ PROPERTY_PROXY_ONEWAY_METHODS ] = oneways ; return input ; } export class SystemManagerImpl implements SystemManager { api : PluginAPI ; state : { [ id ] : { [ property ] : SystemDeviceState ; } ; } ; deviceProxies : { [ id ] : ScryptedDevice ; } = { } ; log : Logger ; events = new EventRegistry ( ) ; getDeviceState ( id ) { return this . state [ id ] ; } getSystemState ( ) { return this . state ; } getDeviceById ( id ) { if ( ! this . state [ id ] ) return ; let proxy = this . deviceProxies [ id ] ; if ( ! proxy ) proxy = this . deviceProxies [ id ] = newDeviceProxy ( id , this ) ; return proxy ; } getDeviceByName ( name ) { for ( const id of Object . keys ( this . state ) ) { const s = this . state [ id ] ; if ( s . name . value === name ) return this . getDeviceById ( id ) ; } } listen ( callback ) { return this . events . listen ( makeOneWayCallback ( ( id , eventDetails , eventData ) => callback ( this . getDeviceById ( id ) , eventDetails , eventData ) ) ) ; } listenDevice ( id , options , callback ) { let { event , watch } = ( options || { } ) as EventListenerOptions ; if ( ! event && typeof options === 'string' ) event = options as string ; if ( ! event ) event = undefined ; if ( watch ) return this . events . listenDevice ( id , event , ( eventDetails , eventData ) => callback ( this . getDeviceById ( id ) , eventDetails , eventData ) ) ; return new EventListenerRegisterImpl ( this . api . listenDevice ( id , options , makeOneWayCallback ( ( eventDetails , eventData ) => callback ( this . getDeviceById ( id ) , eventDetails , eventData ) ) ) ) ; } async removeDevice ( id ) { return this . api . removeDevice ( id ) ; } getComponent ( id ) { return this . api . getComponent ( id ) ; } }