Compare commits

..

6 Commits

Author SHA1 Message Date
Koushik Dutta
06d0a4a2f1 server: verup 2023-11-22 13:07:41 -08:00
Koushik Dutta
2fb6e0a368 server: fix connectRPCObject in python. cache/optimize connect code. 2023-11-22 13:00:33 -08:00
Koushik Dutta
c6ed0d8729 snapshot: publish latest with sharp fallbacks 2023-11-22 11:49:42 -08:00
Koushik Dutta
67c6f63dbe Merge branch 'main' of github.com:koush/scrypted 2023-11-22 11:16:10 -08:00
Koushik Dutta
e62b4ad68b snapshot: publish beta with sharp + fallback 2023-11-22 11:16:05 -08:00
slyoldfox
bfec5eb3f3 Support turning on/off ringer (#1193)
Support turning on/off answering machine

Co-authored-by: Marc Vanbrabant <marc@foreach.be>
2023-11-22 10:59:56 -08:00
15 changed files with 534 additions and 404 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/bticino",
"version": "0.0.11",
"version": "0.0.12",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/bticino",
"version": "0.0.11",
"version": "0.0.12",
"dependencies": {
"@slyoldfox/sip": "^0.0.6-1",
"sdp": "^3.0.3",
@@ -40,7 +40,7 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.103",
"version": "0.2.105",
"dev": true,
"license": "ISC",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/bticino",
"version": "0.0.11",
"version": "0.0.12",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",

View File

@@ -0,0 +1,61 @@
import { ScryptedDeviceBase, HttpRequest, HttpResponse, HttpRequestHandler, OnOff } from "@scrypted/sdk";
import { BticinoSipCamera } from "./bticino-camera";
import { VoicemailHandler } from "./bticino-voicemailHandler";
export class BticinoAswmSwitch extends ScryptedDeviceBase implements OnOff, HttpRequestHandler {
private timeout : NodeJS.Timeout
constructor(private camera: BticinoSipCamera, private voicemailHandler : VoicemailHandler) {
super( camera.nativeId + "-aswm-switch")
this.timeout = setTimeout( () => this.syncStatus() , 5000 )
}
turnOff(): Promise<void> {
this.on = false
return this.camera.turnOffAswm()
}
turnOn(): Promise<void> {
this.on = true
return this.camera.turnOnAswm()
}
syncStatus() {
this.on = this.voicemailHandler.isAswmEnabled()
this.timeout = setTimeout( () => this.syncStatus() , 5000 )
}
cancelTimer() {
if( this.timeout ) {
clearTimeout(this.timeout)
}
}
public async onRequest(request: HttpRequest, response: HttpResponse): Promise<void> {
if (request.url.endsWith('/disabled')) {
this.on = false
response.send('Success', {
code: 200,
});
} else if( request.url.endsWith('/enabled') ) {
this.on = true
response.send('Success', {
code: 200,
});
} else if( request.url.endsWith('/enable') ) {
this.turnOn()
response.send('Success', {
code: 200,
});
} else if( request.url.endsWith('/disable') ) {
this.turnOff()
response.send('Success', {
code: 200,
});
} else {
response.send('Unsupported operation', {
code: 400,
});
}
}
}

View File

@@ -20,6 +20,8 @@ import { SipRequest } from '../../sip/src/sip-manager';
import { get } from 'http'
import { ControllerApi } from './c300x-controller-api';
import { BticinoAswmSwitch } from './bticino-aswm-switch';
import { BticinoMuteSwitch } from './bticino-mute-switch';
const STREAM_TIMEOUT = 65000;
const { mediaManager } = sdk;
@@ -63,10 +65,50 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
get(`http://${c300x}:8080/reboot?now`, (res) => {
console.log("Reboot API result: " + res.statusCode)
});
}).on('error', (error) => {
this.console.error(error)
reject(error)
} ).end();
})
}
muteRinger(mute : boolean): Promise<void> {
return new Promise<void>( (resolve,reject ) => {
let c300x = SipHelper.getIntercomIp(this)
get(`http://${c300x}:8080/mute?raw=true&enable=` + mute, (res) => {
console.log("Mute API result: " + res.statusCode)
}).on('error', (error) => {
this.console.error(error)
reject(error)
} ).end();
})
}
muteStatus(): Promise<boolean> {
return new Promise<boolean>( (resolve,reject ) => {
let c300x = SipHelper.getIntercomIp(this)
get(`http://${c300x}:8080/mute?status=true&raw=true`, (res) => {
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; })
res.on('error', (error) => this.console.log(error))
res.on('end', () => {
try {
return resolve(JSON.parse(rawData))
} catch (e) {
console.error(e.message);
reject(e.message)
}
})
}).on('error', (error) => {
this.console.error(error)
reject(error)
} ).end();
})
}
getVideoClips(options?: VideoClipOptions): Promise<VideoClip[]> {
return new Promise<VideoClip[]>( (resolve,reject ) => {
let c300x = SipHelper.getIntercomIp(this)
@@ -95,7 +137,10 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
console.error(e.message);
}
})
});
}).on('error', (error) => {
this.console.error(error)
reject(error)
} ).end(); ;
});
}
@@ -132,6 +177,18 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
} )
}
turnOnAswm() : Promise<void> {
return this.persistentSipManager.enable().then( (sipCall) => {
sipCall.message( "*8*91##" )
} )
}
turnOffAswm() : Promise<void> {
return this.persistentSipManager.enable().then( (sipCall) => {
sipCall.message( "*8*92##" )
} )
}
async takePicture(option?: PictureOptions): Promise<MediaObject> {
throw new Error("The SIP doorbell camera does not provide snapshots. Install the Snapshot Plugin if snapshots are available via an URL.");
}
@@ -378,11 +435,17 @@ export class BticinoSipCamera extends ScryptedDeviceBase implements DeviceProvid
]
}
async getDevice(nativeId: string) : Promise<BticinoSipLock> {
async getDevice(nativeId: string) : Promise<any> {
if( nativeId && nativeId.endsWith('-aswm-switch')) {
return new BticinoAswmSwitch(this, this.voicemailHandler)
} else if( nativeId && nativeId.endsWith('-mute-switch') ) {
return new BticinoMuteSwitch(this)
}
return new BticinoSipLock(this)
}
async releaseDevice(id: string, nativeId: string): Promise<void> {
this.stopIntercom()
this.voicemailHandler.cancelTimer()
this.persistentSipManager.cancelTimer()
this.controllerApi.cancelTimer()

View File

@@ -3,6 +3,7 @@ import { BticinoSipCamera } from "./bticino-camera"
export class VoicemailHandler extends SipRequestHandler {
private timeout : NodeJS.Timeout
private aswmIsEnabled: boolean
constructor( private sipCamera : BticinoSipCamera ) {
super()
@@ -15,14 +16,12 @@ export class VoicemailHandler extends SipRequestHandler {
checkVoicemail() {
if( !this.sipCamera )
return
if( this.isEnabled() ) {
this.sipCamera.console.debug("Checking answering machine, cameraId: " + this.sipCamera.id )
this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) )
} else {
this.sipCamera.console.debug("Answering machine check not enabled, cameraId: " + this.sipCamera.id )
}
//TODO: make interval customizable, now every 5 minutes
this.timeout = setTimeout( () => this.checkVoicemail() , 5 * 60 * 1000 )
this.sipCamera.console.debug("Checking answering machine, cameraId: " + this.sipCamera.id )
this.sipCamera.getAswmStatus().catch( e => this.sipCamera.console.error(e) )
//TODO: make interval customizable, now every minute
this.timeout = setTimeout( () => this.checkVoicemail() , 1 * 60 * 1000 )
}
cancelTimer() {
@@ -32,10 +31,11 @@ export class VoicemailHandler extends SipRequestHandler {
}
handle(request: SipRequest) {
if( this.isEnabled() ) {
const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1
const message : string = request.content.toString()
if( message.startsWith('*#8**40*0*0*1176*0*2##') ) {
const lastVoicemailMessageTimestamp : number = Number.parseInt( this.sipCamera.storage.getItem('lastVoicemailMessageTimestamp') ) || -1
const message : string = request.content.toString()
if( message.startsWith('*#8**40*0*0*') || message.startsWith('*#8**40*1*0*') ) {
this.aswmIsEnabled = message.startsWith('*#8**40*1*0*');
if( this.isEnabled() ) {
this.sipCamera.console.debug("Handling incoming answering machine reply")
const messages : string[] = message.split(';')
let lastMessageTimestamp : number = 0
@@ -53,12 +53,12 @@ export class VoicemailHandler extends SipRequestHandler {
}
} )
if( (lastVoicemailMessageTimestamp == null && lastMessageTimestamp > 0) ||
( lastVoicemailMessageTimestamp != null && lastMessageTimestamp > lastVoicemailMessageTimestamp ) ) {
( lastVoicemailMessageTimestamp != null && lastMessageTimestamp > lastVoicemailMessageTimestamp ) ) {
this.sipCamera.log.a(`You have ${countNewMessages} new voicemail messages.`)
this.sipCamera.storage.setItem('lastVoicemailMessageTimestamp', lastMessageTimestamp.toString())
} else {
} else {
this.sipCamera.console.debug("No new messages since: " + lastVoicemailMessageTimestamp + " lastMessage: " + lastMessageTimestamp)
}
}
}
}
}
@@ -66,4 +66,8 @@ export class VoicemailHandler extends SipRequestHandler {
isEnabled() : boolean {
return this.sipCamera?.storage?.getItem('notifyVoicemail')?.toLocaleLowerCase() === 'true' || false
}
isAswmEnabled() : boolean {
return this.aswmIsEnabled
}
}

View File

@@ -99,7 +99,7 @@ export class ControllerApi {
})
}
console.log("Endpoint registration status: " + res.statusCode)
});
}).on('error', (e) => this.sipCamera.console.error(e) );
// The default evict time on the c300x-controller is 5 minutes, so this will certainly be within bounds
this.timeout = setTimeout( () => this.registerEndpoints( false ) , 2 * 60 * 1000 )
@@ -114,7 +114,7 @@ export class ControllerApi {
return new Promise( (resolve, reject) => get(`http://${ipAddress}:8080/register-endpoint?raw=true&updateStreamEndpoint=${sipFrom}`, (res) => {
if( res.statusCode != 200 ) reject( "ERROR: Could not update streaming endpoint, call returned: " + res.statusCode )
else resolve()
} ) );
} ).on('error', (error) => this.sipCamera.console.error(error) ).end() );
}
public cancelTimer() {

View File

@@ -36,7 +36,7 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid
const name = settings.newCamera?.toString() === undefined ? "Doorbell" : settings.newCamera?.toString()
await this.updateDevice(nativeId, name)
const device: Device = {
const lockDevice: Device = {
providerNativeId: nativeId,
info: {
//model: `${camera.model} (${camera.data.kind})`,
@@ -49,10 +49,38 @@ export class BticinoSipPlugin extends ScryptedDeviceBase implements DeviceProvid
type: ScryptedDeviceType.Lock,
interfaces: [ScryptedInterface.Lock, ScryptedInterface.HttpRequestHandler],
}
const aswmSwitchDevice: Device = {
providerNativeId: nativeId,
info: {
//model: `${camera.model} (${camera.data.kind})`,
manufacturer: 'BticinoPlugin',
//firmware: camera.data.firmware_version,
//serialNumber: camera.data.device_id
},
nativeId: nativeId + '-aswm-switch',
name: name + ' Voicemail',
type: ScryptedDeviceType.Switch,
interfaces: [ScryptedInterface.OnOff, ScryptedInterface.HttpRequestHandler],
}
const muteSwitchDevice: Device = {
providerNativeId: nativeId,
info: {
//model: `${camera.model} (${camera.data.kind})`,
manufacturer: 'BticinoPlugin',
//firmware: camera.data.firmware_version,
//serialNumber: camera.data.device_id
},
nativeId: nativeId + '-mute-switch',
name: name + ' Muted',
type: ScryptedDeviceType.Switch,
interfaces: [ScryptedInterface.OnOff, ScryptedInterface.HttpRequestHandler],
}
await deviceManager.onDevicesChanged({
providerNativeId: nativeId,
devices: [device],
devices: [lockDevice, aswmSwitchDevice, muteSwitchDevice],
})
let sipCamera : BticinoSipCamera = await this.getDevice(nativeId)

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/snapshot",
"version": "0.2.1",
"version": "0.2.11",
"description": "Snapshot Plugin for Scrypted",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
@@ -32,13 +32,11 @@
"DeviceProvider"
]
},
"optionalDependencies": {
"@koush/sharp": "^0.32.7"
},
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
"@types/node": "^18.16.18",
"axios": "^1.4.0",
"sharp": "^0.32.6",
"whatwg-mimetype": "^3.0.0"
},
"devDependencies": {

View File

@@ -1,5 +1,16 @@
import sdk, { BufferConverter, Image, ImageOptions, MediaObject, MediaObjectOptions, ScryptedDeviceBase, ScryptedMimeTypes } from "@scrypted/sdk";
import sharp from '@koush/sharp';
import type sharp from 'sharp';
export let sharpInstance: typeof sharp;
try {
sharpInstance = require('sharp');
console.log('sharp loaded');
}
catch (e) {
console.warn('sharp failed to load, scrypted server may be out of date', e);
}
export const ImageReaderNativeId = 'imagereader';
async function createVipsMediaObject(image: VipsImage): Promise<Image & MediaObject> {
const ret: Image & MediaObject = await sdk.mediaManager.createMediaObject(image, ScryptedMimeTypes.Image, {
@@ -65,7 +76,7 @@ export class VipsImage implements Image {
resolveWithObject: true,
});
const newImage = sharp(data, {
const newImage = sharpInstance(data, {
raw: info,
});
@@ -87,8 +98,8 @@ export class VipsImage implements Image {
}
}
export async function loadVipsImage(data: Buffer, sourceId: string) {
const image = sharp(data, {
export async function loadVipsImage(data: Buffer|string, sourceId: string) {
const image = sharpInstance(data, {
failOnError: false,
});
const metadata = await image.metadata();

View File

@@ -2,15 +2,15 @@ import AxiosDigestAuth from '@koush/axios-digest-auth';
import { AutoenableMixinProvider } from "@scrypted/common/src/autoenable-mixin-provider";
import { createMapPromiseDebouncer, RefreshPromise, singletonPromise, TimeoutError } from "@scrypted/common/src/promise-utils";
import { SettingsMixinDeviceBase, SettingsMixinDeviceOptions } from "@scrypted/common/src/settings-mixin";
import sdk, { BufferConverter, Camera, DeviceProvider, FFmpegInput, Image, MediaObject, MediaObjectOptions, MixinProvider, RequestMediaStreamOptions, RequestPictureOptions, ResponsePictureOptions, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from "@scrypted/sdk";
import sdk, { BufferConverter, Camera, DeviceManifest, DeviceProvider, FFmpegInput, MediaObject, MediaObjectOptions, MixinProvider, RequestMediaStreamOptions, RequestPictureOptions, ResponsePictureOptions, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera } from "@scrypted/sdk";
import { StorageSettings } from "@scrypted/sdk/storage-settings";
import axios, { AxiosInstance } from "axios";
import https from 'https';
import path from 'path';
import MimeType from 'whatwg-mimetype';
import { ffmpegFilterImage, ffmpegFilterImageBuffer } from './ffmpeg-image-filter';
import { ImageReader, ImageReaderNativeId, loadVipsImage, sharpInstance } from './image-reader';
import { ImageWriter, ImageWriterNativeId } from './image-writer';
import { loadVipsImage, VipsImage } from './image-reader';
const { mediaManager, systemManager } = sdk;
@@ -303,6 +303,20 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
}, async () => {
this.debugConsole?.log("Resizing picture from camera", options?.picture);
if (sharpInstance) {
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
resize: options?.picture,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
// try {
// const mo = await mediaManager.createMediaObject(picture, 'image/jpeg', {
// sourceId: this.id,
@@ -326,24 +340,13 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
// throw e;
// }
// return ffmpegFilterImageBuffer(picture, {
// console: this.debugConsole,
// ffmpegPath: await mediaManager.getFFmpegPath(),
// resize: options?.picture,
// timeout: 10000,
// });
return ffmpegFilterImageBuffer(picture, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
resize: options?.picture,
timeout: 10000,
});
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
resize: options?.picture,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
});
}
catch (e) {
@@ -364,6 +367,25 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
const xmax = Math.max(...this.storageSettings.values.snapshotCropScale.map(([x, y]) => x)) / 100;
const ymax = Math.max(...this.storageSettings.values.snapshotCropScale.map(([x, y]) => y)) / 100;
if (sharpInstance) {
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
crop: {
left: xmin * vips.width,
top: ymin * vips.height,
width: (xmax - xmin) * vips.width,
height: (ymax - ymin) * vips.height,
},
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
// try {
// const mo = await mediaManager.createMediaObject(picture, 'image/jpeg');
// const image = await mediaManager.convertMediaObject<Image>(mo, ScryptedMimeTypes.Image);
@@ -387,35 +409,18 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
// throw e;
// }
// return ffmpegFilterImageBuffer(picture, {
// console: this.debugConsole,
// ffmpegPath: await mediaManager.getFFmpegPath(),
// crop: {
// fractional: true,
// left: xmin,
// top: ymin,
// width: xmax - xmin,
// height: ymax - ymin,
// },
// timeout: 10000,
// });
const vips = await loadVipsImage(picture, this.id);
try {
const ret = await vips.toBuffer({
crop: {
left: xmin * vips.width,
top: ymin * vips.height,
width: (xmax - xmin) * vips.width,
height: (ymax - ymin) * vips.height,
},
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
return ffmpegFilterImageBuffer(picture, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
crop: {
fractional: true,
left: xmin,
top: ymin,
width: xmax - xmin,
height: ymax - ymin,
},
timeout: 10000,
});
}
clearErrorImages() {
@@ -546,7 +551,7 @@ export function parseDims<T extends string>(dict: DimDict<T>) {
ret[t] = parseFloat(val?.substring(0, val?.length - 1)) / 100;
}
else {
ret[t] = parseFloat(val);
ret[t] = val ? parseFloat(val) : undefined;
}
}
return ret;
@@ -567,25 +572,42 @@ class SnapshotPlugin extends AutoenableMixinProvider implements MixinProvider, B
this.fromMimeType = ScryptedMimeTypes.FFmpegInput;
this.toMimeType = 'image/jpeg';
const manifest: DeviceManifest = {
devices: [
{
name: 'Image Writer',
interfaces: [
ScryptedInterface.BufferConverter,
],
type: ScryptedDeviceType.Builtin,
nativeId: ImageWriterNativeId,
}
]
};
if (sharpInstance) {
manifest.devices.push(
{
name: 'Image Reader',
interfaces: [
ScryptedInterface.BufferConverter,
],
type: ScryptedDeviceType.Builtin,
nativeId: ImageReaderNativeId,
}
);
}
process.nextTick(() => {
sdk.deviceManager.onDevicesChanged({
devices: [
{
name: 'Image Writer',
interfaces: [
ScryptedInterface.BufferConverter,
],
type: ScryptedDeviceType.Builtin,
nativeId: ImageWriterNativeId,
}
]
})
})
sdk.deviceManager.onDevicesChanged(manifest)
});
}
async getDevice(nativeId: string): Promise<any> {
if (nativeId === ImageWriterNativeId)
return new ImageWriter(ImageWriterNativeId);
if (nativeId === ImageReaderNativeId)
return new ImageReader(ImageReaderNativeId);
}
async releaseDevice(id: string, nativeId: string): Promise<void> {
@@ -609,11 +631,6 @@ class SnapshotPlugin extends AutoenableMixinProvider implements MixinProvider, B
const ffmpegInput = JSON.parse(data.toString()) as FFmpegInput;
const args = [
...ffmpegInput.inputArguments,
...(ffmpegInput.h264EncoderArguments || []),
];
const {
width,
height,
@@ -636,17 +653,65 @@ class SnapshotPlugin extends AutoenableMixinProvider implements MixinProvider, B
bottom: mime.parameters.get('bottom'),
});
const filename = ffmpegInput.url?.startsWith('file:') && new URL(ffmpegInput.url).pathname;
if (filename && sharpInstance) {
const vips = await loadVipsImage(filename, options?.sourceId);
const resize = width && {
width,
height,
};
if (fractional) {
if (resize.width)
resize.width *= vips.width;
if (resize.height)
resize.height *= vips.height;
}
const crop = left && {
left,
top,
width: right - left,
height: bottom - top,
};
if (cropFractional) {
crop.left *= vips.width;
crop.top *= vips.height;
crop.width *= vips.width;
crop.height *= vips.height;
}
try {
const ret = await vips.toBuffer({
resize,
crop,
format: 'jpg',
});
return ret;
}
finally {
vips.close();
}
}
const args = [
...ffmpegInput.inputArguments,
...(ffmpegInput.h264EncoderArguments || []),
];
return ffmpegFilterImage(args, {
console: this.debugConsole,
ffmpegPath: await mediaManager.getFFmpegPath(),
resize: (isNaN(width) && isNaN(height))
resize: width === undefined && height === undefined
? undefined
: {
width,
height,
fractional,
},
crop: (isNaN(left) && isNaN(top) && isNaN(right) && isNaN(bottom))
crop: left === undefined || right === undefined || top === undefined || bottom === undefined
? undefined
: {
left,

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/server",
"version": "0.66.0",
"version": "0.67.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/server",
"version": "0.66.0",
"version": "0.67.0",
"license": "ISC",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/server",
"version": "0.67.0",
"version": "0.68.0",
"description": "",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",

View File

@@ -45,7 +45,7 @@ class ClusterObject(TypedDict):
id: str
port: int
proxyId: str
sourcePort: str
sourcePort: int
sha256: str
@@ -403,7 +403,7 @@ class PluginRemote:
m.update(bytes(f"{o['id']}{o['port']}{o.get('sourcePort', '')}{o['proxyId']}{clusterSecret}", 'utf8'))
return base64.b64encode(m.digest()).decode('utf-8')
def onProxySerialization(value: Any, proxyId: str, source: int = None):
def onProxySerialization(value: Any, proxyId: str, sourcePeerPort: int = None):
properties: dict = rpc.RpcPeer.prepareProxyProperties(value) or {}
clusterEntry = properties.get('__cluster', None)
if not properties.get('__cluster', None):
@@ -411,13 +411,11 @@ class PluginRemote:
'id': clusterId,
'proxyId': proxyId,
'port': clusterPort,
'source': source,
'sourcePort': sourcePeerPort,
}
clusterEntry['sha256'] = computeClusterObjectHash(clusterEntry)
properties['__cluster'] = clusterEntry
# clusterEntry['proxyId'] = proxyId
# clusterEntry['source'] = source
return properties
self.peer.onProxySerialization = onProxySerialization
@@ -498,18 +496,21 @@ class PluginRemote:
port = clusterObject['port']
proxyId = clusterObject['proxyId']
source = clusterObject.get('source', None)
sourcePort = clusterObject.get('sourcePort', None)
if port == clusterPort:
return await resolveObject(proxyId, source)
return await resolveObject(proxyId, sourcePort)
clusterPeerPromise = ensureClusterPeer(port)
try:
clusterPeer = await clusterPeerPromise
if clusterPeer.tags.get('localPort') == source:
if clusterPeer.tags.get('localPort') == sourcePort:
return value
c = await clusterPeer.getParam('connectRPCObject')
newValue = await c(clusterObject)
peerConnectRPCObject = clusterPeer.tags.get('connectRPCObject')
if not peerConnectRPCObject:
peerConnectRPCObject = await clusterPeer.getParam('connectRPCObject')
clusterPeer.tags['connectRPCObject'] = peerConnectRPCObject
newValue = await peerConnectRPCObject(clusterObject)
if not newValue:
raise Exception('ipc object not found?')
return newValue

View File

@@ -181,8 +181,12 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
// so return the existing proxy.
if (clusterPeer.tags.localPort === sourcePort)
return value;
const connectRPCObject: ConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
const newValue = await connectRPCObject(clusterObject);
let peerConnectRPCObject: ConnectRPCObject = clusterPeer.tags['connectRPCObject'];
if (!peerConnectRPCObject) {
peerConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
clusterPeer.tags['connectRPCObject'] = peerConnectRPCObject;
}
const newValue = await peerConnectRPCObject(clusterObject);
if (!newValue)
throw new Error('rpc object not found?');
return newValue;