mirror of
https://github.com/koush/scrypted.git
synced 2026-02-06 23:42:19 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
544531122d | ||
|
|
778f0b7ad1 | ||
|
|
35e8a86593 | ||
|
|
c370773af4 | ||
|
|
184f293b92 | ||
|
|
6e10172f7e | ||
|
|
c5ae2cd539 | ||
|
|
e40566e89c | ||
|
|
59ccd4e4d8 | ||
|
|
ae80eb7727 | ||
|
|
f054172dcf | ||
|
|
0d7fb9e13c |
6
.github/workflows/docker-common.yml
vendored
6
.github/workflows/docker-common.yml
vendored
@@ -26,12 +26,6 @@ jobs:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
|
||||
6
.github/workflows/docker.yml
vendored
6
.github/workflows/docker.yml
vendored
@@ -52,12 +52,6 @@ jobs:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM64 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up SSH
|
||||
uses: MrSquaare/ssh-setup-action@v2
|
||||
with:
|
||||
host: ${{ secrets.DOCKER_SSH_HOST_ARM7 }}
|
||||
private-key: ${{ secrets.DOCKER_SSH_PRIVATE_KEY }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
|
||||
@@ -58,18 +58,13 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
worker.worker.terminate();
|
||||
}
|
||||
|
||||
const smProxy = new SystemManagerImpl();
|
||||
smProxy.state = systemManager.getSystemState();
|
||||
const apiProxy = new PluginAPIProxy(sdk.pluginHostAPI);
|
||||
smProxy.api = apiProxy;
|
||||
|
||||
const allParams = Object.assign({}, params, {
|
||||
sdk,
|
||||
fs: require('realfs'),
|
||||
fetch,
|
||||
ScryptedDeviceBase,
|
||||
MixinDeviceBase,
|
||||
systemManager: smProxy,
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
@@ -104,7 +99,6 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
return {
|
||||
value,
|
||||
defaultExport,
|
||||
apiProxy,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
cd /tmp
|
||||
curl -O -L https://github.com/koush/scrypted/releases/download/v0.72.0/scrypted.tar.zst
|
||||
pct restore 10443 scrypted.tar.zst
|
||||
|
||||
function readyn() {
|
||||
while true; do
|
||||
read -p "$1 (y/n) " yn
|
||||
@@ -13,6 +9,48 @@ function readyn() {
|
||||
done
|
||||
}
|
||||
|
||||
cd /tmp
|
||||
SCRYPTED_VERSION=v0.72.0
|
||||
SCRYPTED_TAR_ZST=scrypted-$SCRYPTED_VERSION.tar.zst
|
||||
if [ -z "$VMID" ]
|
||||
then
|
||||
VMID=10443
|
||||
fi
|
||||
|
||||
echo "Downloading scrypted container backup."
|
||||
if [ ! -f "$SCRYPTED_TAR_ZST" ]
|
||||
then
|
||||
curl -O -L https://github.com/koush/scrypted/releases/download/$SCRYPTED_VERSION/scrypted.tar.zst
|
||||
mv scrypted.tar.zst $SCRYPTED_TAR_ZST
|
||||
fi
|
||||
|
||||
echo "Checking for existing container."
|
||||
pct config $VMID
|
||||
if [ "$?" == "0" ]
|
||||
then
|
||||
echo ""
|
||||
echo "Existing container $VMID found. Run this script with --force to overwrite the existing container."
|
||||
echo "This will wipe all existing data. Clone the existing container to retain the data, then reassign the owner of the scrypted volume after installation is complete."
|
||||
echo ""
|
||||
echo "bash $0 --force"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
pct restore $VMID $SCRYPTED_TAR_ZST $@
|
||||
|
||||
if [ "$?" != "0" ]
|
||||
then
|
||||
echo ""
|
||||
echo "pct restore failed"
|
||||
echo ""
|
||||
echo "This may be caused by the server's 'local' storage not supporting containers."
|
||||
echo "Try running this script again with a different storage device (local-lvm, local-zfs). For example:"
|
||||
echo ""
|
||||
echo "bash $0 --storage local-lvm"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Adding udev rule: /etc/udev/rules.d/65-scrypted.rules"
|
||||
readyn "Add udev rule for hardware acceleration? This may conflict with existing rules."
|
||||
if [ "$yn" == "y" ]
|
||||
@@ -23,4 +61,6 @@ then
|
||||
udevadm control --reload-rules && udevadm trigger
|
||||
fi
|
||||
|
||||
echo "Scrypted setup is complete and the container can be started."
|
||||
echo "Scrypted setup is complete and the container resources can be started."
|
||||
echo "Scrypted NVR users should provide at least 4 cores and 16GB RAM prior to starting."
|
||||
|
||||
|
||||
2
plugins/core/.vscode/settings.json
vendored
2
plugins/core/.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"scrypted.debugHost": "scrypted-server",
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
}
|
||||
4
plugins/core/package-lock.json
generated
4
plugins/core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting } from "@scrypted/sdk";
|
||||
import { Script } from "./script";
|
||||
import sdk from '@scrypted/sdk';
|
||||
import sdk, { Device, DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting } from '@scrypted/sdk';
|
||||
import { randomBytes } from "crypto";
|
||||
import fs from 'fs';
|
||||
import path from "path/posix";
|
||||
import { Worker } from "worker_threads";
|
||||
import { Script } from "./script";
|
||||
|
||||
const { deviceManager } = sdk;
|
||||
export const ScriptCoreNativeId = 'scriptcore';
|
||||
@@ -42,6 +41,10 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
const nativeId = 'script:' + randomBytes(8).toString('hex');
|
||||
await this.reportScript(nativeId, name?.toString());
|
||||
const script = new Script(nativeId);
|
||||
this.scripts.set(nativeId, {
|
||||
script,
|
||||
worker: undefined,
|
||||
});
|
||||
if (template) {
|
||||
try {
|
||||
await script.saveScript({
|
||||
@@ -76,20 +79,39 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
|
||||
async getDevice(nativeId: string) {
|
||||
const e = this.scripts.get(nativeId);
|
||||
if (e)
|
||||
return e;
|
||||
if (e) {
|
||||
if (e.script)
|
||||
return e.script;
|
||||
e.worker?.terminate();
|
||||
this.scripts.delete(nativeId);
|
||||
}
|
||||
|
||||
let script = new Script(nativeId);
|
||||
let worker: Worker;
|
||||
|
||||
const triggerDeviceDiscover = async (name: string, type: ScryptedDeviceType, interfaces: string[]) => {
|
||||
const e = this.scripts.get(nativeId);
|
||||
if (e?.script == script)
|
||||
e.script = undefined;
|
||||
|
||||
const device: Device = {
|
||||
providerNativeId: this.nativeId,
|
||||
name,
|
||||
nativeId,
|
||||
type,
|
||||
interfaces,
|
||||
refresh: true,
|
||||
};
|
||||
return await deviceManager.onDeviceDiscovered(device);
|
||||
};
|
||||
|
||||
if (script.providedInterfaces.length > 2) {
|
||||
const fork = sdk.fork<{
|
||||
newScript: typeof newScript,
|
||||
}>();
|
||||
worker = fork.worker;
|
||||
try {
|
||||
script = await (await fork.result).newScript(nativeId);
|
||||
await script.run();
|
||||
script = await (await fork.result).newScript(nativeId, triggerDeviceDiscover);
|
||||
}
|
||||
catch (e) {
|
||||
worker.terminate();
|
||||
@@ -98,8 +120,14 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
}
|
||||
|
||||
worker?.on('exit', () => {
|
||||
if (this.scripts.get(nativeId)?.worker === worker)
|
||||
if (this.scripts.get(nativeId)?.worker === worker) {
|
||||
this.scripts.delete(nativeId);
|
||||
// notify the system that the device needs to be refreshed.
|
||||
if (deviceManager.getNativeIds().includes(nativeId)) {
|
||||
const script = new Script(nativeId);
|
||||
triggerDeviceDiscover(script.providedName, script.providedType, script.providedInterfaces);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.scripts.set(nativeId, {
|
||||
@@ -110,10 +138,14 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
}
|
||||
|
||||
async releaseDevice(id: string, nativeId: string): Promise<void> {
|
||||
this.scripts.get(nativeId)?.worker?.terminate();
|
||||
const worker = this.scripts.get(nativeId)?.worker;
|
||||
this.scripts.delete(nativeId);
|
||||
worker?.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
export async function newScript(nativeId: ScryptedNativeId) {
|
||||
return new Script(nativeId);
|
||||
export async function newScript(nativeId: ScryptedNativeId, triggerDeviceDiscover: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
|
||||
const script = new Script(nativeId, triggerDeviceDiscover);
|
||||
await script.run();
|
||||
return script;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ import { PluginAPIProxy } from "../../../server/src/plugin/plugin-api";
|
||||
const { deviceManager } = sdk;
|
||||
|
||||
export class Script extends ScryptedDeviceBase implements Scriptable, Program, ScriptDeviceImpl {
|
||||
apiProxy: PluginAPIProxy;
|
||||
|
||||
constructor(nativeId: string) {
|
||||
constructor(nativeId: string, public triggerDeviceDiscover?: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
|
||||
super(nativeId);
|
||||
}
|
||||
|
||||
@@ -18,6 +16,8 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program, S
|
||||
this.storage.setItem('data', JSON.stringify({
|
||||
'script.ts': source.script,
|
||||
}));
|
||||
|
||||
this.triggerDeviceDiscover?.(this.providedName, this.providedType, this.providedInterfaces);
|
||||
}
|
||||
|
||||
async loadScripts(): Promise<{ [filename: string]: ScriptSource; }> {
|
||||
@@ -70,46 +70,38 @@ export class Script extends ScryptedDeviceBase implements Scriptable, Program, S
|
||||
}
|
||||
|
||||
prepareScript() {
|
||||
this.apiProxy?.removeListeners();
|
||||
|
||||
Object.assign(this, createScriptDevice([
|
||||
ScryptedInterface.Scriptable,
|
||||
ScryptedInterface.Program,
|
||||
]));
|
||||
}
|
||||
|
||||
async run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
async runInternal(script: string, variables?: { [name: string]: any; }): Promise<any> {
|
||||
this.prepareScript();
|
||||
|
||||
try {
|
||||
const data = JSON.parse(this.storage.getItem('data'));
|
||||
|
||||
const { value, defaultExport, apiProxy } = await scryptedEval(this, data['script.ts'], Object.assign({
|
||||
const { value, defaultExport } = await scryptedEval(this, script, Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
this.apiProxy = apiProxy;
|
||||
|
||||
await this.postRunScript(defaultExport);
|
||||
return value;
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('error loading script', e);
|
||||
this.console.error('error evaluating script', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async run(variables?: { [name: string]: any; }): Promise<any> {
|
||||
const data = JSON.parse(this.storage.getItem('data'));
|
||||
return this.runInternal(data['script.ts'], variables)
|
||||
}
|
||||
|
||||
async eval(source: ScriptSource, variables?: { [name: string]: any }) {
|
||||
this.prepareScript();
|
||||
|
||||
const { value, defaultExport, apiProxy } = await scryptedEval(this, source.script, Object.assign({
|
||||
device: this,
|
||||
}, variables));
|
||||
|
||||
this.apiProxy = apiProxy;
|
||||
|
||||
await this.postRunScript(defaultExport);
|
||||
return value;
|
||||
return this.runInternal(source.script, variables);
|
||||
}
|
||||
|
||||
// will be done at runtime
|
||||
|
||||
@@ -43,6 +43,9 @@ export async function listenEvents(thisDevice: ScryptedDeviceBase, client: Onvif
|
||||
// thisDevice.motionDetected = true;
|
||||
}
|
||||
else if (event === OnvifEvent.MotionStop) {
|
||||
// reset the trigger to debounce per above.
|
||||
triggerMotion();
|
||||
|
||||
// thisDevice.motionDetected = false;
|
||||
}
|
||||
else if (event === OnvifEvent.AudioStart)
|
||||
|
||||
4
plugins/reolink/package-lock.json
generated
4
plugins/reolink/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/reolink",
|
||||
"version": "0.0.52",
|
||||
"version": "0.0.53",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/reolink",
|
||||
"version": "0.0.52",
|
||||
"version": "0.0.53",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/reolink",
|
||||
"version": "0.0.52",
|
||||
"version": "0.0.53",
|
||||
"description": "Reolink Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, Intercom, MediaObject, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, PictureOptions, Reboot, ScryptedDeviceType, ScryptedInterface, Setting } from "@scrypted/sdk";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, Intercom, MediaObject, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, PanTiltZoom, PanTiltZoomCommand, PictureOptions, Reboot, ScryptedDeviceType, ScryptedInterface, Setting } from "@scrypted/sdk";
|
||||
import { StorageSettings } from '@scrypted/sdk/storage-settings';
|
||||
import { EventEmitter } from "stream";
|
||||
import { Destroyable, RtspProvider, RtspSmartCamera, UrlMediaStreamOptions } from "../../rtsp/src/rtsp";
|
||||
@@ -8,7 +8,7 @@ import { listenEvents } from './onvif-events';
|
||||
import { OnvifIntercom } from './onvif-intercom';
|
||||
import { AIState, DevInfo, Enc, ReolinkCameraClient } from './reolink-api';
|
||||
|
||||
class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom, ObjectDetector {
|
||||
class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom, ObjectDetector, PanTiltZoom {
|
||||
client: ReolinkCameraClient;
|
||||
onvifClient: OnvifCameraAPI;
|
||||
onvifIntercom = new OnvifIntercom(this);
|
||||
@@ -36,6 +36,19 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom,
|
||||
hasObjectDetector: {
|
||||
json: true,
|
||||
hide: true,
|
||||
},
|
||||
ptz: {
|
||||
title: 'PTZ Capabilities',
|
||||
choices: [
|
||||
'Pan',
|
||||
'Tilt',
|
||||
'Zoom',
|
||||
],
|
||||
multiple: true,
|
||||
onPut: async () => {
|
||||
await this.updateDevice();
|
||||
this.updatePtzCaps();
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -44,12 +57,28 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom,
|
||||
|
||||
this.updateDeviceInfo();
|
||||
this.updateDevice();
|
||||
|
||||
this.updatePtzCaps();
|
||||
}
|
||||
|
||||
updatePtzCaps() {
|
||||
const { ptz } = this.storageSettings.values;
|
||||
this.ptzCapabilities = {
|
||||
pan: ptz?.includes('Pan'),
|
||||
tilt: ptz?.includes('Tilt'),
|
||||
zoom: ptz?.includes('Zoom'),
|
||||
}
|
||||
}
|
||||
|
||||
async getDetectionInput(detectionId: string, eventId?: any): Promise<MediaObject> {
|
||||
return;
|
||||
}
|
||||
|
||||
async ptzCommand(command: PanTiltZoomCommand): Promise<void> {
|
||||
const client = this.getClient();
|
||||
client.ptz(command);
|
||||
}
|
||||
|
||||
async getObjectTypes(): Promise<ObjectDetectionTypes> {
|
||||
try {
|
||||
const ai: AIState = this.storageSettings.values.hasObjectDetector[0]?.value;
|
||||
@@ -86,7 +115,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom,
|
||||
return this.onvifIntercom.stopIntercom();
|
||||
}
|
||||
|
||||
updateDevice() {
|
||||
async updateDevice() {
|
||||
const interfaces = this.provider.getInterfaces();
|
||||
let type = ScryptedDeviceType.Camera;
|
||||
let name = 'Reolink Camera';
|
||||
@@ -98,10 +127,13 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom,
|
||||
type = ScryptedDeviceType.Doorbell;
|
||||
name = 'Reolink Doorbell';
|
||||
}
|
||||
if (this.storageSettings.values.ptz?.length) {
|
||||
interfaces.push(ScryptedInterface.PanTiltZoom);
|
||||
}
|
||||
if (this.storageSettings.values.hasObjectDetector) {
|
||||
interfaces.push(ScryptedInterface.ObjectDetector);
|
||||
}
|
||||
this.provider.updateDevice(this.nativeId, name, interfaces, type);
|
||||
await this.provider.updateDevice(this.nativeId, name, interfaces, type);
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
@@ -393,7 +425,12 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, Reboot, Intercom,
|
||||
|
||||
async putSetting(key: string, value: string) {
|
||||
this.client = undefined;
|
||||
super.putSetting(key, value);
|
||||
if (this.storageSettings.keys[key]) {
|
||||
await this.storageSettings.putSetting(key, value);
|
||||
}
|
||||
else {
|
||||
await super.putSetting(key, value);
|
||||
}
|
||||
this.updateDevice();
|
||||
this.updateDeviceInfo();
|
||||
}
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
import AxiosDigestAuth from "@koush/axios-digest-auth";
|
||||
import { getMotionState, reolinkHttpsAgent } from './probe';
|
||||
import { PanTiltZoomCommand } from "@scrypted/sdk";
|
||||
import { sleep } from "@scrypted/common/src/sleep";
|
||||
|
||||
export interface Enc {
|
||||
audio: number;
|
||||
channel: number;
|
||||
audio: number;
|
||||
channel: number;
|
||||
mainStream: Stream;
|
||||
subStream: Stream;
|
||||
subStream: Stream;
|
||||
}
|
||||
|
||||
export interface Stream {
|
||||
bitRate: number;
|
||||
bitRate: number;
|
||||
frameRate: number;
|
||||
gop: number;
|
||||
height: number;
|
||||
profile: string;
|
||||
size: string;
|
||||
vType: string;
|
||||
width: number;
|
||||
gop: number;
|
||||
height: number;
|
||||
profile: string;
|
||||
size: string;
|
||||
vType: string;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface DevInfo {
|
||||
B485: number;
|
||||
IOInputNum: number;
|
||||
IOOutputNum: number;
|
||||
audioNum: number;
|
||||
buildDay: string;
|
||||
cfgVer: string;
|
||||
channelNum: number;
|
||||
detail: string;
|
||||
diskNum: number;
|
||||
exactType: string;
|
||||
firmVer: string;
|
||||
B485: number;
|
||||
IOInputNum: number;
|
||||
IOOutputNum: number;
|
||||
audioNum: number;
|
||||
buildDay: string;
|
||||
cfgVer: string;
|
||||
channelNum: number;
|
||||
detail: string;
|
||||
diskNum: number;
|
||||
exactType: string;
|
||||
firmVer: string;
|
||||
frameworkVer: number;
|
||||
hardVer: string;
|
||||
model: string;
|
||||
name: string;
|
||||
pakSuffix: string;
|
||||
serial: string;
|
||||
type: string;
|
||||
wifi: number;
|
||||
hardVer: string;
|
||||
model: string;
|
||||
name: string;
|
||||
pakSuffix: string;
|
||||
serial: string;
|
||||
type: string;
|
||||
wifi: number;
|
||||
}
|
||||
|
||||
export interface AIDetectionState {
|
||||
@@ -156,4 +158,62 @@ export class ReolinkCameraClient {
|
||||
|
||||
return response.data?.[0]?.value?.DevInfo;
|
||||
}
|
||||
|
||||
async ptz(command: PanTiltZoomCommand) {
|
||||
let op = '';
|
||||
if (command.pan < 0)
|
||||
op += 'Left';
|
||||
else if (command.pan > 0)
|
||||
op += 'Right'
|
||||
if (command.tilt < 0)
|
||||
op += 'Down';
|
||||
else if (command.tilt > 0)
|
||||
op += 'Up';
|
||||
|
||||
if (!op)
|
||||
return;
|
||||
|
||||
const url = new URL(`http://${this.host}/api.cgi`);
|
||||
const params = url.searchParams;
|
||||
params.set('cmd', 'PtzCtrl');
|
||||
params.set('user', this.username);
|
||||
params.set('password', this.password);
|
||||
|
||||
const c1 = this.digestAuth.request({
|
||||
method: 'POST',
|
||||
url: url.toString(),
|
||||
httpsAgent: reolinkHttpsAgent,
|
||||
data: [
|
||||
{
|
||||
cmd: "PtzCtrl",
|
||||
param: {
|
||||
channel: this.channelId,
|
||||
op,
|
||||
speed: 10,
|
||||
timeout: 1,
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
await sleep(500);
|
||||
|
||||
const c2 = this.digestAuth.request({
|
||||
method: 'POST',
|
||||
url: url.toString(),
|
||||
httpsAgent: reolinkHttpsAgent,
|
||||
data: [
|
||||
{
|
||||
cmd: "PtzCtrl",
|
||||
param: {
|
||||
channel: this.channelId,
|
||||
op: "Stop"
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
this.console.log(await c1);
|
||||
this.console.log(await c2);
|
||||
}
|
||||
}
|
||||
|
||||
1487
plugins/snapshot/package-lock.json
generated
1487
plugins/snapshot/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/snapshot",
|
||||
"version": "0.2.23",
|
||||
"version": "0.2.30",
|
||||
"description": "Snapshot Plugin for Scrypted",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
@@ -35,9 +35,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@koush/axios-digest-auth": "^0.8.5",
|
||||
"@types/node": "^20.10.5",
|
||||
"axios": "^1.4.0",
|
||||
"sharp": "^0.33.1",
|
||||
"@types/node": "^20.10.6",
|
||||
"axios": "^0.21.4",
|
||||
"sharp": "^0.32.6",
|
||||
"whatwg-mimetype": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -12,8 +12,12 @@ import { ffmpegFilterImage, ffmpegFilterImageBuffer } from './ffmpeg-image-filte
|
||||
import { ImageConverter, ImageConverterNativeId } from './image-converter';
|
||||
import { ImageReader, ImageReaderNativeId, loadSharp, loadVipsImage } from './image-reader';
|
||||
import { ImageWriter, ImageWriterNativeId } from './image-writer';
|
||||
import os from 'os';
|
||||
|
||||
const { mediaManager, systemManager } = sdk;
|
||||
if (os.cpus().find(cpu => cpu.model?.toLowerCase().includes('qemu'))) {
|
||||
sdk.log.a('QEMU CPU detected. Set your CPU model to host.');
|
||||
}
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
@@ -300,7 +304,7 @@ class SnapshotMixin extends SettingsMixinDeviceBase<Camera> implements Camera {
|
||||
picture = this.currentPicture;
|
||||
}
|
||||
|
||||
const needSoftwareResize = !!(options?.picture?.width || options?.picture?.height);
|
||||
const needSoftwareResize = !!(options?.picture?.width || options?.picture?.height) && this.storageSettings.values.snapshotResolution !== 'Full Resolution';
|
||||
if (needSoftwareResize) {
|
||||
try {
|
||||
picture = await this.snapshotDebouncer({
|
||||
@@ -669,6 +673,9 @@ export class SnapshotPlugin extends AutoenableMixinProvider implements MixinProv
|
||||
}
|
||||
};
|
||||
|
||||
if (mixin.storageSettings.values.snapshotResolution === 'Full Resolution')
|
||||
delete rpo.picture;
|
||||
|
||||
if (mixin && iface === ScryptedInterface.Camera) {
|
||||
buffer = await mixin.takePictureRaw(rpo)
|
||||
}
|
||||
|
||||
2
server/.vscode/launch.json
vendored
2
server/.vscode/launch.json
vendored
@@ -27,7 +27,7 @@
|
||||
"${workspaceFolder}/**/*.js"
|
||||
],
|
||||
"env": {
|
||||
"SCRYPTED_DEFAULT_AUTHENTICATION": "demo"
|
||||
// "SCRYPTED_DEFAULT_AUTHENTICATION": "demo"
|
||||
// force usage of system python because brew python is 3.11
|
||||
// which has no wheels for coreml tools or tflite-runtime
|
||||
// "SCRYPTED_PYTHON_PATH": "/usr/bin/python3",
|
||||
|
||||
19
server/package-lock.json
generated
19
server/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.77.0",
|
||||
"version": "0.79.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.77.0",
|
||||
"version": "0.79.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
@@ -25,7 +25,7 @@
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^4.6.0",
|
||||
"mime": "^4.0.1",
|
||||
"mime": "^3.0.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
@@ -2491,17 +2491,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz",
|
||||
"integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa"
|
||||
],
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||
"bin": {
|
||||
"mime": "bin/cli.js"
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.77.0",
|
||||
"version": "0.79.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
@@ -19,7 +19,7 @@
|
||||
"linkfs": "^2.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"memfs": "^4.6.0",
|
||||
"mime": "^4.0.1",
|
||||
"mime": "^3.0.0",
|
||||
"nan": "^2.18.0",
|
||||
"node-dijkstra": "^2.5.0",
|
||||
"node-forge": "^1.3.1",
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface StdPassThroughs {
|
||||
}
|
||||
|
||||
export function getConsole(hook: (stdout: PassThrough, stderr: PassThrough) => Promise<void>,
|
||||
also?: Console, alsoPrefix?: string) {
|
||||
also?: Console, alsoPrefix?: string) {
|
||||
|
||||
const stdout = new PassThrough();
|
||||
const stderr = new PassThrough();
|
||||
@@ -63,7 +63,7 @@ export function getConsole(hook: (stdout: PassThrough, stderr: PassThrough) => P
|
||||
|
||||
export function prepareConsoles(getConsoleName: () => string, systemManager: () => SystemManager, deviceManager: () => DeviceManager, getPlugins: () => Promise<any>) {
|
||||
const deviceConsoles = new Map<string, Console>();
|
||||
function getDeviceConsole (nativeId?: ScryptedNativeId) {
|
||||
function getDeviceConsole(nativeId?: ScryptedNativeId) {
|
||||
// the the plugin console is simply the default console
|
||||
// and gets read from stderr/stdout.
|
||||
if (!nativeId)
|
||||
@@ -126,13 +126,16 @@ export function prepareConsoles(getConsoleName: () => string, systemManager: ()
|
||||
|
||||
const connect = async () => {
|
||||
const ds = deviceManager().getDeviceState(nativeId);
|
||||
if (!ds) {
|
||||
// deleted?
|
||||
// device deleted
|
||||
if (!ds)
|
||||
return;
|
||||
}
|
||||
|
||||
const plugins = await getPlugins();
|
||||
const { pluginId, nativeId: mixinNativeId } = await plugins.getDeviceInfo(mixinId);
|
||||
const mixin = systemManager().getDeviceById(mixinId);
|
||||
// mixin deleted
|
||||
if (!mixin)
|
||||
return;
|
||||
const { pluginId, nativeId: mixinNativeId } = mixin;
|
||||
const port = await plugins.getRemoteServicePort(pluginId, 'console-writer');
|
||||
const socket = net.connect(port);
|
||||
socket.write(mixinNativeId + '\n');
|
||||
@@ -161,7 +164,7 @@ export function prepareConsoles(getConsoleName: () => string, systemManager: ()
|
||||
nativeIdConsoles.set(mixinId, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
getDeviceConsole,
|
||||
getMixinConsole,
|
||||
|
||||
Reference in New Issue
Block a user