This commit is contained in:
Koushik Dutta
2023-01-28 22:28:56 -08:00
parent 182bffee97
commit 09c48fe768
10 changed files with 0 additions and 1673 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,23 +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": [
"**/plugin-remote-worker.*",
"<node_internals>/**"
],
"preLaunchTask": "scrypted: deploy+debug",
"sourceMaps": true,
"localRoot": "${workspaceFolder}/out",
"remoteRoot": "/plugin/",
"type": "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,4 +0,0 @@
# MyQ Garage Plugin for Scrypted
The MyQ Garage Plugin brings MyQ garage door support into Scrypted.
Currently, no other MyQ devices are supported.

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +0,0 @@
{
"name": "@scrypted/myq",
"version": "0.1.44",
"description": "A MyQ Garage 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": [
"myq",
"scrypted",
"plugin"
],
"scrypted": {
"name": "MyQ Garage Controller",
"type": "DeviceProvider",
"realfs": true,
"interfaces": [
"DeviceProvider",
"Settings"
]
},
"devDependencies": {
"@scrypted/sdk": "file:../../sdk",
"@types/lodash": "^4.14.172"
},
"dependencies": {
"@hjdhjd/myq": "^6.0.7",
"axios": "^0.27.2",
"lodash": "^4.17.21"
}
}

View File

@@ -1,191 +0,0 @@
import sdk, { ScryptedDeviceBase, DeviceProvider, Device, ScryptedDeviceType, Entry, Refresh, OnOff, Settings, Setting, EntrySensor, ScryptedInterface, Battery, Online } from '@scrypted/sdk';
const { log } = sdk;
import { myQApi, myQDevice, myQDeviceInterface } from '@hjdhjd/myq';
import throttle from 'lodash/throttle';
const { deviceManager } = sdk;
function isValidGarageDoor(device: myQDeviceInterface) {
//return device_type === 'wifigaragedooropener' || device_type === 'virtualgaragedooropener' || device_type === 'garagedooropener';
return device.device_family === 'garagedoor'
}
class GarageController extends ScryptedDeviceBase implements DeviceProvider, Settings, Battery {
devices = new Map<string, GarageDoor>();
account: myQApi;
loginTokenTime: number;
start: Promise<void>;
throttleRefresh = throttle(async () => {
try {
await this.discoverDevices(0);
await this.updateStates();
}
catch (e) {
console.error('refresh failed', e);
}
}, 60000, {
leading: true,
trailing: true,
});
constructor() {
super();
this.start = this.discoverDevices(0);
this.start.then(() => this.updateStates());
this.start.catch(e => console.error('discovery error', e));
}
async getSettings(): Promise<Setting[]> {
return [
{
title: 'Email',
key: 'email',
value: localStorage.getItem('email'),
},
{
title: 'Password',
type: 'password',
key: 'password',
value: localStorage.getItem('password'),
}
];
}
async putSetting(key: string, value: string | number | boolean) {
localStorage.setItem(key, value.toString());
}
async getDevice(nativeId: string) {
await this.start;
if (!this.devices[nativeId])
this.devices[nativeId] = new GarageDoor(this, this.account.devices.find(d => d.serial_number === nativeId)!);
this.devices[nativeId]?.refresh();
return this.devices[nativeId];
}
async releaseDevice(id: string, nativeId: string) {
this.console.info(`Device with serial number '${nativeId}' was removed`)
}
async discoverDevices(duration: number) {
if (!this.account) {
const email = localStorage.getItem('email');
const password = localStorage.getItem('password');
if (!email || !password) {
throw new Error('Not logged in.');
}
this.account = new myQApi(email, password, console);
}
await this.account.refreshDevices();
console.log(this.account.devices);
const devices: Device[] = [];
for (const device of this.account.devices) {
if (!isValidGarageDoor(device)) {
console.log('ignoring device', device);
continue;
}
const parent = this.account.devices.find((value: any) => value.serial_number === device.parent_device_id) as any;
const interfaces = [ScryptedInterface.Entry, ScryptedInterface.EntrySensor, ScryptedInterface.Refresh, ScryptedInterface.Online];
if (device.state.dps_low_battery_mode !== undefined)
interfaces.push(ScryptedInterface.Battery);
devices.push({
name: device.name,
nativeId: device.serial_number,
interfaces,
type: ScryptedDeviceType.Garage,
info: {
manufacturer: parent?.state?.brand_name ?? 'MyQ',
serialNumber: device.serial_number,
firmware: parent?.state?.firmware_version
}
});
}
await deviceManager.onDevicesChanged({
devices,
});
}
async updateStates() {
for (const device of this.account.devices) {
if (!isValidGarageDoor(device)) {
console.log('ignoring device', device);
continue;
}
const d = await this.getDevice(device.serial_number) as GarageDoor;
d.entryOpen = device.state.door_state !== 'closed';
// there's no battery level, so set it to full or empty.
// consumers of the battery level can decide when to alert on low battery.
if (device.state.dps_low_battery_mode !== undefined)
d.batteryLevel = device.state.dps_low_battery_mode ? 0 : 100;
}
}
}
class GarageDoor extends ScryptedDeviceBase implements Entry, Refresh, EntrySensor, Online {
controller: GarageController;
constructor(controller: GarageController, public device: myQDevice) {
super(device.serial_number)
this.controller = controller;
this.online = device.state.online;
this.refresh();
}
async closeEntry() {
this.controller.account.execute(this.device, 'close');
this.refreshForLongRunningOperation();
}
async openEntry() {
this.controller.account.execute(this.device, 'open');
this.refreshForLongRunningOperation();
}
async getRefreshFrequency() {
return 60;
}
refreshForLongRunningOperation() {
// refreshf for 10 minutes.
for (let i = 0; i < 10; i++) {
setTimeout(() => this.refresh(), i * 60000);
}
}
async refresh() {
this.controller.throttleRefresh();
}
}
// class GarageLight extends ScryptedDeviceBase implements OnOff, Refresh {
// controller: GarageController;
// info: Device;
// constructor(controller, info: Device) {
// super(info.nativeId);
// this.controller = controller;
// this.info = info;
// this.refresh();
// }
// async turnOn() {
// // this.lightStateCommand(1);
// }
// async turnOff() {
// // this.lightStateCommand(0);
// }
// async refresh() {
// };
// async getRefreshFrequency() {
// return 60;
// };
// }
export default new GarageController();

View File

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