@scrypted/server 0.0.71 → 0.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @scrypted/server might be problematic. Click here for more details.
- package/.vscode/settings.json +2 -1
- package/dist/cert.js +75 -0
- package/dist/cert.js.map +1 -0
- package/dist/plugin/media.js +50 -14
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-device.js +11 -1
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js +7 -3
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-host.js +66 -45
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-npm-dependencies.js +53 -0
- package/dist/plugin/plugin-npm-dependencies.js.map +1 -0
- package/dist/plugin/plugin-remote.js +5 -5
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/plugin-volume.js +20 -0
- package/dist/plugin/plugin-volume.js.map +1 -0
- package/dist/plugin/system.js +9 -3
- package/dist/plugin/system.js.map +1 -1
- package/dist/rpc.js +32 -12
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +20 -27
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-main.js +4 -19
- package/dist/scrypted-main.js.map +1 -1
- package/package.json +4 -3
- package/python/media.py +40 -0
- package/python/plugin-remote.py +67 -30
- package/python/rpc.py +24 -81
- package/src/cert.ts +74 -0
- package/src/plugin/media.ts +53 -14
- package/src/plugin/plugin-api.ts +0 -1
- package/src/plugin/plugin-device.ts +13 -2
- package/src/plugin/plugin-host-api.ts +15 -3
- package/src/plugin/plugin-host.ts +69 -49
- package/src/plugin/plugin-npm-dependencies.ts +55 -0
- package/src/plugin/plugin-remote.ts +8 -7
- package/src/plugin/plugin-volume.ts +13 -0
- package/src/plugin/system.ts +11 -3
- package/src/rpc.ts +35 -12
- package/src/runtime.ts +24 -30
- package/src/scrypted-main.ts +5 -24
package/python/rpc.py
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
from asyncio.events import AbstractEventLoop
|
|
2
1
|
from asyncio.futures import Future
|
|
3
2
|
from typing import Callable
|
|
4
|
-
import asyncio
|
|
5
|
-
import json
|
|
6
3
|
import traceback
|
|
7
4
|
import inspect
|
|
8
|
-
import json
|
|
9
5
|
from collections.abc import Mapping, Sequence
|
|
10
6
|
import weakref
|
|
11
7
|
|
|
@@ -50,17 +46,19 @@ class RpcProxyMethod:
|
|
|
50
46
|
return self.__proxy.__apply__(self.__proxy_method_name, args)
|
|
51
47
|
|
|
52
48
|
|
|
53
|
-
class RpcProxy:
|
|
49
|
+
class RpcProxy(object):
|
|
54
50
|
def __init__(self, peer, proxyId: str, proxyConstructorName: str, proxyProps: any, proxyOneWayMethods: list[str]):
|
|
55
|
-
self.__proxy_id = proxyId
|
|
56
|
-
self.__proxy_constructor = proxyConstructorName
|
|
57
|
-
self.__proxy_peer = peer
|
|
58
|
-
self.__proxy_props = proxyProps
|
|
59
|
-
self.__proxy_oneway_methods = proxyOneWayMethods
|
|
51
|
+
self.__dict__['__proxy_id'] = proxyId
|
|
52
|
+
self.__dict__['__proxy_constructor'] = proxyConstructorName
|
|
53
|
+
self.__dict__['__proxy_peer'] = peer
|
|
54
|
+
self.__dict__['__proxy_props'] = proxyProps
|
|
55
|
+
self.__dict__['__proxy_oneway_methods'] = proxyOneWayMethods
|
|
60
56
|
|
|
61
57
|
def __getattr__(self, name):
|
|
62
|
-
if
|
|
63
|
-
return self.
|
|
58
|
+
if name in self.__dict__:
|
|
59
|
+
return self.__dict__[name]
|
|
60
|
+
if self.__dict__['__proxy_props'] and name in self.__dict__['__proxy_props']:
|
|
61
|
+
return self.__dict__['__proxy_props'][name]
|
|
64
62
|
return RpcProxyMethod(self, name)
|
|
65
63
|
|
|
66
64
|
def __call__(self, *args, **kwargs):
|
|
@@ -68,7 +66,7 @@ class RpcProxy:
|
|
|
68
66
|
pass
|
|
69
67
|
|
|
70
68
|
def __apply__(self, method: str, args: list):
|
|
71
|
-
return self.__proxy_peer.__apply__(self.__proxy_id, self.__proxy_oneway_methods, method, args)
|
|
69
|
+
return self.__dict__['__proxy_peer'].__apply__(self.__dict__['__proxy_id'], self.__dict__['__proxy_oneway_methods'], method, args)
|
|
72
70
|
|
|
73
71
|
|
|
74
72
|
class RpcPeer:
|
|
@@ -86,20 +84,20 @@ class RpcPeer:
|
|
|
86
84
|
def __init__(self, send: Callable[[object, Callable[[Exception], None]], None]) -> None:
|
|
87
85
|
self.send = send
|
|
88
86
|
|
|
89
|
-
def __apply__(self, proxyId: str, oneWayMethods: list[str], method: str,
|
|
90
|
-
|
|
91
|
-
for arg in
|
|
92
|
-
|
|
87
|
+
def __apply__(self, proxyId: str, oneWayMethods: list[str], method: str, args: list):
|
|
88
|
+
serializedArgs = []
|
|
89
|
+
for arg in args:
|
|
90
|
+
serializedArgs.append(self.serialize(arg, False))
|
|
93
91
|
|
|
94
92
|
rpcApply = {
|
|
95
93
|
'type': 'apply',
|
|
96
94
|
'id': None,
|
|
97
95
|
'proxyId': proxyId,
|
|
98
|
-
'
|
|
96
|
+
'args': serializedArgs,
|
|
99
97
|
'method': method,
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
if
|
|
100
|
+
if oneWayMethods and method in oneWayMethods:
|
|
103
101
|
rpcApply['oneway'] = True
|
|
104
102
|
self.send(rpcApply)
|
|
105
103
|
future = Future()
|
|
@@ -142,8 +140,7 @@ class RpcPeer:
|
|
|
142
140
|
}
|
|
143
141
|
return ret
|
|
144
142
|
|
|
145
|
-
serializerMapName = self.constructorSerializerMap.get(
|
|
146
|
-
type(value).__name__)
|
|
143
|
+
serializerMapName = self.constructorSerializerMap.get(type(value), None)
|
|
147
144
|
if serializerMapName:
|
|
148
145
|
__remote_constructor_name = serializerMapName
|
|
149
146
|
serializer = self.nameDeserializerMap.get(serializerMapName, None)
|
|
@@ -227,8 +224,8 @@ class RpcPeer:
|
|
|
227
224
|
|
|
228
225
|
async def handleMessage(self, message: any):
|
|
229
226
|
try:
|
|
230
|
-
|
|
231
|
-
if
|
|
227
|
+
messageType = message['type']
|
|
228
|
+
if messageType == 'param':
|
|
232
229
|
result = {
|
|
233
230
|
'type': 'result',
|
|
234
231
|
'id': message['id'],
|
|
@@ -246,7 +243,7 @@ class RpcPeer:
|
|
|
246
243
|
|
|
247
244
|
self.send(result)
|
|
248
245
|
|
|
249
|
-
elif
|
|
246
|
+
elif messageType == 'apply':
|
|
250
247
|
result = {
|
|
251
248
|
'type': 'result',
|
|
252
249
|
'id': message['id'],
|
|
@@ -261,7 +258,7 @@ class RpcPeer:
|
|
|
261
258
|
message['proxyId'])
|
|
262
259
|
|
|
263
260
|
args = []
|
|
264
|
-
for arg in (message['
|
|
261
|
+
for arg in (message['args'] or []):
|
|
265
262
|
args.append(self.deserialize(arg))
|
|
266
263
|
|
|
267
264
|
value = None
|
|
@@ -284,7 +281,7 @@ class RpcPeer:
|
|
|
284
281
|
if not message.get('oneway', False):
|
|
285
282
|
self.send(result)
|
|
286
283
|
|
|
287
|
-
elif
|
|
284
|
+
elif messageType == 'result':
|
|
288
285
|
future = self.pendingResults.get(message['id'], None)
|
|
289
286
|
if not future:
|
|
290
287
|
raise RpcResultException(
|
|
@@ -299,7 +296,7 @@ class RpcPeer:
|
|
|
299
296
|
return
|
|
300
297
|
future.set_result(self.deserialize(
|
|
301
298
|
message.get('result', None)))
|
|
302
|
-
elif
|
|
299
|
+
elif messageType == 'finalize':
|
|
303
300
|
local = self.localProxyMap.pop(
|
|
304
301
|
message['__local_proxy_id'], None)
|
|
305
302
|
self.localProxied.pop(local, None)
|
|
@@ -330,57 +327,3 @@ class RpcPeer:
|
|
|
330
327
|
}
|
|
331
328
|
self.send(paramMessage, reject)
|
|
332
329
|
return await self.createPendingResult(send)
|
|
333
|
-
|
|
334
|
-
# c = RpcPeer()
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
async def readLoop(loop, peer, reader):
|
|
338
|
-
async for line in reader:
|
|
339
|
-
try:
|
|
340
|
-
message = json.loads(line)
|
|
341
|
-
asyncio.run_coroutine_threadsafe(peer.handleMessage(message), loop)
|
|
342
|
-
except Exception as e:
|
|
343
|
-
print('read loop error', e)
|
|
344
|
-
pass
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
async def async_main(loop: AbstractEventLoop):
|
|
348
|
-
reader, writer = await asyncio.open_connection(
|
|
349
|
-
'127.0.0.1', 3033)
|
|
350
|
-
|
|
351
|
-
async def send(message, reject):
|
|
352
|
-
jsonString = json.dumps(message)
|
|
353
|
-
writer.write(bytes(jsonString + '\n', 'utf8'))
|
|
354
|
-
try:
|
|
355
|
-
await writer.drain()
|
|
356
|
-
except Exception as e:
|
|
357
|
-
if reject:
|
|
358
|
-
reject(e)
|
|
359
|
-
|
|
360
|
-
peer = RpcPeer(send)
|
|
361
|
-
peer.params['print'] = print
|
|
362
|
-
|
|
363
|
-
async def consoleTest():
|
|
364
|
-
console = await peer.getParam('console')
|
|
365
|
-
await console.log('test', 'poops', 'peddeps')
|
|
366
|
-
|
|
367
|
-
await asyncio.gather(readLoop(loop, peer, reader), consoleTest())
|
|
368
|
-
print('done')
|
|
369
|
-
|
|
370
|
-
# print("line %s" % line)
|
|
371
|
-
|
|
372
|
-
# async with aiofiles.open(0, mode='r') as f:
|
|
373
|
-
# async for line in f:
|
|
374
|
-
# print("line %s" % line)
|
|
375
|
-
# # pokemon = json.loads(contents)
|
|
376
|
-
# # print(pokemon['name'])
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
def main():
|
|
380
|
-
loop = asyncio.get_event_loop()
|
|
381
|
-
loop.run_until_complete(async_main(loop))
|
|
382
|
-
loop.close()
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if __name__ == "__main__":
|
|
386
|
-
main()
|
package/src/cert.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// import libraries
|
|
2
|
+
import forge from 'node-forge';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
|
|
5
|
+
const { pki } = forge;
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const CURRENT_SELF_SIGNED_CERTIFICATE_VERSION = 'v2';
|
|
9
|
+
|
|
10
|
+
export function createSelfSignedCertificate() {
|
|
11
|
+
|
|
12
|
+
// generate a keypair and create an X.509v3 certificate
|
|
13
|
+
const keys = pki.rsa.generateKeyPair(2048);
|
|
14
|
+
const cert = pki.createCertificate();
|
|
15
|
+
cert.publicKey = keys.publicKey;
|
|
16
|
+
|
|
17
|
+
// NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER.
|
|
18
|
+
// Conforming CAs should ensure serialNumber is:
|
|
19
|
+
// - no more than 20 octets
|
|
20
|
+
// - non-negative (prefix a '00' if your value starts with a '1' bit)
|
|
21
|
+
cert.serialNumber = '01' + crypto.randomBytes(19).toString("hex"); // 1 octet = 8 bits = 1 byte = 2 hex chars
|
|
22
|
+
cert.validity.notBefore = new Date();
|
|
23
|
+
cert.validity.notAfter = new Date();
|
|
24
|
+
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); // adding 1 year of validity from now
|
|
25
|
+
const attrs = [{
|
|
26
|
+
name: 'commonName',
|
|
27
|
+
value: 'localhost'
|
|
28
|
+
}];
|
|
29
|
+
cert.setSubject(attrs);
|
|
30
|
+
cert.setIssuer(attrs);
|
|
31
|
+
cert.setExtensions([{
|
|
32
|
+
name: 'basicConstraints',
|
|
33
|
+
cA: true
|
|
34
|
+
}, {
|
|
35
|
+
name: 'keyUsage',
|
|
36
|
+
keyCertSign: true,
|
|
37
|
+
digitalSignature: true,
|
|
38
|
+
nonRepudiation: true,
|
|
39
|
+
keyEncipherment: true,
|
|
40
|
+
dataEncipherment: true
|
|
41
|
+
}, {
|
|
42
|
+
name: 'extKeyUsage',
|
|
43
|
+
serverAuth: true,
|
|
44
|
+
clientAuth: true,
|
|
45
|
+
codeSigning: true,
|
|
46
|
+
emailProtection: true,
|
|
47
|
+
timeStamping: true
|
|
48
|
+
}, {
|
|
49
|
+
name: 'nsCertType',
|
|
50
|
+
client: true,
|
|
51
|
+
server: true,
|
|
52
|
+
email: true,
|
|
53
|
+
objsign: true,
|
|
54
|
+
sslCA: true,
|
|
55
|
+
emailCA: true,
|
|
56
|
+
objCA: true
|
|
57
|
+
}, {
|
|
58
|
+
name: 'subjectAltName',
|
|
59
|
+
altNames: [{
|
|
60
|
+
type: 7, // IP
|
|
61
|
+
ip: '127.0.0.1'
|
|
62
|
+
}]
|
|
63
|
+
}, {
|
|
64
|
+
name: 'subjectKeyIdentifier'
|
|
65
|
+
}]);
|
|
66
|
+
|
|
67
|
+
// self-sign certificate
|
|
68
|
+
cert.sign(keys.privateKey);
|
|
69
|
+
return {
|
|
70
|
+
serviceKey: pki.privateKeyToPem(keys.privateKey),
|
|
71
|
+
certificate: pki.certificateToPem(cert),
|
|
72
|
+
version: CURRENT_SELF_SIGNED_CERTIFICATE_VERSION,
|
|
73
|
+
};
|
|
74
|
+
}
|
package/src/plugin/media.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MediaStreamUrl, VideoCamera, Camera, BufferConverter, FFMpegInput, MediaManager, MediaObject, ScryptedDevice, ScryptedInterface, ScryptedMimeTypes, SystemManager, SCRYPTED_MEDIA_SCHEME } from "@scrypted/sdk/types";
|
|
1
|
+
import { ScryptedInterfaceProperty, SystemDeviceState, MediaStreamUrl, VideoCamera, Camera, BufferConverter, FFMpegInput, MediaManager, MediaObject, ScryptedDevice, ScryptedInterface, ScryptedMimeTypes, SystemManager, SCRYPTED_MEDIA_SCHEME } from "@scrypted/sdk/types";
|
|
2
2
|
import { convert, ensureBuffer } from "../convert";
|
|
3
3
|
import { MediaObjectRemote } from "./plugin-api";
|
|
4
4
|
import mimeType from 'mime'
|
|
@@ -15,14 +15,27 @@ function addBuiltins(console: Console, mediaManager: MediaManager) {
|
|
|
15
15
|
fromMimeType: ScryptedMimeTypes.Url + ';' + ScryptedMimeTypes.AcceptUrlParameter,
|
|
16
16
|
toMimeType: ScryptedMimeTypes.FFmpegInput,
|
|
17
17
|
async convert(data: string | Buffer, fromMimeType: string): Promise<Buffer | string> {
|
|
18
|
+
const url = data.toString();
|
|
18
19
|
const args: FFMpegInput = {
|
|
19
|
-
|
|
20
|
+
url,
|
|
21
|
+
inputArguments: [
|
|
22
|
+
'-i',
|
|
23
|
+
url,
|
|
24
|
+
],
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
return Buffer.from(JSON.stringify(args));
|
|
23
28
|
}
|
|
24
29
|
});
|
|
25
30
|
|
|
31
|
+
mediaManager.builtinConverters.push({
|
|
32
|
+
fromMimeType: ScryptedMimeTypes.FFmpegInput,
|
|
33
|
+
toMimeType: ScryptedMimeTypes.MediaStreamUrl,
|
|
34
|
+
async convert(data: string | Buffer, fromMimeType: string): Promise<Buffer | string> {
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
26
39
|
mediaManager.builtinConverters.push({
|
|
27
40
|
fromMimeType: ScryptedMimeTypes.MediaStreamUrl,
|
|
28
41
|
toMimeType: ScryptedMimeTypes.FFmpegInput,
|
|
@@ -52,10 +65,9 @@ function addBuiltins(console: Console, mediaManager: MediaManager) {
|
|
|
52
65
|
)
|
|
53
66
|
}
|
|
54
67
|
|
|
55
|
-
const ret: FFMpegInput = {
|
|
68
|
+
const ret: FFMpegInput = Object.assign({
|
|
56
69
|
inputArguments,
|
|
57
|
-
|
|
58
|
-
}
|
|
70
|
+
}, mediaUrl);
|
|
59
71
|
|
|
60
72
|
return Buffer.from(JSON.stringify(ret));
|
|
61
73
|
}
|
|
@@ -87,15 +99,16 @@ function addBuiltins(console: Console, mediaManager: MediaManager) {
|
|
|
87
99
|
});
|
|
88
100
|
}
|
|
89
101
|
|
|
90
|
-
export class
|
|
91
|
-
systemManager: SystemManager;
|
|
102
|
+
export abstract class MediaManagerBase implements MediaManager {
|
|
92
103
|
builtinConverters: BufferConverter[] = [];
|
|
93
104
|
|
|
94
|
-
constructor(
|
|
95
|
-
this.systemManager = systemManager;
|
|
105
|
+
constructor(public console: Console) {
|
|
96
106
|
addBuiltins(this.console, this);
|
|
97
107
|
}
|
|
98
108
|
|
|
109
|
+
abstract getSystemState(): { [id: string]: { [property: string]: SystemDeviceState } };
|
|
110
|
+
abstract getDeviceById<T>(id: string): T;
|
|
111
|
+
|
|
99
112
|
async getFFmpegPath(): Promise<string> {
|
|
100
113
|
// try to get the ffmpeg path as a value of another variable
|
|
101
114
|
// ie, in docker builds:
|
|
@@ -119,9 +132,9 @@ export class MediaManagerImpl implements MediaManager {
|
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
getConverters(): BufferConverter[] {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
.map(
|
|
135
|
+
const converters = Object.entries(this.getSystemState())
|
|
136
|
+
.filter(([id, state]) => state[ScryptedInterfaceProperty.interfaces]?.value?.includes(ScryptedInterface.BufferConverter))
|
|
137
|
+
.map(([id]) => this.getDeviceById<BufferConverter>(id));
|
|
125
138
|
converters.push(...this.builtinConverters);
|
|
126
139
|
return converters;
|
|
127
140
|
}
|
|
@@ -185,10 +198,10 @@ export class MediaManagerImpl implements MediaManager {
|
|
|
185
198
|
const path = url.pathname.split('/')[1];
|
|
186
199
|
let mo: MediaObject;
|
|
187
200
|
if (path === ScryptedInterface.VideoCamera) {
|
|
188
|
-
mo = await this.
|
|
201
|
+
mo = await this.getDeviceById<VideoCamera>(id).getVideoStream();
|
|
189
202
|
}
|
|
190
203
|
else if (path === ScryptedInterface.Camera) {
|
|
191
|
-
mo = await this.
|
|
204
|
+
mo = await this.getDeviceById<Camera>(id).takePicture() as any;
|
|
192
205
|
}
|
|
193
206
|
else {
|
|
194
207
|
throw new Error('Unrecognized Scrypted Media interface.')
|
|
@@ -197,3 +210,29 @@ export class MediaManagerImpl implements MediaManager {
|
|
|
197
210
|
return mo;
|
|
198
211
|
}
|
|
199
212
|
}
|
|
213
|
+
|
|
214
|
+
export class MediaManagerImpl extends MediaManagerBase {
|
|
215
|
+
constructor(public systemManager: SystemManager, console: Console) {
|
|
216
|
+
super(console);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getSystemState(): { [id: string]: { [property: string]: SystemDeviceState; }; } {
|
|
220
|
+
return this.systemManager.getSystemState();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getDeviceById<T>(id: string): T {
|
|
224
|
+
return this.systemManager.getDeviceById<T>(id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export class MediaManagerHostImpl extends MediaManagerBase {
|
|
229
|
+
constructor(public systemState: { [id: string]: { [property: string]: SystemDeviceState } },
|
|
230
|
+
public getDeviceById: (id: string) => any,
|
|
231
|
+
console: Console) {
|
|
232
|
+
super(console);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
getSystemState(): { [id: string]: { [property: string]: SystemDeviceState; }; } {
|
|
236
|
+
return this.systemState;
|
|
237
|
+
}
|
|
238
|
+
}
|
package/src/plugin/plugin-api.ts
CHANGED
|
@@ -136,7 +136,6 @@ export interface PluginRemoteLoadZipOptions {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
export interface PluginRemote {
|
|
139
|
-
__proxy_oneway_methods?: string[],
|
|
140
139
|
loadZip(packageJson: any, zipData: Buffer, options?: PluginRemoteLoadZipOptions): Promise<any>;
|
|
141
140
|
setSystemState(state: {[id: string]: {[property: string]: SystemDeviceState}}): Promise<void>;
|
|
142
141
|
setNativeId(nativeId: ScryptedNativeId, id: string, storage: {[key: string]: any}): Promise<void>;
|
|
@@ -2,7 +2,7 @@ import { DeviceProvider, EventDetails, EventListenerOptions, EventListenerRegist
|
|
|
2
2
|
import { ScryptedRuntime } from "../runtime";
|
|
3
3
|
import { PluginDevice } from "../db-types";
|
|
4
4
|
import { MixinProvider } from "@scrypted/sdk/types";
|
|
5
|
-
import { handleFunctionInvocations
|
|
5
|
+
import { handleFunctionInvocations } from "../rpc";
|
|
6
6
|
import { getState } from "../state";
|
|
7
7
|
import { getDisplayType } from "../infer-defaults";
|
|
8
8
|
import { allInterfaceProperties, isValidInterfaceMethod, methodInterfaces } from "./descriptor";
|
|
@@ -39,6 +39,7 @@ export class PluginDeviceProxyHandler implements ProxyHandler<any>, ScryptedDevi
|
|
|
39
39
|
})().catch(() => { });;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// this must not be async, because it potentially changes execution order.
|
|
42
43
|
ensureProxy(): Promise<PluginDevice> {
|
|
43
44
|
const pluginDevice = this.scrypted.findPluginDeviceById(this.id);
|
|
44
45
|
if (!pluginDevice)
|
|
@@ -115,7 +116,7 @@ export class PluginDeviceProxyHandler implements ProxyHandler<any>, ScryptedDevi
|
|
|
115
116
|
return mixinTable;
|
|
116
117
|
})();
|
|
117
118
|
|
|
118
|
-
return this.mixinTable.then(
|
|
119
|
+
return this.mixinTable.then(_ => pluginDevice);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
get(target: any, p: PropertyKey, receiver: any): any {
|
|
@@ -164,6 +165,16 @@ export class PluginDeviceProxyHandler implements ProxyHandler<any>, ScryptedDevi
|
|
|
164
165
|
this.scrypted.stateManager.updateDescriptor(device);
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
async probe(): Promise<boolean> {
|
|
169
|
+
try {
|
|
170
|
+
await this.ensureProxy();
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
167
178
|
async applyMixin(method: string, argArray?: any): Promise<any> {
|
|
168
179
|
const iface = methodInterfaces[method];
|
|
169
180
|
if (!iface)
|
|
@@ -6,11 +6,23 @@ import { Logger } from '../logger';
|
|
|
6
6
|
import { getState } from '../state';
|
|
7
7
|
import { PluginHost } from './plugin-host';
|
|
8
8
|
import debounce from 'lodash/debounce';
|
|
9
|
+
import { PROPERTY_PROXY_ONEWAY_METHODS } from '../rpc';
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAPI {
|
|
12
13
|
pluginId: string;
|
|
13
14
|
|
|
15
|
+
[PROPERTY_PROXY_ONEWAY_METHODS]: [
|
|
16
|
+
'onMixinEvent',
|
|
17
|
+
'onDeviceEvent',
|
|
18
|
+
'setStorage',
|
|
19
|
+
'ioSend',
|
|
20
|
+
'ioClose',
|
|
21
|
+
'setDeviceProperty',
|
|
22
|
+
'deliverPush',
|
|
23
|
+
'requestRestart',
|
|
24
|
+
];
|
|
25
|
+
|
|
14
26
|
restartDebounced = debounce(async () => {
|
|
15
27
|
const host = this.scrypted.plugins[this.pluginId];
|
|
16
28
|
const logger = await this.getLogger(undefined);
|
|
@@ -23,7 +35,7 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
|
|
|
23
35
|
this.scrypted.runPlugin(plugin);
|
|
24
36
|
}, 15000);
|
|
25
37
|
|
|
26
|
-
constructor(public scrypted: ScryptedRuntime, plugin: Plugin, public pluginHost: PluginHost) {
|
|
38
|
+
constructor(public scrypted: ScryptedRuntime, plugin: Plugin, public pluginHost: PluginHost, public mediaManager: MediaManager) {
|
|
27
39
|
super();
|
|
28
40
|
this.pluginId = plugin._id;
|
|
29
41
|
}
|
|
@@ -40,8 +52,8 @@ export class PluginHostAPI extends PluginAPIManagedListeners implements PluginAP
|
|
|
40
52
|
this.scrypted.stateManager.notifyInterfaceEvent(device, eventInterface, eventData);
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
getMediaManager(): Promise<MediaManager> {
|
|
44
|
-
return
|
|
55
|
+
async getMediaManager(): Promise<MediaManager> {
|
|
56
|
+
return this.mediaManager;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
async deliverPush(endpoint: string, httpRequest: HttpRequest) {
|