lifx/owntracks: migrate

This commit is contained in:
Koushik Dutta
2023-01-28 22:51:29 -08:00
parent 51d648c2f4
commit bb1aaea221
20 changed files with 0 additions and 1036 deletions

View File

@@ -1,4 +0,0 @@
.DS_Store
out/
node_modules/
dist/

View File

@@ -1,4 +0,0 @@
.DS_Store
out/
node_modules/
dist/*.map

View File

@@ -1,22 +0,0 @@
{
// 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"
}
]
}

View File

@@ -1,4 +0,0 @@
{
"scrypted.debugHost": "127.0.0.1",
}

View File

@@ -1,20 +0,0 @@
{
// 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}",
},
]
}

View File

@@ -1,15 +0,0 @@
# A Lifx plugin
## 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.

View File

@@ -1,324 +0,0 @@
{
"name": "@scrypted/lifx",
"version": "0.2.23",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/lifx",
"version": "0.2.23",
"hasInstallScript": true,
"license": "Apache",
"dependencies": {
"axios": "^0.18.0",
"node-lifx": "github:koush/node-lifx",
"terser": "^3.14.1"
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^16.7.1"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.199",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"webpack": "^5.59.0"
},
"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-setup-project": "bin/scrypted-setup-project.js",
"scrypted-webpack": "bin/scrypted-webpack.js"
},
"devDependencies": {
"@types/node": "^16.11.1",
"@types/stringify-object": "^4.0.0",
"stringify-object": "^3.3.0",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack-bundle-analyzer": "^4.5.0"
}
},
"../sdk": {
"extraneous": true
},
"node_modules/@scrypted/sdk": {
"resolved": "../../sdk",
"link": true
},
"node_modules/@types/node": {
"version": "16.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
"integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==",
"dev": true
},
"node_modules/axios": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
"integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
"deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
"dependencies": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
}
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"node_modules/commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"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/eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
},
"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/is-buffer": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"engines": {
"node": ">=4"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/node-lifx": {
"resolved": "git+ssh://git@github.com/koush/node-lifx.git#ad85de6742168e867d4e54b8571dd3e8220ed9f2",
"dependencies": {
"eventemitter3": "^2.0.2",
"lodash": "^4.5.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
"integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/terser": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-3.14.1.tgz",
"integrity": "sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw==",
"dependencies": {
"commander": "~2.17.1",
"source-map": "~0.6.1",
"source-map-support": "~0.5.6"
},
"bin": {
"terser": "bin/uglifyjs"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/terser/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
}
},
"dependencies": {
"@scrypted/sdk": {
"version": "file:../../sdk",
"requires": {
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^16.11.1",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"stringify-object": "^3.3.0",
"tmp": "^0.2.1",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack": "^5.59.0",
"webpack-bundle-analyzer": "^4.5.0"
}
},
"@types/node": {
"version": "16.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
"integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==",
"dev": true
},
"axios": {
"version": "0.18.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
"integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"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"
}
},
"eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
},
"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"
}
},
"is-buffer": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node-lifx": {
"version": "git+ssh://git@github.com/koush/node-lifx.git#ad85de6742168e867d4e54b8571dd3e8220ed9f2",
"from": "node-lifx@github:koush/node-lifx",
"requires": {
"eventemitter3": "^2.0.2",
"lodash": "^4.5.0"
}
},
"source-map-support": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
"integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"terser": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-3.14.1.tgz",
"integrity": "sha512-NSo3E99QDbYSMeJaEk9YW2lTg3qS9V0aKGlb+PlOrei1X02r1wSBHCNX/O+yeTRFSWPKPIGj6MqvvdqV4rnVGw==",
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1",
"source-map-support": "~0.5.6"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
}
}
}

View File

@@ -1,41 +0,0 @@
{
"name": "@scrypted/lifx",
"version": "0.2.23",
"description": "A Lifx plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",
"build": "scrypted-webpack",
"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"
},
"keywords": [
"lifx",
"scrypted",
"plugin"
],
"scrypted": {
"name": "LIFX Controller",
"type": "DeviceProvider",
"interfaces": [
"DeviceProvider"
]
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^16.7.1"
},
"dependencies": {
"axios": "^0.18.0",
"node-lifx": "github:koush/node-lifx",
"terser": "^3.14.1"
}
}

View File

@@ -1,156 +0,0 @@
import {Client as LifxClient} from 'node-lifx';
var client = new LifxClient();
import sdk, { Device, ScryptedDeviceBase, OnOff, Brightness, ColorSettingHsv, ColorSettingTemperature, Refresh, ScryptedDeviceType, DeviceProvider } from '@scrypted/sdk';
const { deviceManager, log } = sdk;
const StateSetters = {
OnOff: function (s, state) {
state.on = !!(s && s.power);
},
Brightness: function (s, state) {
state.brightness = (s && s.color && s.color.brightness) || 0;
},
ColorSettingTemperature: function (s, state) {
state.colorTemperature = (s && s.color && s.color.kelvin) || 0;
},
ColorSettingHsv: function (st, state) {
var h = (st && st.color && st.color.hue) || 0;
var s = ((st && st.color && st.color.saturation) || 0) / 100;
var v = ((st && st.color && st.color.brightness) || 0) / 100;
state.hsv = { h, s, v };
},
}
class LifxDevice extends ScryptedDeviceBase implements OnOff, Brightness, ColorSettingHsv, ColorSettingTemperature, Refresh {
light: any;
device: Device;
refresher: Function;
constructor(light: any, device: Device) {
super(device.nativeId);
this.light = light;
this.device = device;
this.refresher = (err) => this.refresh();
// schedule a refresh. not doing it immediately, to allow the device to be reported
// by sync first.
setImmediate(() => this.refresh());
}
async refresh() {
this._refresh();
}
async getRefreshFrequency() {
return 5;
}
async _refresh(cb?) {
this.light.getState((err, state) => {
if (state) {
for (var iface of this.device.interfaces) {
var setter = StateSetters[iface];
if (setter) {
setter(state, this);
}
}
}
if (cb) {
cb(err);
}
});
}
// setters
async turnOn() {
this.light.on(0, this.refresher);
}
async turnOff() {
this.light.off(0, this.refresher);
}
async setBrightness(level: number) {
this.light.getState((err, state) => {
var color = state.color;
this.light.color(color.hue, color.saturation, level, color.kelvin, undefined, this.refresher);
})
}
async setColorTemperature(kelvin: number) {
this.light.color(0, 0, 100, kelvin, undefined, this.refresher);
}
async setHsv(h: number, s: number, v: number) {
this.light.color(h, Math.round(s * 100), Math.round(v * 100), undefined, undefined, this.refresher);
}
async getTemperatureMinK() {
return 2500;
}
async getTemperatureMaxK() {
return 9000;
}
}
class LifxController implements DeviceProvider {
lights: any = {};
constructor() {
this.discoverDevices(30);
}
getDevice(id) {
return this.lights[id];
}
newLight(light) {
light.getHardwareVersion((err, data) => {
if (err) {
log.e(`unable to get product version: ${err}`);
return;
}
// these are the interfaces (capabilities) provided by this bulb
var interfaces = ['OnOff', 'Brightness'];
if (data.productFeatures && data.productFeatures.color) {
interfaces.push('ColorSettingHsv');
interfaces.push('ColorSettingTemperature');
}
// lifx bulbs require polling to get their state. it is not
// actively pushed to the controller. implementing the Refresh interface allows
// Scrypted to poll the device intelligently: such as when the UI is visible,
// or HomeKit/Google requests a sync and needs updated state.
interfaces.push('Refresh');
var info = {
name: data.productName,
nativeId: light.id,
interfaces: interfaces,
type: ScryptedDeviceType.Light,
};
log.i(`light found: ${JSON.stringify(info)}`);
deviceManager.onDeviceDiscovered(info);
this.lights[light.id] = new LifxDevice(light, info);
});
}
async discoverDevices(duration: number) {
client.on('light-new', this.newLight.bind(this));
client.init();
setTimeout(() => {
client.stopDiscovery();
}, duration * 1000);
}
}
export default new LifxController();

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2021",
"resolveJsonModule": true,
"moduleResolution": "Node16",
"esModuleInterop": true,
"sourceMap": true
},
"include": [
"src/**/*"
]
}

View File

@@ -1,4 +0,0 @@
.DS_Store
out/
node_modules/
dist/

View File

@@ -1,4 +0,0 @@
.DS_Store
out/
node_modules/
dist/*.map

View File

@@ -1,22 +0,0 @@
{
// 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"
}
]
}

View File

@@ -1,4 +0,0 @@
{
"scrypted.debugHost": "127.0.0.1",
}

View File

@@ -1,20 +0,0 @@
{
// 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}",
},
]
}

View File

@@ -1,15 +0,0 @@
# Owntracks Plugin
## 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.

View File

@@ -1,118 +0,0 @@
{
"name": "@scrypted/owntracks",
"version": "0.0.24",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/owntracks",
"version": "0.0.24",
"hasInstallScript": true,
"license": "Apache",
"dependencies": {
"basic-auth": "^2.0.1"
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.199",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"webpack": "^5.59.0"
},
"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-setup-project": "bin/scrypted-setup-project.js",
"scrypted-webpack": "bin/scrypted-webpack.js"
},
"devDependencies": {
"@types/node": "^16.11.1",
"@types/stringify-object": "^4.0.0",
"stringify-object": "^3.3.0",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack-bundle-analyzer": "^4.5.0"
}
},
"../sdk": {
"extraneous": true
},
"node_modules/@scrypted/sdk": {
"resolved": "../../sdk",
"link": true
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"dependencies": {
"safe-buffer": "5.1.2"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
},
"dependencies": {
"@scrypted/sdk": {
"version": "file:../../sdk",
"requires": {
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^16.11.1",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"stringify-object": "^3.3.0",
"tmp": "^0.2.1",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack": "^5.59.0",
"webpack-bundle-analyzer": "^4.5.0"
}
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
}

View File

@@ -1,40 +0,0 @@
{
"name": "@scrypted/owntracks",
"version": "0.0.24",
"description": "Owntracks Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",
"build": "scrypted-webpack",
"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"
},
"keywords": [
"scrypted",
"plugin"
],
"scrypted": {
"name": "Owntracks Plugin",
"type": "DeviceProvider",
"interfaces": [
"PushHandler",
"Settings",
"DeviceProvider",
"PasswordStore"
]
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"dependencies": {
"basic-auth": "^2.0.1"
}
}

View File

@@ -1,193 +0,0 @@
// https://developer.scrypted.app/#getting-started
import sdk, { Settings, DeviceProvider, ScryptedDeviceType, OccupancySensor, Setting, HttpRequest, PasswordStore, PushHandler, PositionSensor, ScryptedInterface } from "@scrypted/sdk";
import { ScryptedDeviceBase } from "@scrypted/sdk";
const { log, deviceManager, endpointManager } = sdk;
import auth from 'basic-auth';
class OwntracksUser extends ScryptedDeviceBase implements PositionSensor {
constructor(nativeId: string) {
super(nativeId);
}
}
class OwntracksRegion extends ScryptedDeviceBase implements OccupancySensor, Settings {
getSetting(key: string): string | number | boolean {
return null;
}
// create settings that correspond to allowed usernames in this region
async getSettings(): Promise<Setting[]> {
const ret: Setting[] = [];
for (let i = 0; i < this.storage.length; i++) {
const key = this.storage.key(i);
ret.push({
key,
value: key,
title: 'Owntracks Username',
description: 'This sensor will be marked as occupied if this user is in this Owntracks region.',
})
}
ret.push({
title: 'Add User',
placeholder: 'username',
description: 'Owntracks Username',
key: 'new-user',
})
return ret;
}
// create/rename users
async putSetting(key: string, value: string | number | boolean) {
if (key == 'new-user') {
this.storage.setItem(value as string, false.toString());
return;
}
this.storage.removeItem(key);
if ((value as string).length) {
this.storage.setItem(value as string, false.toString());
}
this.sendOccupancyEvent();
}
// look at the user status for every user, and send the correct event.
// todo: timestamp? purge old users?
sendOccupancyEvent() {
for (let i = 0; i < this.storage.length; i++) {
const key = this.storage.key(i);
if (this.storage.getItem(key) === 'true') {
this.occupied = true;
return;
}
}
this.occupied = false;
}
constructor(nativeId: string) {
super(nativeId);
}
}
class Owntracks extends ScryptedDeviceBase implements PushHandler, Settings, DeviceProvider, PasswordStore {
constructor() {
super();
if (!localStorage.getItem('private_http')) {
endpointManager.getPublicPushEndpoint().then(endpoint => {
localStorage.setItem('private_http', endpoint);
log.a('The Owntracks Private HTTP endpoint is available in Settings.');
});
}
}
// owntracks will call the endpoint with a password, so set up a simple password store
// that can be accessed via the scrypted web ui. this allows revocation of passwords,
// and denial of unauthorized users that may have the owntracks private http endpoint.
async getPasswords(): Promise<string[]> {
try {
return JSON.parse(localStorage.getItem('passwords')) || [];
}
catch (e) {
return [];
}
}
get passwords(): string[] {
try {
return JSON.parse(localStorage.getItem('passwords')) || [];
}
catch (e) {
return [];
}
}
savePasswords(passwords: string[]) {
const uniques = {};
passwords.map(password => uniques[password] = true);
passwords = Object.keys(uniques);
localStorage.setItem('passwords', JSON.stringify(passwords));
}
async addPassword(password: string) {
const passwords = this.passwords;
passwords.push(password)
this.savePasswords(passwords);
}
async removePassword(password: string) {
const passwords = this.passwords;
passwords.filter(entry => entry != password);
this.savePasswords(passwords);
}
async checkPassword(password: string): Promise<boolean> {
return this.passwords.includes(password);
}
async discoverDevices(duration: number) {
}
getDevice(nativeId: string) {
return new OwntracksRegion(nativeId);
}
getSetting(key: string): string | number | boolean {
return null;
}
async getSettings(): Promise<Setting[]> {
// create a settings menu that shows the private http endpoint, and allows creation of new regions.
return [
{
key: 'private_http',
title: 'Private HTTP',
description: 'The Private HTTP endpoint that is configured within the Owntracks mobile application. Owntracks users will need to authenticate with one of the passcodes set up by this Plugin.',
readonly: true,
value: localStorage.getItem('private_http') || 'Error creating Private HTTP. Try reloading the plugin',
},
{
key: 'region',
description: 'The name of the Region within Owntracks. Multiple users may specifiy the same Region. The OccupancySensor will be marked as occupied when any of them are within that Region.',
title: 'Add Owntracks Region',
},
];
}
async putSetting(key: string, value: string | number | boolean) {
// creat the named region from the setting.
deviceManager.onDeviceDiscovered({
name: value.toString(),
interfaces: ['OccupancySensor', 'Settings'],
nativeId: value.toString(),
type: ScryptedDeviceType.Sensor,
});
}
getEndpoint(): string {
return "@scrypted/owntracks";
}
async onPush(request: HttpRequest) {
const user = auth.parse(request.headers['authorization']);
if (!this.passwords.includes(user.pass)) {
return;
}
const body = JSON.parse(request.body);
this.console.log(body);
const userNativeId = `user-${user.name}`;
if (!deviceManager.getNativeIds().includes(userNativeId)) {
deviceManager.onDeviceDiscovered({
name: `Owntracks User - ${user.name}`,
nativeId: userNativeId,
type: ScryptedDeviceType.Sensor,
interfaces: [ScryptedInterface.PositionSensor],
})
}
const owntracksUser = new OwntracksUser(userNativeId);
owntracksUser.position = {
latitude: body.lat,
longitude: body.lon,
accuracyRadius: body.acc,
}
// find all regions this user belongs to, and update them.
for (const nativeId of deviceManager.getNativeIds()) {
if (!nativeId || nativeId.startsWith('user-'))
continue;
let region = new OwntracksRegion(nativeId);
let value = region.storage.getItem(user.name);
if (value !== null) {
region.storage.setItem(user.name, body.inregions && body.inregions.includes(nativeId) ? 'true': 'false');
region.sendOccupancyEvent();
}
}
}
}
export default new Owntracks();

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2021",
"resolveJsonModule": true,
"moduleResolution": "Node16",
"esModuleInterop": true,
"sourceMap": true
},
"include": [
"src/**/*"
]
}