Skip to content
This repository was archived by the owner on May 27, 2025. It is now read-only.

Commit b8761a0

Browse files
author
danecreekphotography
authored
Add support for custom Deepstack models (#417)
* Initial implementation * Schema update * Fix name of property in schema * More schema experiments * Update changelog
1 parent 189132b commit b8761a0

6 files changed

Lines changed: 34 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
## Unreleased
4+
5+
- Add support for `customEndpoint` on a trigger to support custom Deepstack models. Resolves [issue 416](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/416).
6+
37
## 5.6.1
48

59
- Address a minor security vulnerability in a dependency. Resolves [issue 407](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/407).

src/DeepStack.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,19 @@ import * as Settings from "./Settings";
99
import request from "request-promise-native";
1010
import IDeepStackResponse from "./types/IDeepStackResponse";
1111

12-
export default async function analyzeImage(fileName: string): Promise<IDeepStackResponse> {
12+
export default async function analyzeImage(fileName: string, endpoint?: string): Promise<IDeepStackResponse> {
1313
// This method of calling DeepStack comes from https://nodejs.deepstack.cc/
1414
const imageStream = fs.createReadStream(fileName);
1515
const form = { image: imageStream };
1616

17+
const deepstackUri = endpoint
18+
? new URL(endpoint, Settings.deepstackUri)
19+
: new URL("/v1/vision/detection", Settings.deepstackUri);
20+
1721
const rawResponse = await request
1822
.post({
1923
formData: form,
20-
uri: new URL("/v1/vision/detection", Settings.deepstackUri).toString(),
24+
uri: deepstackUri.toString(),
2125
})
2226
.catch(e => {
2327
throw Error(`Failed to call DeepStack at ${Settings.deepstackUri}: ${e.error}`);

src/Trigger.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default class Trigger {
3737
* Use the incrementAnalyzedFiles() method to update the total.
3838
*/
3939
public analyzedFilesCount = 0;
40+
public customEndpoint?: string;
4041
public cooldownTime: number;
4142
public enabled = true;
4243
public name: string;
@@ -76,7 +77,7 @@ export default class Trigger {
7677
private async analyzeImage(fileName: string): Promise<IDeepStackPrediction[] | undefined> {
7778
log.verbose(`Trigger ${this.name}`, `${fileName}: Analyzing`);
7879
const startTime = new Date();
79-
const analysis = await analyzeImage(fileName).catch(e => {
80+
const analysis = await analyzeImage(fileName, this.customEndpoint).catch(e => {
8081
log.warn(`Trigger ${this.name}`, e);
8182
return undefined;
8283
});
@@ -283,6 +284,16 @@ export default class Trigger {
283284
* @returns True if the trigger is activated by the label
284285
*/
285286
public isRegisteredForObject(fileName: string, label: string): boolean {
287+
// If a custom endpoint is specified then watchObjects don't apply and it is assumed
288+
// the trigger is always registered for the detected object.
289+
if (this.customEndpoint) {
290+
log.verbose(`Trigger ${this.name}`, `${fileName}: Custom endpoint matched triggering object ${label}`);
291+
return true;
292+
}
293+
294+
// In the far more common, normal, case where no custom endpoint is specified
295+
// check and see if the detected object is in the list of registered objects
296+
// to watch.
286297
const isRegistered = this.watchObjects?.some(watchLabel => {
287298
return watchLabel.toLowerCase() === label?.toLowerCase();
288299
});

src/TriggerManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export function loadConfiguration(configurations: IConfiguration[]): IConfigurat
7878
triggers = triggerConfigJson.triggers.map(triggerJson => {
7979
log.info("Triggers", `Loaded configuration for ${triggerJson.name}`);
8080
const configuredTrigger = new Trigger({
81+
customEndpoint: triggerJson.customEndpoint,
8182
cooldownTime: triggerJson.cooldownTime,
8283
enabled: triggerJson.enabled ?? true, // If it isn't specified then enable the camera
8384
name: triggerJson.name,

src/schemas/triggerConfiguration.schema.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@
2424
"title": "Trigger",
2525
"description": "A trigger that responds to DeepStack predictions.",
2626
"type": "object",
27-
"anyOf": [{ "required": ["watchPattern"] }, { "required": ["snapshotUri"] }],
28-
"required": ["handlers", "name", "watchObjects"],
27+
"allOf": [
28+
{ "anyOf": [{ "required": ["watchPattern"] }, { "required": ["snapshotUri"] }] },
29+
{ "oneOf": [{ "required": ["watchObjects"] }, { "required": ["customEndpoint"] }] },
30+
{ "required": ["handlers", "name"] }
31+
],
2932
"additionalProperties": false,
3033
"properties": {
3134
"name": {
@@ -45,6 +48,11 @@
4548
"maximum": 600,
4649
"type": "integer"
4750
},
51+
"customEndpoint": {
52+
"description": "Custom endpoint for DeepStack, used to call a custom model. When specified watchObjects is ignored.",
53+
"examples": ["/v1/vision/custom/my_custom_model_name?image"],
54+
"type": "string"
55+
},
4856
"enabled": {
4957
"description": "Set to true to enable the trigger. If false the trigger will be ignored.",
5058
"examples": [true],

src/types/ITriggerJson.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import IPushbulletConfigJson from "../handlers/pushbulletManager/IPushoverConfig
1111

1212
export default interface ITriggerJson {
1313
cooldownTime: number;
14+
customEndpoint?: string;
1415
enabled: boolean;
1516
name: string;
1617
snapshotUri: string;

0 commit comments

Comments
 (0)