mirror of
https://github.com/koush/scrypted.git
synced 2026-03-20 16:40:24 +00:00
dummy-switch: run shell scripts and typescript programs by flipping a switch
This commit is contained in:
4
plugins/dummy-switch/.gitignore
vendored
Normal file
4
plugins/dummy-switch/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.DS_Store
|
||||
out/
|
||||
node_modules/
|
||||
dist/
|
||||
8
plugins/dummy-switch/.npmignore
Normal file
8
plugins/dummy-switch/.npmignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
out/
|
||||
node_modules/
|
||||
*.map
|
||||
fs
|
||||
src
|
||||
.vscode
|
||||
dist/*.js
|
||||
22
plugins/dummy-switch/.vscode/launch.json
vendored
Normal file
22
plugins/dummy-switch/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Scrypted Debugger",
|
||||
"address": "${config:scrypted.debugHost}",
|
||||
"port": 10081,
|
||||
"request": "attach",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"preLaunchTask": "scrypted: deploy+debug",
|
||||
"sourceMaps": true,
|
||||
"localRoot": "${workspaceFolder}/out",
|
||||
"remoteRoot": "/plugin/",
|
||||
"type": "pwa-node"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
plugins/dummy-switch/.vscode/settings.json
vendored
Normal file
4
plugins/dummy-switch/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
}
|
||||
20
plugins/dummy-switch/.vscode/tasks.json
vendored
Normal file
20
plugins/dummy-switch/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "scrypted: deploy+debug",
|
||||
"type": "shell",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "silent",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"command": "npm run scrypted-vscode-launch ${config:scrypted.debugHost}",
|
||||
},
|
||||
]
|
||||
}
|
||||
21
plugins/dummy-switch/README.md
Normal file
21
plugins/dummy-switch/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# @scrypted/dummy-switch
|
||||
|
||||
## Usage
|
||||
|
||||
This dummy switch plugin lets you create dummy switches
|
||||
that can be synced with HomeKit or Google Home. The switch
|
||||
can be programmed to run a script or shell script when turned on or off.
|
||||
|
||||
## npm commands
|
||||
* npm run scrypted-webpack
|
||||
* npm run scrypted-deploy <ipaddress>
|
||||
* npm run scrypted-debug <ipaddress>
|
||||
|
||||
## scrypted distribution via npm
|
||||
1. Ensure package.json is set up properly for publishing on npm.
|
||||
2. npm publish
|
||||
|
||||
## Visual Studio Code configuration
|
||||
|
||||
* If using a remote server, edit [.vscode/settings.json](blob/master/.vscode/settings.json) to specify the IP Address of the Scrypted server.
|
||||
* Launch Scrypted Debugger from the launch menu.
|
||||
205
plugins/dummy-switch/package-lock.json
generated
Normal file
205
plugins/dummy-switch/package-lock.json
generated
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"name": "@scrypted/dummy-motion-sensor",
|
||||
"version": "0.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/dummy-motion-sensor",
|
||||
"version": "0.0.2",
|
||||
"dependencies": {
|
||||
"@types/node": "^16.6.1",
|
||||
"axios": "^0.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.0.72",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.4.4",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
|
||||
"@babel/plugin-transform-typescript": "^7.15.0",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/node": "^16.6.1",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.1",
|
||||
"babel-loader": "^8.0.4",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"babel-plugin-minify-dead-code-elimination": "^0.5.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-template": "^6.26.0",
|
||||
"browserify-buffertools": "^1.0.2",
|
||||
"bytebuffer": "^5.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"engine.io-client": "^3.3.2",
|
||||
"event-target-shim": "^5.0.1",
|
||||
"events": "^3.0.0",
|
||||
"long": "^4.0.0",
|
||||
"node-cmd": "^3.0.0",
|
||||
"node-ip": "^0.1.2",
|
||||
"raw-loader": "^1.0.0",
|
||||
"terser": "^3.14.1",
|
||||
"ts-loader": "^5.4.5",
|
||||
"tsconfig-paths-webpack-plugin": "^3.2.0",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^4.28.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-inject-plugin": "^1.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-debug": "bin/scrypted-debug.js",
|
||||
"scrypted-deploy": "bin/scrypted-deploy.js",
|
||||
"scrypted-deploy-debug": "bin/scrypted-deploy-debug.js",
|
||||
"scrypted-package-json": "bin/scrypted-package-json.js",
|
||||
"scrypted-readme": "bin/scrypted-readme.js",
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@scrypted/sdk": {
|
||||
"resolved": "../../sdk",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz",
|
||||
"integrity": "sha512-Sr7BhXEAer9xyGuCN3Ek9eg9xPviCF2gfu9kTfuU2HkTVAMYSDeX40fvpmo72n5nansg3nsBjuQBrsS28r+NUw=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
|
||||
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "1.5.10",
|
||||
"is-buffer": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"dependencies": {
|
||||
"debug": "=3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects/node_modules/debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/is-buffer": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
|
||||
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../../sdk",
|
||||
"requires": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.4.4",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
|
||||
"@babel/plugin-transform-typescript": "^7.15.0",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/node": "^16.6.1",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^0.21.1",
|
||||
"babel-loader": "^8.0.4",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"babel-plugin-minify-dead-code-elimination": "^0.5.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-template": "^6.26.0",
|
||||
"browserify-buffertools": "^1.0.2",
|
||||
"bytebuffer": "^5.0.1",
|
||||
"chalk": "^2.4.2",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"engine.io-client": "^3.3.2",
|
||||
"event-target-shim": "^5.0.1",
|
||||
"events": "^3.0.0",
|
||||
"long": "^4.0.0",
|
||||
"node-cmd": "^3.0.0",
|
||||
"node-ip": "^0.1.2",
|
||||
"raw-loader": "^1.0.0",
|
||||
"terser": "^3.14.1",
|
||||
"ts-loader": "^5.4.5",
|
||||
"tsconfig-paths-webpack-plugin": "^3.2.0",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^4.28.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-inject-plugin": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz",
|
||||
"integrity": "sha512-Sr7BhXEAer9xyGuCN3Ek9eg9xPviCF2gfu9kTfuU2HkTVAMYSDeX40fvpmo72n5nansg3nsBjuQBrsS28r+NUw=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
|
||||
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10",
|
||||
"is-buffer": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"requires": {
|
||||
"debug": "=3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
|
||||
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
38
plugins/dummy-switch/package.json
Normal file
38
plugins/dummy-switch/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@scrypted/dummy-switch",
|
||||
"scripts": {
|
||||
"prepublishOnly": "NODE_ENV=production scrypted-webpack",
|
||||
"prescrypted-vscode-launch": "scrypted-webpack",
|
||||
"scrypted-vscode-launch": "scrypted-deploy-debug",
|
||||
"scrypted-deploy-debug": "scrypted-deploy-debug",
|
||||
"scrypted-debug": "scrypted-debug",
|
||||
"scrypted-deploy": "scrypted-deploy",
|
||||
"scrypted-readme": "scrypted-readme",
|
||||
"scrypted-package-json": "scrypted-package-json",
|
||||
"scrypted-webpack": "scrypted-webpack"
|
||||
},
|
||||
"keywords": [
|
||||
"scrypted",
|
||||
"plugin",
|
||||
"dummy",
|
||||
"switch",
|
||||
"shell",
|
||||
"script"
|
||||
],
|
||||
"scrypted": {
|
||||
"name": "Dummy Switch Plugin",
|
||||
"type": "DeviceProvider",
|
||||
"interfaces": [
|
||||
"DeviceProvider",
|
||||
"Settings"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^16.6.1",
|
||||
"axios": "^0.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
},
|
||||
"version": "0.0.2"
|
||||
}
|
||||
125
plugins/dummy-switch/src/main.ts
Normal file
125
plugins/dummy-switch/src/main.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { DeviceProvider, OnOff, Scriptable, ScriptSource, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings } from '@scrypted/sdk';
|
||||
import sdk from '@scrypted/sdk';
|
||||
import { createMonacoEvalDefaults, scryptedEval } from '../../../common/src/scrypted-eval';
|
||||
import child_process from 'child_process';
|
||||
|
||||
const { log, deviceManager } = sdk;
|
||||
|
||||
class DummySwitch extends ScryptedDeviceBase implements OnOff, Scriptable {
|
||||
language: string;
|
||||
constructor(nativeId: string) {
|
||||
super(nativeId);
|
||||
|
||||
if (nativeId.startsWith('typescript:'))
|
||||
this.language = 'typescript';
|
||||
else
|
||||
this.language = 'shell';
|
||||
}
|
||||
async turnOff(): Promise<void> {
|
||||
this.on = false;
|
||||
const source = JSON.parse(this.storage.getItem('source'));
|
||||
this.eval(source);
|
||||
}
|
||||
async turnOn(): Promise<void> {
|
||||
this.on = true;
|
||||
const source = JSON.parse(this.storage.getItem('source'));
|
||||
this.eval(source);
|
||||
}
|
||||
async saveScript(script: ScriptSource): Promise<void> {
|
||||
this.storage.setItem('source', JSON.stringify(script));
|
||||
}
|
||||
async loadScripts(): Promise<{ [filename: string]: ScriptSource; }> {
|
||||
const filename = this.language === 'typescript' ? 'dummy-switch-script.ts' : 'dummy-switch-script.sh';
|
||||
const ret: { [filename: string]: ScriptSource; } = {
|
||||
};
|
||||
|
||||
try {
|
||||
const source = JSON.parse(this.storage.getItem('source'));
|
||||
ret[filename] = source;
|
||||
}
|
||||
catch (e) {
|
||||
ret[filename] = {
|
||||
script: '',
|
||||
}
|
||||
}
|
||||
Object.assign(ret[filename], {
|
||||
language: this.language,
|
||||
name: 'Switch Script',
|
||||
monacoEvalDefaults: this.language === 'typescript' ? createMonacoEvalDefaults({}) : undefined,
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
async eval(source: ScriptSource, variables?: { [name: string]: any; }): Promise<any> {
|
||||
if (this.language === 'typescript')
|
||||
return scryptedEval(this, source.script, {}, {});
|
||||
const cp = child_process.spawn('sh', {
|
||||
env: {
|
||||
DUMMY_ON: (!!this.on).toString(),
|
||||
},
|
||||
});
|
||||
cp.stdin.write(source.script);
|
||||
cp.stdout.on('data', data => this.console.log(data.toString()));
|
||||
cp.stderr.on('data', data => this.console.log(data.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
class DummySwitchProvider extends ScryptedDeviceBase implements DeviceProvider, Settings {
|
||||
devices = new Map<string, any>();
|
||||
|
||||
constructor(nativeId?: string) {
|
||||
super(nativeId);
|
||||
|
||||
for (const camId of deviceManager.getNativeIds()) {
|
||||
if (camId)
|
||||
this.getDevice(camId);
|
||||
}
|
||||
|
||||
}
|
||||
async getSettings(): Promise<Setting[]> {
|
||||
return [
|
||||
{
|
||||
key: 'shell:',
|
||||
title: 'Add Switch (Shell Script)',
|
||||
placeholder: 'Switch Name',
|
||||
},
|
||||
{
|
||||
key: 'typescript:',
|
||||
title: 'Add Switch (Typescript)',
|
||||
placeholder: 'Switch Name',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async putSetting(key: string, value: string | number) {
|
||||
// generate a random id
|
||||
const nativeId = key + Math.random().toString();
|
||||
const name = value.toString();
|
||||
|
||||
deviceManager.onDeviceDiscovered({
|
||||
nativeId,
|
||||
name,
|
||||
interfaces: [ScryptedInterface.OnOff, ScryptedInterface.Scriptable],
|
||||
type: ScryptedDeviceType.Switch,
|
||||
});
|
||||
|
||||
|
||||
var text = `New Switch ${name} ready. Check the notification area to complete setup.`;
|
||||
log.a(text);
|
||||
log.clearAlert(text);
|
||||
}
|
||||
|
||||
async discoverDevices(duration: number) {
|
||||
}
|
||||
|
||||
getDevice(nativeId: string) {
|
||||
let ret = this.devices.get(nativeId);
|
||||
if (!ret) {
|
||||
ret = new DummySwitch(nativeId);
|
||||
if (ret)
|
||||
this.devices.set(nativeId, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export default new DummySwitchProvider();
|
||||
6
plugins/dummy-switch/src/scrypted-eval.ts
Normal file
6
plugins/dummy-switch/src/scrypted-eval.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ScryptedDeviceBase } from "@scrypted/sdk";
|
||||
import { scryptedEval as scryptedEvalBase } from "../../../common/src/scrypted-eval";
|
||||
|
||||
export async function scryptedEval(device: ScryptedDeviceBase, script: string, params: { [name: string]: any }) {
|
||||
return scryptedEvalBase(device, script, {}, params);
|
||||
}
|
||||
Reference in New Issue
Block a user