@scrypted/server 0.1.12 → 0.1.15
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/dist/plugin/descriptor.js +23 -18
- package/dist/plugin/descriptor.js.map +1 -1
- package/dist/plugin/media.js +2 -2
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-api.js +3 -0
- package/dist/plugin/plugin-api.js.map +1 -1
- package/dist/plugin/plugin-device.js +24 -14
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host-api.js +13 -4
- package/dist/plugin/plugin-host-api.js.map +1 -1
- package/dist/plugin/plugin-npm-dependencies.js +5 -1
- package/dist/plugin/plugin-npm-dependencies.js.map +1 -1
- package/dist/plugin/plugin-remote.js.map +1 -1
- package/dist/plugin/plugin-volume.js +2 -1
- package/dist/plugin/plugin-volume.js.map +1 -1
- package/dist/plugin/runtime/python-worker.js +2 -1
- package/dist/plugin/runtime/python-worker.js.map +1 -1
- package/dist/plugin/system.js +40 -4
- package/dist/plugin/system.js.map +1 -1
- package/dist/rpc-serializer.js +23 -10
- package/dist/rpc-serializer.js.map +1 -1
- package/dist/rpc.js +11 -0
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +23 -23
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-server-main.js +1 -0
- package/dist/scrypted-server-main.js.map +1 -1
- package/dist/state.js +10 -19
- package/dist/state.js.map +1 -1
- package/package.json +4 -3
- package/python/plugin-remote.py +28 -19
- package/src/plugin/descriptor.ts +24 -18
- package/src/plugin/media.ts +2 -2
- package/src/plugin/plugin-api.ts +7 -1
- package/src/plugin/plugin-device.ts +31 -23
- package/src/plugin/plugin-host-api.ts +16 -7
- package/src/plugin/plugin-npm-dependencies.ts +5 -1
- package/src/plugin/plugin-remote.ts +0 -1
- package/src/plugin/plugin-volume.ts +2 -1
- package/src/plugin/runtime/python-worker.ts +3 -1
- package/src/plugin/system.ts +51 -13
- package/src/rpc-serializer.ts +30 -13
- package/src/rpc.ts +11 -0
- package/src/runtime.ts +28 -29
- package/src/scrypted-server-main.ts +1 -0
- package/src/state.ts +15 -26
package/src/plugin/system.ts
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
-
import {
|
2
|
-
import { PluginAPI } from "./plugin-api";
|
3
|
-
import { PrimitiveProxyHandler, RpcPeer } from '../rpc';
|
1
|
+
import { EventListener, EventListenerOptions, EventListenerRegister, Logger, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceDescriptor, ScryptedInterfaceDescriptors, ScryptedInterfaceProperty, SystemDeviceState, SystemManager } from "@scrypted/types";
|
4
2
|
import { EventRegistry } from "../event-registry";
|
5
|
-
import {
|
6
|
-
|
3
|
+
import { PrimitiveProxyHandler, RpcPeer } from '../rpc';
|
4
|
+
import { getInterfaceMethods, getInterfaceProperties, getPropertyInterfaces, isValidInterfaceMethod, propertyInterfaces } from "./descriptor";
|
5
|
+
import { PluginAPI } from "./plugin-api";
|
7
6
|
|
8
7
|
function newDeviceProxy(id: string, systemManager: SystemManagerImpl) {
|
9
8
|
const handler = new DeviceProxyHandler(id, systemManager);
|
10
9
|
return new Proxy(handler, handler);
|
11
10
|
}
|
12
11
|
|
13
|
-
|
14
12
|
class DeviceProxyHandler implements PrimitiveProxyHandler<any>, ScryptedDevice {
|
15
13
|
device: Promise<ScryptedDevice>;
|
16
14
|
constructor(public id: string, public systemManager: SystemManagerImpl) {
|
@@ -20,6 +18,31 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any>, ScryptedDevice {
|
|
20
18
|
return `ScryptedDevice-${this.id}`
|
21
19
|
}
|
22
20
|
|
21
|
+
ownKeys(target: any): ArrayLike<string | symbol> {
|
22
|
+
const interfaces = new Set<string>(this.systemManager.state[this.id].interfaces.value);
|
23
|
+
const methods = getInterfaceMethods(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces);
|
24
|
+
const properties = getInterfaceProperties(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces);
|
25
|
+
return [...methods, ...properties];
|
26
|
+
}
|
27
|
+
|
28
|
+
getOwnPropertyDescriptor(target: any, p: string | symbol): PropertyDescriptor {
|
29
|
+
const interfaces = new Set<string>(this.systemManager.state[this.id].interfaces.value);
|
30
|
+
const methods = getInterfaceMethods(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces);
|
31
|
+
const prop = p.toString();
|
32
|
+
if (methods.includes(prop)) {
|
33
|
+
return {
|
34
|
+
configurable: true,
|
35
|
+
};
|
36
|
+
}
|
37
|
+
const properties = getInterfaceProperties(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces);
|
38
|
+
if (properties.includes(prop)) {
|
39
|
+
return {
|
40
|
+
configurable: true,
|
41
|
+
value: this.systemManager.state[this.id][prop]?.value
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
23
46
|
get(target: any, p: PropertyKey, receiver: any): any {
|
24
47
|
if (p === 'id')
|
25
48
|
return this.id;
|
@@ -28,11 +51,16 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any>, ScryptedDevice {
|
|
28
51
|
if (handled)
|
29
52
|
return handled;
|
30
53
|
|
31
|
-
|
54
|
+
const interfaces = new Set<string>(this.systemManager.state[this.id].interfaces.value);
|
55
|
+
const prop = p.toString();
|
56
|
+
const isValidProperty = this.systemManager.propertyInterfaces?.[prop] || propertyInterfaces[prop];
|
57
|
+
|
58
|
+
// this will also return old properties that should not exist on a device. ie, a disabled mixin provider.
|
59
|
+
// should this change?
|
60
|
+
if (isValidProperty)
|
32
61
|
return (this.systemManager.state[this.id] as any)?.[p]?.value;
|
33
62
|
|
34
|
-
|
35
|
-
if (!isValidInterfaceMethod(this.systemManager.state[this.id].interfaces.value, prop))
|
63
|
+
if (!isValidInterfaceMethod(this.systemManager.descriptors || ScryptedInterfaceDescriptors, interfaces, prop))
|
36
64
|
return;
|
37
65
|
|
38
66
|
if (ScryptedInterfaceDescriptors[ScryptedInterface.ScryptedDevice].methods.includes(prop))
|
@@ -41,7 +69,7 @@ class DeviceProxyHandler implements PrimitiveProxyHandler<any>, ScryptedDevice {
|
|
41
69
|
return new Proxy(() => p, this);
|
42
70
|
}
|
43
71
|
|
44
|
-
|
72
|
+
ensureDevice() {
|
45
73
|
if (!this.device)
|
46
74
|
this.device = this.systemManager.api.getDeviceById(this.id);
|
47
75
|
return this.device;
|
@@ -101,15 +129,18 @@ function makeOneWayCallback<T>(input: T): T {
|
|
101
129
|
|
102
130
|
export class SystemManagerImpl implements SystemManager {
|
103
131
|
api: PluginAPI;
|
104
|
-
state: {[id: string]: {[property: string]: SystemDeviceState}};
|
105
|
-
deviceProxies: {[id: string]: ScryptedDevice} = {};
|
132
|
+
state: { [id: string]: { [property: string]: SystemDeviceState } };
|
133
|
+
deviceProxies: { [id: string]: ScryptedDevice } = {};
|
106
134
|
log: Logger;
|
107
135
|
events = new EventRegistry();
|
136
|
+
typesVersion: string;
|
137
|
+
descriptors: { [scryptedInterface: string]: ScryptedInterfaceDescriptor };
|
138
|
+
propertyInterfaces: ReturnType<typeof getPropertyInterfaces>;
|
108
139
|
|
109
140
|
getDeviceState(id: string) {
|
110
141
|
return this.state[id];
|
111
142
|
}
|
112
|
-
getSystemState(): {[id: string]: {[property: string]: SystemDeviceState}} {
|
143
|
+
getSystemState(): { [id: string]: { [property: string]: SystemDeviceState } } {
|
113
144
|
return this.state;
|
114
145
|
}
|
115
146
|
|
@@ -155,4 +186,11 @@ export class SystemManagerImpl implements SystemManager {
|
|
155
186
|
getComponent(id: string): Promise<any> {
|
156
187
|
return this.api.getComponent(id);
|
157
188
|
}
|
189
|
+
|
190
|
+
setScryptedInterfaceDescriptors(typesVersion: string, descriptors: { [scryptedInterface: string]: ScryptedInterfaceDescriptor }): Promise<void> {
|
191
|
+
this.typesVersion = typesVersion;
|
192
|
+
this.descriptors = descriptors;
|
193
|
+
this.propertyInterfaces = getPropertyInterfaces(descriptors);
|
194
|
+
return this.api.setScryptedInterfaceDescriptors(typesVersion, descriptors);
|
195
|
+
}
|
158
196
|
}
|
package/src/rpc-serializer.ts
CHANGED
@@ -3,7 +3,7 @@ import { SidebandBufferSerializer } from "./plugin/buffer-serializer";
|
|
3
3
|
import { RpcPeer } from "./rpc";
|
4
4
|
|
5
5
|
export function createDuplexRpcPeer(selfName: string, peerName: string, readable: Readable, writable: Writable) {
|
6
|
-
const serializer = createRpcDuplexSerializer(
|
6
|
+
const serializer = createRpcDuplexSerializer(writable);
|
7
7
|
|
8
8
|
const rpcPeer = new RpcPeer(selfName, peerName, (message, reject, serializationContext) => {
|
9
9
|
try {
|
@@ -16,6 +16,7 @@ export function createDuplexRpcPeer(selfName: string, peerName: string, readable
|
|
16
16
|
});
|
17
17
|
|
18
18
|
serializer.setupRpcPeer(rpcPeer);
|
19
|
+
readable.on('data', data => serializer.onData(data));
|
19
20
|
readable.on('close', serializer.onDisconnected);
|
20
21
|
readable.on('error', serializer.onDisconnected);
|
21
22
|
return rpcPeer;
|
@@ -34,7 +35,7 @@ export function createRpcSerializer(options: {
|
|
34
35
|
rpcPeer.kill('connection closed.');
|
35
36
|
}
|
36
37
|
|
37
|
-
const sendMessage = (message: any, reject: (e: Error) => void, serializationContext: any,
|
38
|
+
const sendMessage = (message: any, reject: (e: Error) => void, serializationContext: any,) => {
|
38
39
|
if (!connected) {
|
39
40
|
reject?.(new Error('peer disconnected'));
|
40
41
|
return;
|
@@ -78,7 +79,9 @@ export function createRpcSerializer(options: {
|
|
78
79
|
};
|
79
80
|
}
|
80
81
|
|
81
|
-
export function createRpcDuplexSerializer(
|
82
|
+
export function createRpcDuplexSerializer(writable: {
|
83
|
+
write: (data: Buffer) => void;
|
84
|
+
}) {
|
82
85
|
const socketSend = (type: number, data: Buffer) => {
|
83
86
|
const header = Buffer.alloc(5);
|
84
87
|
header.writeUInt32BE(data.length + 1, 0);
|
@@ -102,38 +105,52 @@ export function createRpcDuplexSerializer(readable: Readable, writable: Writable
|
|
102
105
|
});
|
103
106
|
|
104
107
|
let header: Buffer;
|
105
|
-
|
108
|
+
let pending: Buffer;
|
109
|
+
|
110
|
+
const readPending = (length: number) => {
|
111
|
+
if (!pending || pending.length < length)
|
112
|
+
return;
|
113
|
+
|
114
|
+
const ret = pending.slice(0, length);
|
115
|
+
pending = pending.slice(length);
|
116
|
+
if (!pending.length)
|
117
|
+
pending = undefined;
|
118
|
+
return ret;
|
119
|
+
}
|
120
|
+
|
121
|
+
const onData = (data: Buffer) => {
|
122
|
+
if (!pending)
|
123
|
+
pending = data;
|
124
|
+
else
|
125
|
+
pending = Buffer.concat([pending, data]);
|
126
|
+
|
106
127
|
while (true) {
|
107
128
|
if (!header) {
|
108
|
-
header =
|
129
|
+
header = readPending(5);
|
109
130
|
if (!header)
|
110
131
|
return;
|
111
132
|
}
|
112
133
|
|
113
134
|
const length = header.readUInt32BE(0);
|
114
135
|
const type = header.readUInt8(4);
|
115
|
-
const payload: Buffer =
|
136
|
+
const payload: Buffer = readPending(length - 1);
|
116
137
|
if (!payload)
|
117
138
|
return;
|
118
139
|
|
119
140
|
header = undefined;
|
120
141
|
|
121
|
-
const data = payload;
|
122
|
-
|
123
142
|
if (type === 0) {
|
124
|
-
const message = JSON.parse(
|
143
|
+
const message = JSON.parse(payload.toString());
|
125
144
|
serializer.onMessageFinish(message);
|
126
145
|
}
|
127
146
|
else {
|
128
|
-
serializer.onMessageBuffer(
|
147
|
+
serializer.onMessageBuffer(payload);
|
129
148
|
}
|
130
149
|
}
|
131
150
|
}
|
132
151
|
|
133
|
-
readable.on('readable', readMessages);
|
134
|
-
readMessages();
|
135
|
-
|
136
152
|
return {
|
153
|
+
onData,
|
137
154
|
setupRpcPeer: serializer.setupRpcPeer,
|
138
155
|
sendMessage: serializer.sendMessage,
|
139
156
|
onDisconnected: serializer.onDisconnected,
|
package/src/rpc.ts
CHANGED
@@ -246,6 +246,17 @@ export class RpcPeer {
|
|
246
246
|
static readonly PROPERTY_JSON_DISABLE_SERIALIZATION = '__json_disable_serialization';
|
247
247
|
static readonly PROPERTY_PROXY_PROPERTIES = '__proxy_props';
|
248
248
|
static readonly PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children';
|
249
|
+
static readonly PROBED_PROPERTIES = new Set<any>([
|
250
|
+
'then',
|
251
|
+
'constructor',
|
252
|
+
'__proxy_id',
|
253
|
+
'__proxy_constructor',
|
254
|
+
'__proxy_peer',
|
255
|
+
RpcPeer.PROPERTY_PROXY_ONEWAY_METHODS,
|
256
|
+
RpcPeer.PROPERTY_JSON_DISABLE_SERIALIZATION,
|
257
|
+
RpcPeer.PROPERTY_PROXY_PROPERTIES,
|
258
|
+
RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN,
|
259
|
+
]);
|
249
260
|
|
250
261
|
constructor(public selfName: string, public peerName: string, public send: (message: RpcMessage, reject?: (e: Error) => void, serializationContext?: any) => void) {
|
251
262
|
}
|
package/src/runtime.ts
CHANGED
@@ -1,39 +1,38 @@
|
|
1
|
-
import {
|
2
|
-
import
|
3
|
-
import { ScryptedNativeId, Device, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty, DeviceInformation } from '@scrypted/types';
|
4
|
-
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
5
|
-
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
6
|
-
import { getState, ScryptedStateManager, setState } from './state';
|
7
|
-
import { Request, Response } from 'express';
|
8
|
-
import { createResponseInterface } from './http-interfaces';
|
9
|
-
import http, { ServerResponse, IncomingHttpHeaders } from 'http';
|
10
|
-
import https from 'https';
|
11
|
-
import express from 'express';
|
12
|
-
import { LogEntry, Logger, makeAlertId } from './logger';
|
13
|
-
import { getDisplayName, getDisplayRoom, getDisplayType, getProvidedNameOrDefault, getProvidedRoomOrDefault, getProvidedTypeOrDefault } from './infer-defaults';
|
14
|
-
import { URL } from "url";
|
15
|
-
import qs from "query-string";
|
16
|
-
import { PluginComponent } from './services/plugin';
|
17
|
-
import WebSocket, { Server as WebSocketServer } from "ws";
|
1
|
+
import { Device, DeviceInformation, EngineIOHandler, HttpRequest, HttpRequestHandler, OauthClient, PushHandler, ScryptedDevice, ScryptedInterface, ScryptedInterfaceProperty, ScryptedNativeId } from '@scrypted/types';
|
2
|
+
import AdmZip from 'adm-zip';
|
18
3
|
import axios from 'axios';
|
19
|
-
import
|
4
|
+
import * as io from 'engine.io';
|
20
5
|
import { once } from 'events';
|
6
|
+
import express, { Request, Response } from 'express';
|
7
|
+
import http, { ServerResponse } from 'http';
|
8
|
+
import https from 'https';
|
9
|
+
import { spawn as ptySpawn } from 'node-pty-prebuilt-multiarch';
|
10
|
+
import path from 'path';
|
11
|
+
import qs from "query-string";
|
12
|
+
import rimraf from 'rimraf';
|
13
|
+
import semver from 'semver';
|
21
14
|
import { PassThrough } from 'stream';
|
15
|
+
import tar from 'tar';
|
16
|
+
import { URL } from "url";
|
17
|
+
import WebSocket, { Server as WebSocketServer } from "ws";
|
18
|
+
import { Plugin, PluginDevice, ScryptedAlert } from './db-types';
|
19
|
+
import { createResponseInterface } from './http-interfaces';
|
20
|
+
import { getDisplayName, getDisplayRoom, getDisplayType, getProvidedNameOrDefault, getProvidedRoomOrDefault, getProvidedTypeOrDefault } from './infer-defaults';
|
21
|
+
import { IOServer } from './io';
|
22
|
+
import { Level } from './level';
|
23
|
+
import { LogEntry, Logger, makeAlertId } from './logger';
|
22
24
|
import { PluginDebug } from './plugin/plugin-debug';
|
25
|
+
import { PluginDeviceProxyHandler } from './plugin/plugin-device';
|
26
|
+
import { PluginHost } from './plugin/plugin-host';
|
27
|
+
import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http';
|
28
|
+
import { getPluginVolume } from './plugin/plugin-volume';
|
23
29
|
import { getIpAddress, SCRYPTED_INSECURE_PORT, SCRYPTED_SECURE_PORT } from './server-settings';
|
24
|
-
import semver from 'semver';
|
25
|
-
import { ServiceControl } from './services/service-control';
|
26
30
|
import { Alerts } from './services/alerts';
|
27
|
-
import { Info } from './services/info';
|
28
|
-
import * as io from 'engine.io';
|
29
|
-
import { spawn as ptySpawn } from 'node-pty';
|
30
|
-
import rimraf from 'rimraf';
|
31
|
-
import { getPluginVolume } from './plugin/plugin-volume';
|
32
|
-
import { isConnectionUpgrade, PluginHttp } from './plugin/plugin-http';
|
33
|
-
import AdmZip from 'adm-zip';
|
34
|
-
import path from 'path';
|
35
31
|
import { CORSControl, CORSServer } from './services/cors';
|
36
|
-
import {
|
32
|
+
import { Info } from './services/info';
|
33
|
+
import { PluginComponent } from './services/plugin';
|
34
|
+
import { ServiceControl } from './services/service-control';
|
35
|
+
import { getState, ScryptedStateManager, setState } from './state';
|
37
36
|
|
38
37
|
interface DeviceProxyPair {
|
39
38
|
handler: PluginDeviceProxyHandler;
|
@@ -225,6 +225,7 @@ async function start() {
|
|
225
225
|
});
|
226
226
|
|
227
227
|
console.log('#######################################################');
|
228
|
+
console.log(`Scrypted Volume : ${volumeDir}`);
|
228
229
|
console.log(`Scrypted Server (Local) : https://localhost:${SCRYPTED_SECURE_PORT}/`);
|
229
230
|
for (const address of getHostAddresses(true, true)) {
|
230
231
|
console.log(`Scrypted Server (Remote) : https://${address}:${SCRYPTED_SECURE_PORT}/`);
|
package/src/state.ts
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
import {
|
2
|
-
import { ScryptedNativeId, EventDetails, EventListenerOptions, EventListenerRegister, Refresh, ScryptedInterface, ScryptedInterfaceProperty, SystemDeviceState } from "@scrypted/types";
|
3
|
-
import { RefreshSymbol } from "./plugin/plugin-device";
|
1
|
+
import { EventDetails, EventListenerOptions, EventListenerRegister, Refresh, ScryptedInterface, ScryptedInterfaceProperty, ScryptedNativeId, SystemDeviceState } from "@scrypted/types";
|
4
2
|
import throttle from 'lodash/throttle';
|
5
|
-
import { sleep } from "./sleep";
|
6
|
-
import { EventListenerRegisterImpl, EventRegistry } from "./event-registry";
|
7
3
|
import { PluginDevice } from "./db-types";
|
4
|
+
import { EventListenerRegisterImpl, EventRegistry } from "./event-registry";
|
8
5
|
import { allInterfaceProperties, propertyInterfaces } from "./plugin/descriptor";
|
6
|
+
import { RefreshSymbol } from "./plugin/plugin-device";
|
7
|
+
import { ScryptedRuntime } from "./runtime";
|
8
|
+
import { sleep } from "./sleep";
|
9
9
|
|
10
10
|
export class ScryptedStateManager extends EventRegistry {
|
11
11
|
scrypted: ScryptedRuntime;
|
@@ -34,41 +34,30 @@ export class ScryptedStateManager extends EventRegistry {
|
|
34
34
|
this.scrypted = scrypted;
|
35
35
|
}
|
36
36
|
|
37
|
-
setPluginState(pluginId: string, nativeId: ScryptedNativeId, property: string, value: any) {
|
37
|
+
setPluginState(pluginId: string, nativeId: ScryptedNativeId, eventInterface: ScryptedInterface, property: string, value: any) {
|
38
38
|
const device = this.scrypted.findPluginDevice(pluginId, nativeId);
|
39
39
|
if (!device)
|
40
40
|
throw new Error(`device not found for plugin id ${pluginId} native id ${nativeId}`);
|
41
|
-
this.setPluginDeviceState(device, property, value);
|
41
|
+
this.setPluginDeviceState(device, property, value, eventInterface);
|
42
42
|
}
|
43
43
|
|
44
|
-
setPluginDeviceState(device: PluginDevice, property: string, value: any) {
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
// this currently doesn't work because inherited properties are not detected.
|
49
|
-
// ie, MediaPlayer implements StartStop and Pause
|
50
|
-
// if (!isValidInterfaceProperty(device.state.interfaces.value, property))
|
51
|
-
// throw new Error(`interface for property ${property} not implemented`);
|
52
|
-
if (!allInterfaceProperties.includes(property))
|
44
|
+
setPluginDeviceState(device: PluginDevice, property: string, value: any, eventInterface?: ScryptedInterface) {
|
45
|
+
eventInterface = eventInterface || propertyInterfaces[property];
|
46
|
+
if (!eventInterface)
|
53
47
|
throw new Error(`${property} is not a valid property`);
|
54
48
|
|
55
49
|
const changed = setState(device, property, value);
|
56
50
|
|
57
|
-
this.notifyPropertyEvent(device, property, value, changed);
|
58
|
-
|
59
|
-
this.upserts.add(device._id);
|
60
|
-
this.upsertThrottle();
|
61
|
-
|
62
|
-
return changed;
|
63
|
-
}
|
64
|
-
|
65
|
-
notifyPropertyEvent(device: PluginDevice, property: string, value: any, changed?: boolean) {
|
66
51
|
const eventTime = device?.state?.[property]?.lastEventTime;
|
67
|
-
const eventInterface = propertyInterfaces[property];
|
68
52
|
|
69
53
|
if (this.notify(device?._id, eventTime, eventInterface, property, value, changed) && device) {
|
70
54
|
this.scrypted.getDeviceLogger(device).log('i', `state change: ${property} ${value}`);
|
71
55
|
}
|
56
|
+
|
57
|
+
this.upserts.add(device._id);
|
58
|
+
this.upsertThrottle();
|
59
|
+
|
60
|
+
return changed;
|
72
61
|
}
|
73
62
|
|
74
63
|
updateDescriptor(device: PluginDevice) {
|