mqtt: frigate api

This commit is contained in:
Koushik Dutta
2021-09-21 01:15:04 -07:00
parent fdbb1434a4
commit cc2cde233f
8 changed files with 142 additions and 26 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/mqtt",
"version": "0.0.14",
"version": "0.0.17",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/mqtt",
"version": "0.0.14",
"version": "0.0.17",
"dependencies": {
"@types/node": "^16.6.1",
"aedes": "^0.46.1",

View File

@@ -35,5 +35,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.0.14"
"version": "0.0.17"
}

View File

@@ -0,0 +1,108 @@
import type { ScryptedDeviceBase } from "@scrypted/sdk";
import type { MqttClient, MqttSubscriptions } from "./mqtt-client";
declare const mqtt: MqttClient;
declare const device: ScryptedDeviceBase;
export type FrigateObjectType =
'person' |
'bicycle' |
'car' |
'motorcycle' |
'airplane' |
'bus' |
'train' |
'car' |
'boat' |
'traffic light' |
'fire hydrant' |
'stop sign' |
'parking meter' |
'bench' |
'bird' |
'cat' |
'dog' |
'horse' |
'sheep' |
'cow' |
'elephant' |
'bear' |
'zebra' |
'giraffe' |
'backpack' |
'umbrella' |
'handbag' |
'tie' |
'suitcase' |
'frisbee' |
'skis' |
'snowboard' |
'sports ball' |
'kite' |
'baseball bat' |
'baseball glove' |
'skateboard' |
'surfboard' |
'tennis racket' |
'bottle' |
'wine glass' |
'cup' |
'fork' |
'knife' |
'spoon' |
'bowl' |
'banana' |
'apple' |
'sandwich' |
'orange' |
'broccoli' |
'carrot' |
'hot dog' |
'pizza' |
'donut' |
'cake' |
'chair' |
'couch' |
'potted plant' |
'bed' |
'dining table' |
'toilet' |
'tv' |
'laptop' |
'mouse' |
'remote' |
'keyboard' |
'cell phone' |
'microwave' |
'oven' |
'toaster' |
'sink' |
'refrigerator' |
'book' |
'clock' |
'vase' |
'scissors' |
'teddy bear' |
'hair drier' |
'toothbrush';
export interface FrigateZoneWatcher {
zone: string,
objects: FrigateObjectType[];
}
export function frigateMotionSensor(zoneWatcher: FrigateZoneWatcher, ...zoneWatchers: FrigateZoneWatcher[]) {
const subs: MqttSubscriptions = {};
const counts: { [type: string]: number } = {};
for (const z of [zoneWatcher, ...zoneWatchers]) {
for (const o of z.objects) {
subs[`/${z.zone}/${o}`] = message => {
counts[z.zone] = message.json;
device.motionDetected = Object.values(counts).reduce((a, b) => a + b, 0) !== 0;
}
}
}
mqtt.subscribe(subs);
mqtt.handle({}, "MotionSensor");
}

View File

@@ -16,6 +16,6 @@ export interface MqttHandler {
export interface MqttClient {
subscribe(subscriptions: MqttSubscriptions, options?: any): void;
handle<T>(handler?: T, ...interfaces: ScryptedInterface[]): void;
handle<T>(handler?: T, ...interfaces: string[]): void;
publish(topic: string, value: any): Promise<void>;
}

View File

@@ -6,14 +6,14 @@ import { Settings, Setting, DeviceProvider, OnOff, ScryptedDeviceBase, ScryptedI
import sdk from '@scrypted/sdk';
import { monacoEvalDefaults } from './monaco';
import { scryptedEval } from './scrypted-eval';
import { MqttClient, MqttSubscriptions } from './mqtt-client';
import { MqttClient, MqttSubscriptions } from './api/mqtt-client';
import { connect, Client, IClientSubscribeOptions, ClientSubscribeCallback } from 'mqtt';
import aedes from 'aedes';
import net from 'net';
import ws from 'websocket-stream';
import http from 'http';
const loopbackLight = require("!!raw-loader!./loopback-light.ts");
const loopbackLight = require("!!raw-loader!./examples/loopback-light.ts");
const methodInterfaces: { [method: string]: string } = {};
for (const desc of Object.values(ScryptedInterfaceDescriptors)) {
@@ -77,8 +77,7 @@ class MqttDevice extends ScryptedDeviceBase implements Scriptable, Settings {
this.client?.end();
this.client = undefined;
const url = new URL(this.storage.getItem('url'));
const { pathname } = url;
this.pathname = pathname;
this.pathname = url.pathname.substring(1);
const urlWithoutPath = new URL(this.storage.getItem('url'));
urlWithoutPath.pathname = '';
@@ -100,7 +99,7 @@ class MqttDevice extends ScryptedDeviceBase implements Scriptable, Settings {
const mqtt: MqttClient = {
subscribe: (subscriptions: MqttSubscriptions, options?: any) => {
for (const topic of Object.keys(subscriptions)) {
const fullTopic = pathname + topic;
const fullTopic = this.pathname + topic;
const cb = subscriptions[topic];
if (options) {
client.subscribe(fullTopic, options)
@@ -109,7 +108,7 @@ class MqttDevice extends ScryptedDeviceBase implements Scriptable, Settings {
client.subscribe(fullTopic)
}
client.on('message', (messageTopic, message) => {
if (fullTopic !== messageTopic)
if (fullTopic !== messageTopic && fullTopic !== '/' + messageTopic)
return;
this.console.log('mqtt message', topic, message.toString());
cb({
@@ -139,7 +138,7 @@ class MqttDevice extends ScryptedDeviceBase implements Scriptable, Settings {
value = JSON.stringify(value);
if (value.constructor.name !== Buffer.name)
value = value.toString();
client.publish(pathname + topic, value);
client.publish(this.pathname + topic, value);
}
}
await scryptedEval(this, script, {

View File

@@ -1,8 +1,11 @@
const types = require("!!raw-loader!@scrypted/sdk/types.d.ts");
const sdk = require("!!raw-loader!@scrypted/sdk/index.d.ts");
const client = require("!!raw-loader!./mqtt-client.ts");
const libs = {
types: require("!!raw-loader!@scrypted/sdk/types.d.ts"),
sdk: require("!!raw-loader!@scrypted/sdk/index.d.ts"),
client: require("!!raw-loader!./api/mqtt-client.ts"),
frigate: require("!!raw-loader!./api/frigate.ts"),
};
function monacoEvalDefaultsFunction(monaco, types, sdk, client) {
function monacoEvalDefaultsFunction(monaco, libs) {
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
Object.assign(
{},
@@ -14,14 +17,13 @@ function monacoEvalDefaultsFunction(monaco, types, sdk, client) {
)
);
const catLibs = Object.values(libs).join('\n');
const catlibsNoExport = Object.keys(libs).filter(lib => lib !== 'sdk').map(lib => libs[lib]).map(lib => lib.toString().replace(/export /g, '')).join('\n');
monaco.languages.typescript.typescriptDefaults.addExtraLib(`
${types}
${sdk}
${client}
${catLibs}
declare global {
${types.replace("export interface", "interface")}
${client.replace("export interface", "interface")}
${catlibsNoExport}
const log: Logger;
@@ -38,18 +40,16 @@ function monacoEvalDefaultsFunction(monaco, types, sdk, client) {
);
monaco.languages.typescript.typescriptDefaults.addExtraLib(
sdk,
libs['sdk'],
"node_modules/@types/scrypted__sdk/index.d.ts"
);
}
export const monacoEvalDefaults = `(function() {
const types = \`${types}\`;
const sdk = \`${sdk}\`;
const client = \`${client}\`;
const libs = ${JSON.stringify(libs)};
return (monaco) => {
(${monacoEvalDefaultsFunction})(monaco, types, sdk, client);
(${monacoEvalDefaultsFunction})(monaco, libs);
}
})();
`;

View File

@@ -2,6 +2,13 @@ import ts, { ScriptTarget } from "typescript";
import sdk, { ScryptedDeviceBase } from "@scrypted/sdk";
import vm from "vm";
const frigate = require("!!raw-loader!./api/frigate.ts");
const types = require("!!raw-loader!!@scrypted/sdk/types.d.ts");
const libs = {
frigate,
types,
};
const { systemManager, deviceManager, mediaManager, endpointManager } = sdk;
function tsCompile(source: string, options: ts.TranspileOptions = null): string {
@@ -19,7 +26,8 @@ function tsCompile(source: string, options: ts.TranspileOptions = null): string
export async function scryptedEval(device: ScryptedDeviceBase, script: string, params: { [name: string]: any }) {
try {
const compiled = tsCompile(script);
const allScripts = Object.values(libs).join('\n').toString() + script;
const compiled = tsCompile(allScripts);
const allParams = Object.assign({}, params, {
systemManager,
@@ -30,6 +38,7 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, p
console: device.console,
localStorage: device.storage,
device,
exports: {},
});
try {