@camstack/addon-provider-amcrest 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +115 -18
- package/dist/addon.mjs +115 -18
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -6931,10 +6931,18 @@ var ModelVariantGroupSchema = object({
|
|
|
6931
6931
|
precision: _enum(["fp32", "int8"]).optional(),
|
|
6932
6932
|
/**
|
|
6933
6933
|
* Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
|
|
6934
|
-
* latency-optimized export (e.g. ReLU-activation
|
|
6935
|
-
*
|
|
6934
|
+
* latency-optimized export (e.g. ReLU-activation variant) — the slot the
|
|
6935
|
+
* future performance variants plug into.
|
|
6936
6936
|
*/
|
|
6937
|
-
optimization: _enum(["standard", "fast"]).optional()
|
|
6937
|
+
optimization: _enum(["standard", "fast"]).optional(),
|
|
6938
|
+
/**
|
|
6939
|
+
* Input-resolution axis (square input side, px). Omit ⇒ the family's native
|
|
6940
|
+
* resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
|
|
6941
|
+
* cheap latency lever — especially on Apple ANE and the Intel N100 — at a
|
|
6942
|
+
* small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
|
|
6943
|
+
* the group so the selector can offer it as a variant axis.
|
|
6944
|
+
*/
|
|
6945
|
+
resolution: number().int().positive().optional()
|
|
6938
6946
|
});
|
|
6939
6947
|
var ModelCatalogEntrySchema = object({
|
|
6940
6948
|
id: string(),
|
|
@@ -8587,6 +8595,72 @@ function createRuntimeStateBridge(params) {
|
|
|
8587
8595
|
getStatus
|
|
8588
8596
|
};
|
|
8589
8597
|
}
|
|
8598
|
+
/** Reject after `ms`; always clears its own timer. */
|
|
8599
|
+
async function withTimeout(promise, ms, label) {
|
|
8600
|
+
let timer;
|
|
8601
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
8602
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`${label} timed out after ${String(ms)}ms`)), ms);
|
|
8603
|
+
});
|
|
8604
|
+
try {
|
|
8605
|
+
return await Promise.race([promise, timeout]);
|
|
8606
|
+
} finally {
|
|
8607
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
/**
|
|
8611
|
+
* Start the reachability poll loop. Returns a handle whose `stop()` clears the
|
|
8612
|
+
* timer and prevents any further ticks. Start on device activation, stop on
|
|
8613
|
+
* device teardown (`removeDevice`) so no timer leaks.
|
|
8614
|
+
*/
|
|
8615
|
+
function startReachabilityPoll(options) {
|
|
8616
|
+
const intervalMs = options.intervalMs ?? 3e4;
|
|
8617
|
+
const failuresToOffline = options.failuresToOffline ?? 3;
|
|
8618
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? 1e4;
|
|
8619
|
+
const runImmediately = options.runImmediately ?? true;
|
|
8620
|
+
let stopped = false;
|
|
8621
|
+
let running = false;
|
|
8622
|
+
let consecutiveFailures = 0;
|
|
8623
|
+
let timer;
|
|
8624
|
+
const tick = async () => {
|
|
8625
|
+
if (stopped) return;
|
|
8626
|
+
if (running) return;
|
|
8627
|
+
if (options.isEnabled && !options.isEnabled()) return;
|
|
8628
|
+
running = true;
|
|
8629
|
+
try {
|
|
8630
|
+
const reachable = await withTimeout(Promise.resolve().then(options.probe), probeTimeoutMs, "reachability probe");
|
|
8631
|
+
if (stopped) return;
|
|
8632
|
+
if (reachable) {
|
|
8633
|
+
consecutiveFailures = 0;
|
|
8634
|
+
options.setOnline(true);
|
|
8635
|
+
} else registerFailure("probe resolved unreachable");
|
|
8636
|
+
} catch (error) {
|
|
8637
|
+
if (stopped) return;
|
|
8638
|
+
registerFailure(error instanceof Error ? error.message : "probe threw");
|
|
8639
|
+
} finally {
|
|
8640
|
+
running = false;
|
|
8641
|
+
}
|
|
8642
|
+
};
|
|
8643
|
+
const registerFailure = (reason) => {
|
|
8644
|
+
consecutiveFailures += 1;
|
|
8645
|
+
options.logger?.debug("reachability probe failed", {
|
|
8646
|
+
reason,
|
|
8647
|
+
consecutiveFailures,
|
|
8648
|
+
failuresToOffline
|
|
8649
|
+
});
|
|
8650
|
+
if (consecutiveFailures >= failuresToOffline) options.setOnline(false);
|
|
8651
|
+
};
|
|
8652
|
+
timer = setInterval(() => {
|
|
8653
|
+
tick();
|
|
8654
|
+
}, intervalMs);
|
|
8655
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) timer.unref();
|
|
8656
|
+
if (runImmediately) tick();
|
|
8657
|
+
return { stop: () => {
|
|
8658
|
+
if (stopped) return;
|
|
8659
|
+
stopped = true;
|
|
8660
|
+
if (timer !== void 0) clearInterval(timer);
|
|
8661
|
+
timer = void 0;
|
|
8662
|
+
} };
|
|
8663
|
+
}
|
|
8590
8664
|
/**
|
|
8591
8665
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
8592
8666
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
@@ -11900,6 +11974,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
11900
11974
|
})) }), object({ success: literal(true) }), {
|
|
11901
11975
|
kind: "mutation",
|
|
11902
11976
|
auth: "admin"
|
|
11977
|
+
}), method(object({ nodeId: string() }), object({
|
|
11978
|
+
success: literal(true),
|
|
11979
|
+
regeneratedModelId: string().nullable()
|
|
11980
|
+
}), {
|
|
11981
|
+
kind: "mutation",
|
|
11982
|
+
auth: "admin"
|
|
11903
11983
|
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
11904
11984
|
name: string(),
|
|
11905
11985
|
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
@@ -24504,6 +24584,12 @@ Object.freeze({
|
|
|
24504
24584
|
addonId: null,
|
|
24505
24585
|
access: "create"
|
|
24506
24586
|
},
|
|
24587
|
+
"pipelineExecutor.resetToDefault": {
|
|
24588
|
+
capName: "pipeline-executor",
|
|
24589
|
+
capScope: "system",
|
|
24590
|
+
addonId: null,
|
|
24591
|
+
access: "delete"
|
|
24592
|
+
},
|
|
24507
24593
|
"pipelineExecutor.runAudioTest": {
|
|
24508
24594
|
capName: "pipeline-executor",
|
|
24509
24595
|
capScope: "system",
|
|
@@ -26725,6 +26811,10 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
26725
26811
|
motionActive = false;
|
|
26726
26812
|
/** Keepalive re-emit timer for a sustained motion window. */
|
|
26727
26813
|
motionKeepaliveTimer = null;
|
|
26814
|
+
/** Control-plane reachability poll — drives `device.online` from Dahua CGI
|
|
26815
|
+
* `getDeviceInfo` liveness, decoupled from stream-broker video health.
|
|
26816
|
+
* Started in `onActivate`, stopped in `removeDevice`. */
|
|
26817
|
+
reachabilityPoll = null;
|
|
26728
26818
|
/** Single-flight guard for the `stream-params` camera refresh. */
|
|
26729
26819
|
streamParamsRefreshInFlight = null;
|
|
26730
26820
|
/** Single-flight guard for the `motion-zones` camera refresh. */
|
|
@@ -26897,12 +26987,33 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
26897
26987
|
/** Phase 5 — device is live: open the onboard-motion event stream. */
|
|
26898
26988
|
async onActivate() {
|
|
26899
26989
|
this.ensureEventSubscription();
|
|
26990
|
+
this.startReachabilityPolling();
|
|
26991
|
+
}
|
|
26992
|
+
/** Start the control-plane reachability poll: a Dahua CGI `getDeviceInfo`
|
|
26993
|
+
* round-trip every 30s drives `device.online`, with hysteresis. Replaces
|
|
26994
|
+
* the old stream-health→online mirror so an on-demand (idle) but reachable
|
|
26995
|
+
* camera still reports ONLINE. Idempotent. */
|
|
26996
|
+
startReachabilityPolling() {
|
|
26997
|
+
if (this.reachabilityPoll) return;
|
|
26998
|
+
this.reachabilityPoll = startReachabilityPoll({
|
|
26999
|
+
probe: async () => {
|
|
27000
|
+
await this.ensureClient().getDeviceInfo();
|
|
27001
|
+
return true;
|
|
27002
|
+
},
|
|
27003
|
+
setOnline: (online) => {
|
|
27004
|
+
this.markOnline(online);
|
|
27005
|
+
},
|
|
27006
|
+
isEnabled: () => !this.disabled,
|
|
27007
|
+
logger: this.ctx.logger
|
|
27008
|
+
});
|
|
26900
27009
|
}
|
|
26901
27010
|
/** Teardown — stop the stream + motion timers, drop the client. */
|
|
26902
27011
|
async removeDevice() {
|
|
26903
27012
|
this.ctx.logger.info("Removing Amcrest camera", { tags: { deviceId: this.id } });
|
|
26904
27013
|
this.teardownEventSubscription();
|
|
26905
27014
|
this.clearPtzAutoStop();
|
|
27015
|
+
this.reachabilityPoll?.stop();
|
|
27016
|
+
this.reachabilityPoll = null;
|
|
26906
27017
|
this.client = null;
|
|
26907
27018
|
}
|
|
26908
27019
|
registerStreamCatalogProvider() {
|
|
@@ -36923,21 +37034,7 @@ var AmcrestProviderAddon = class extends BaseDeviceProvider {
|
|
|
36923
37034
|
throw new Error(`Amcrest: probe on ${host || "(unknown host)"} resolved neither mac nor host address — cannot persist a stable row key. Verify network reachability + credentials, then retry.`);
|
|
36924
37035
|
}
|
|
36925
37036
|
async onInitialize() {
|
|
36926
|
-
|
|
36927
|
-
this.subscribe({ category: EventCategory.DeviceStateChanged }, (event) => {
|
|
36928
|
-
const data = event.data;
|
|
36929
|
-
if (data.capName !== "camera-streams") return;
|
|
36930
|
-
const deviceId = data.deviceId;
|
|
36931
|
-
if (typeof deviceId !== "number") return;
|
|
36932
|
-
const registry = this.ctx.kernel.deviceRegistry;
|
|
36933
|
-
if (!registry) return;
|
|
36934
|
-
if (registry.getAddonId(deviceId) !== this.addonId) return;
|
|
36935
|
-
const device = registry.getById(deviceId);
|
|
36936
|
-
if (!device) return;
|
|
36937
|
-
const online = data.slice?.online === true;
|
|
36938
|
-
if (device.online !== online) device.online = online;
|
|
36939
|
-
});
|
|
36940
|
-
return regs;
|
|
37037
|
+
return await super.onInitialize();
|
|
36941
37038
|
}
|
|
36942
37039
|
async supportsDiscovery() {
|
|
36943
37040
|
return true;
|
package/dist/addon.mjs
CHANGED
|
@@ -6932,10 +6932,18 @@ var ModelVariantGroupSchema = object({
|
|
|
6932
6932
|
precision: _enum(["fp32", "int8"]).optional(),
|
|
6933
6933
|
/**
|
|
6934
6934
|
* Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
|
|
6935
|
-
* latency-optimized export (e.g. ReLU-activation
|
|
6936
|
-
*
|
|
6935
|
+
* latency-optimized export (e.g. ReLU-activation variant) — the slot the
|
|
6936
|
+
* future performance variants plug into.
|
|
6937
6937
|
*/
|
|
6938
|
-
optimization: _enum(["standard", "fast"]).optional()
|
|
6938
|
+
optimization: _enum(["standard", "fast"]).optional(),
|
|
6939
|
+
/**
|
|
6940
|
+
* Input-resolution axis (square input side, px). Omit ⇒ the family's native
|
|
6941
|
+
* resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
|
|
6942
|
+
* cheap latency lever — especially on Apple ANE and the Intel N100 — at a
|
|
6943
|
+
* small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
|
|
6944
|
+
* the group so the selector can offer it as a variant axis.
|
|
6945
|
+
*/
|
|
6946
|
+
resolution: number().int().positive().optional()
|
|
6939
6947
|
});
|
|
6940
6948
|
var ModelCatalogEntrySchema = object({
|
|
6941
6949
|
id: string(),
|
|
@@ -8588,6 +8596,72 @@ function createRuntimeStateBridge(params) {
|
|
|
8588
8596
|
getStatus
|
|
8589
8597
|
};
|
|
8590
8598
|
}
|
|
8599
|
+
/** Reject after `ms`; always clears its own timer. */
|
|
8600
|
+
async function withTimeout(promise, ms, label) {
|
|
8601
|
+
let timer;
|
|
8602
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
8603
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`${label} timed out after ${String(ms)}ms`)), ms);
|
|
8604
|
+
});
|
|
8605
|
+
try {
|
|
8606
|
+
return await Promise.race([promise, timeout]);
|
|
8607
|
+
} finally {
|
|
8608
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8609
|
+
}
|
|
8610
|
+
}
|
|
8611
|
+
/**
|
|
8612
|
+
* Start the reachability poll loop. Returns a handle whose `stop()` clears the
|
|
8613
|
+
* timer and prevents any further ticks. Start on device activation, stop on
|
|
8614
|
+
* device teardown (`removeDevice`) so no timer leaks.
|
|
8615
|
+
*/
|
|
8616
|
+
function startReachabilityPoll(options) {
|
|
8617
|
+
const intervalMs = options.intervalMs ?? 3e4;
|
|
8618
|
+
const failuresToOffline = options.failuresToOffline ?? 3;
|
|
8619
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? 1e4;
|
|
8620
|
+
const runImmediately = options.runImmediately ?? true;
|
|
8621
|
+
let stopped = false;
|
|
8622
|
+
let running = false;
|
|
8623
|
+
let consecutiveFailures = 0;
|
|
8624
|
+
let timer;
|
|
8625
|
+
const tick = async () => {
|
|
8626
|
+
if (stopped) return;
|
|
8627
|
+
if (running) return;
|
|
8628
|
+
if (options.isEnabled && !options.isEnabled()) return;
|
|
8629
|
+
running = true;
|
|
8630
|
+
try {
|
|
8631
|
+
const reachable = await withTimeout(Promise.resolve().then(options.probe), probeTimeoutMs, "reachability probe");
|
|
8632
|
+
if (stopped) return;
|
|
8633
|
+
if (reachable) {
|
|
8634
|
+
consecutiveFailures = 0;
|
|
8635
|
+
options.setOnline(true);
|
|
8636
|
+
} else registerFailure("probe resolved unreachable");
|
|
8637
|
+
} catch (error) {
|
|
8638
|
+
if (stopped) return;
|
|
8639
|
+
registerFailure(error instanceof Error ? error.message : "probe threw");
|
|
8640
|
+
} finally {
|
|
8641
|
+
running = false;
|
|
8642
|
+
}
|
|
8643
|
+
};
|
|
8644
|
+
const registerFailure = (reason) => {
|
|
8645
|
+
consecutiveFailures += 1;
|
|
8646
|
+
options.logger?.debug("reachability probe failed", {
|
|
8647
|
+
reason,
|
|
8648
|
+
consecutiveFailures,
|
|
8649
|
+
failuresToOffline
|
|
8650
|
+
});
|
|
8651
|
+
if (consecutiveFailures >= failuresToOffline) options.setOnline(false);
|
|
8652
|
+
};
|
|
8653
|
+
timer = setInterval(() => {
|
|
8654
|
+
tick();
|
|
8655
|
+
}, intervalMs);
|
|
8656
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) timer.unref();
|
|
8657
|
+
if (runImmediately) tick();
|
|
8658
|
+
return { stop: () => {
|
|
8659
|
+
if (stopped) return;
|
|
8660
|
+
stopped = true;
|
|
8661
|
+
if (timer !== void 0) clearInterval(timer);
|
|
8662
|
+
timer = void 0;
|
|
8663
|
+
} };
|
|
8664
|
+
}
|
|
8591
8665
|
/**
|
|
8592
8666
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
8593
8667
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
@@ -11901,6 +11975,12 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
11901
11975
|
})) }), object({ success: literal(true) }), {
|
|
11902
11976
|
kind: "mutation",
|
|
11903
11977
|
auth: "admin"
|
|
11978
|
+
}), method(object({ nodeId: string() }), object({
|
|
11979
|
+
success: literal(true),
|
|
11980
|
+
regeneratedModelId: string().nullable()
|
|
11981
|
+
}), {
|
|
11982
|
+
kind: "mutation",
|
|
11983
|
+
auth: "admin"
|
|
11904
11984
|
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
11905
11985
|
name: string(),
|
|
11906
11986
|
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
@@ -24505,6 +24585,12 @@ Object.freeze({
|
|
|
24505
24585
|
addonId: null,
|
|
24506
24586
|
access: "create"
|
|
24507
24587
|
},
|
|
24588
|
+
"pipelineExecutor.resetToDefault": {
|
|
24589
|
+
capName: "pipeline-executor",
|
|
24590
|
+
capScope: "system",
|
|
24591
|
+
addonId: null,
|
|
24592
|
+
access: "delete"
|
|
24593
|
+
},
|
|
24508
24594
|
"pipelineExecutor.runAudioTest": {
|
|
24509
24595
|
capName: "pipeline-executor",
|
|
24510
24596
|
capScope: "system",
|
|
@@ -26726,6 +26812,10 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
26726
26812
|
motionActive = false;
|
|
26727
26813
|
/** Keepalive re-emit timer for a sustained motion window. */
|
|
26728
26814
|
motionKeepaliveTimer = null;
|
|
26815
|
+
/** Control-plane reachability poll — drives `device.online` from Dahua CGI
|
|
26816
|
+
* `getDeviceInfo` liveness, decoupled from stream-broker video health.
|
|
26817
|
+
* Started in `onActivate`, stopped in `removeDevice`. */
|
|
26818
|
+
reachabilityPoll = null;
|
|
26729
26819
|
/** Single-flight guard for the `stream-params` camera refresh. */
|
|
26730
26820
|
streamParamsRefreshInFlight = null;
|
|
26731
26821
|
/** Single-flight guard for the `motion-zones` camera refresh. */
|
|
@@ -26898,12 +26988,33 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
26898
26988
|
/** Phase 5 — device is live: open the onboard-motion event stream. */
|
|
26899
26989
|
async onActivate() {
|
|
26900
26990
|
this.ensureEventSubscription();
|
|
26991
|
+
this.startReachabilityPolling();
|
|
26992
|
+
}
|
|
26993
|
+
/** Start the control-plane reachability poll: a Dahua CGI `getDeviceInfo`
|
|
26994
|
+
* round-trip every 30s drives `device.online`, with hysteresis. Replaces
|
|
26995
|
+
* the old stream-health→online mirror so an on-demand (idle) but reachable
|
|
26996
|
+
* camera still reports ONLINE. Idempotent. */
|
|
26997
|
+
startReachabilityPolling() {
|
|
26998
|
+
if (this.reachabilityPoll) return;
|
|
26999
|
+
this.reachabilityPoll = startReachabilityPoll({
|
|
27000
|
+
probe: async () => {
|
|
27001
|
+
await this.ensureClient().getDeviceInfo();
|
|
27002
|
+
return true;
|
|
27003
|
+
},
|
|
27004
|
+
setOnline: (online) => {
|
|
27005
|
+
this.markOnline(online);
|
|
27006
|
+
},
|
|
27007
|
+
isEnabled: () => !this.disabled,
|
|
27008
|
+
logger: this.ctx.logger
|
|
27009
|
+
});
|
|
26901
27010
|
}
|
|
26902
27011
|
/** Teardown — stop the stream + motion timers, drop the client. */
|
|
26903
27012
|
async removeDevice() {
|
|
26904
27013
|
this.ctx.logger.info("Removing Amcrest camera", { tags: { deviceId: this.id } });
|
|
26905
27014
|
this.teardownEventSubscription();
|
|
26906
27015
|
this.clearPtzAutoStop();
|
|
27016
|
+
this.reachabilityPoll?.stop();
|
|
27017
|
+
this.reachabilityPoll = null;
|
|
26907
27018
|
this.client = null;
|
|
26908
27019
|
}
|
|
26909
27020
|
registerStreamCatalogProvider() {
|
|
@@ -36924,21 +37035,7 @@ var AmcrestProviderAddon = class extends BaseDeviceProvider {
|
|
|
36924
37035
|
throw new Error(`Amcrest: probe on ${host || "(unknown host)"} resolved neither mac nor host address — cannot persist a stable row key. Verify network reachability + credentials, then retry.`);
|
|
36925
37036
|
}
|
|
36926
37037
|
async onInitialize() {
|
|
36927
|
-
|
|
36928
|
-
this.subscribe({ category: EventCategory.DeviceStateChanged }, (event) => {
|
|
36929
|
-
const data = event.data;
|
|
36930
|
-
if (data.capName !== "camera-streams") return;
|
|
36931
|
-
const deviceId = data.deviceId;
|
|
36932
|
-
if (typeof deviceId !== "number") return;
|
|
36933
|
-
const registry = this.ctx.kernel.deviceRegistry;
|
|
36934
|
-
if (!registry) return;
|
|
36935
|
-
if (registry.getAddonId(deviceId) !== this.addonId) return;
|
|
36936
|
-
const device = registry.getById(deviceId);
|
|
36937
|
-
if (!device) return;
|
|
36938
|
-
const online = data.slice?.online === true;
|
|
36939
|
-
if (device.online !== online) device.online = online;
|
|
36940
|
-
});
|
|
36941
|
-
return regs;
|
|
37038
|
+
return await super.onInitialize();
|
|
36942
37039
|
}
|
|
36943
37040
|
async supportsDiscovery() {
|
|
36944
37041
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-amcrest",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|