@camstack/addon-export-hap 1.2.27 → 1.2.28
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/hap-export.addon.js +2717 -293
- package/dist/hap-export.addon.mjs +2717 -293
- package/package.json +3 -4
package/dist/hap-export.addon.js
CHANGED
|
@@ -3082,6 +3082,9 @@ function handlePipeResult(left, next, ctx) {
|
|
|
3082
3082
|
fallback: left.fallback
|
|
3083
3083
|
}, ctx);
|
|
3084
3084
|
}
|
|
3085
|
+
var $ZodPreprocess = /*@__PURE__*/ $constructor("$ZodPreprocess", (inst, def) => {
|
|
3086
|
+
$ZodPipe.init(inst, def);
|
|
3087
|
+
});
|
|
3085
3088
|
var $ZodReadonly = /*@__PURE__*/ $constructor("$ZodReadonly", (inst, def) => {
|
|
3086
3089
|
$ZodType.init(inst, def);
|
|
3087
3090
|
defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
|
|
@@ -5267,6 +5270,10 @@ function pipe(in_, out) {
|
|
|
5267
5270
|
out
|
|
5268
5271
|
});
|
|
5269
5272
|
}
|
|
5273
|
+
var ZodPreprocess = /*@__PURE__*/ $constructor("ZodPreprocess", (inst, def) => {
|
|
5274
|
+
ZodPipe.init(inst, def);
|
|
5275
|
+
$ZodPreprocess.init(inst, def);
|
|
5276
|
+
});
|
|
5270
5277
|
var ZodReadonly = /*@__PURE__*/ $constructor("ZodReadonly", (inst, def) => {
|
|
5271
5278
|
$ZodReadonly.init(inst, def);
|
|
5272
5279
|
ZodType.init(inst, def);
|
|
@@ -5325,6 +5332,13 @@ function _instanceof(cls, params = {}) {
|
|
|
5325
5332
|
};
|
|
5326
5333
|
return inst;
|
|
5327
5334
|
}
|
|
5335
|
+
function preprocess(fn, schema) {
|
|
5336
|
+
return new ZodPreprocess({
|
|
5337
|
+
type: "pipe",
|
|
5338
|
+
in: transform(fn),
|
|
5339
|
+
out: schema
|
|
5340
|
+
});
|
|
5341
|
+
}
|
|
5328
5342
|
//#endregion
|
|
5329
5343
|
//#region ../../node_modules/zod/v4/classic/compat.js
|
|
5330
5344
|
/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
|
|
@@ -7601,7 +7615,7 @@ import { errMsg } from '@camstack/types'
|
|
|
7601
7615
|
* Extract a human-readable message from an unknown error value.
|
|
7602
7616
|
* Replaces the ubiquitous `errMsg(err)` pattern.
|
|
7603
7617
|
*/
|
|
7604
|
-
function errMsg$
|
|
7618
|
+
function errMsg$15(err) {
|
|
7605
7619
|
if (err instanceof Error) return err.message;
|
|
7606
7620
|
if (typeof err === "string") return err;
|
|
7607
7621
|
return String(err);
|
|
@@ -16003,7 +16017,87 @@ var MethodAccessSchema = _enum([
|
|
|
16003
16017
|
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
16004
16018
|
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
16005
16019
|
var CapScopeSchema = _enum(["device", "system"]);
|
|
16006
|
-
|
|
16020
|
+
/**
|
|
16021
|
+
* DeviceSelector (scope model v3 — 2026-08-12).
|
|
16022
|
+
*
|
|
16023
|
+
* A `device` grant no longer carries a frozen list of deviceIds. It carries
|
|
16024
|
+
* a SELECTOR the matcher resolves against the live fleet, so the grant can be
|
|
16025
|
+
* DYNAMIC: a `types:['camera']` selector automatically covers a camera added
|
|
16026
|
+
* AFTER the grant was minted — no re-grant, no re-login.
|
|
16027
|
+
*
|
|
16028
|
+
* - `all` — every device in the deployment. The broad viewer/operator
|
|
16029
|
+
* lever without a `category` grant (a `category` grant also covers device
|
|
16030
|
+
* caps that carry no deviceId; `all` is specifically the device set).
|
|
16031
|
+
* - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
|
|
16032
|
+
* grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
|
|
16033
|
+
* is NOT covered until the grant is edited.
|
|
16034
|
+
* - `types` — every device of a `DeviceType` (e.g. every `camera`).
|
|
16035
|
+
* DYNAMIC. A device that changes type, or a new device of the type,
|
|
16036
|
+
* re-resolves on the next request.
|
|
16037
|
+
* - `locations` — every device whose operator-assigned `location` label is
|
|
16038
|
+
* in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
|
|
16039
|
+
* null/unset location matches NO `locations` selector.
|
|
16040
|
+
*/
|
|
16041
|
+
var DeviceSelectorSchema = discriminatedUnion("kind", [
|
|
16042
|
+
object({ kind: literal("all") }),
|
|
16043
|
+
object({
|
|
16044
|
+
kind: literal("ids"),
|
|
16045
|
+
ids: array(number().int()).min(1)
|
|
16046
|
+
}),
|
|
16047
|
+
object({
|
|
16048
|
+
kind: literal("types"),
|
|
16049
|
+
types: array(_enum(DeviceType)).min(1)
|
|
16050
|
+
}),
|
|
16051
|
+
object({
|
|
16052
|
+
kind: literal("locations"),
|
|
16053
|
+
locations: array(string().min(1)).min(1)
|
|
16054
|
+
})
|
|
16055
|
+
]);
|
|
16056
|
+
var DeviceTokenScopeSchema = object({
|
|
16057
|
+
type: literal("device"),
|
|
16058
|
+
/** The device SET this grant covers — resolved against the live fleet. */
|
|
16059
|
+
selector: DeviceSelectorSchema,
|
|
16060
|
+
access: array(MethodAccessSchema).min(1),
|
|
16061
|
+
/**
|
|
16062
|
+
* Whether a grant on a PARENT device transparently covers its accessory
|
|
16063
|
+
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
16064
|
+
* Direction is parent → children ONLY.
|
|
16065
|
+
*
|
|
16066
|
+
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
16067
|
+
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
16068
|
+
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
16069
|
+
* operator must grant on the child, not inherit from the parent). Set it
|
|
16070
|
+
* explicitly to override that default per grant.
|
|
16071
|
+
*/
|
|
16072
|
+
includeLinked: boolean().optional()
|
|
16073
|
+
});
|
|
16074
|
+
/**
|
|
16075
|
+
* v2 → v3 lazy migration. A pre-v3 `device` grant carried
|
|
16076
|
+
* `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
|
|
16077
|
+
* `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
|
|
16078
|
+
* EVERY parse path — stored records AND the JWT-carried scope arrays
|
|
16079
|
+
* normalised at the request boundary ({@link normalizeTokenScopes} in
|
|
16080
|
+
* `device-selector.ts`). Chosen over a one-time DB migration because a
|
|
16081
|
+
* migration cannot reach a JWT already in a client's hands; parse-time
|
|
16082
|
+
* migration covers both without a flag day. No cast — the raw object is read
|
|
16083
|
+
* through `Reflect.get` (its static type is `unknown`).
|
|
16084
|
+
*/
|
|
16085
|
+
function migrateLegacyTokenScope(raw) {
|
|
16086
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
16087
|
+
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
16088
|
+
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
16089
|
+
const targets = Reflect.get(raw, "targets");
|
|
16090
|
+
if (!Array.isArray(targets)) return raw;
|
|
16091
|
+
return {
|
|
16092
|
+
type: "device",
|
|
16093
|
+
selector: {
|
|
16094
|
+
kind: "ids",
|
|
16095
|
+
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
16096
|
+
},
|
|
16097
|
+
access: Reflect.get(raw, "access")
|
|
16098
|
+
};
|
|
16099
|
+
}
|
|
16100
|
+
var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
|
|
16007
16101
|
object({
|
|
16008
16102
|
type: literal("category"),
|
|
16009
16103
|
target: CapScopeSchema,
|
|
@@ -16019,18 +16113,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
|
|
|
16019
16113
|
target: string(),
|
|
16020
16114
|
access: array(MethodAccessSchema).min(1)
|
|
16021
16115
|
}),
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
/**
|
|
16025
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
16026
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
16027
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
16028
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
16029
|
-
*/
|
|
16030
|
-
targets: array(string()).min(1),
|
|
16031
|
-
access: array(MethodAccessSchema).min(1)
|
|
16032
|
-
})
|
|
16033
|
-
]);
|
|
16116
|
+
DeviceTokenScopeSchema
|
|
16117
|
+
]));
|
|
16034
16118
|
object({
|
|
16035
16119
|
id: string(),
|
|
16036
16120
|
username: string(),
|
|
@@ -18046,7 +18130,7 @@ var detectionFpsField = {
|
|
|
18046
18130
|
var occupancyRecheckSecField = {
|
|
18047
18131
|
min: 0,
|
|
18048
18132
|
max: 300,
|
|
18049
|
-
default:
|
|
18133
|
+
default: 300,
|
|
18050
18134
|
step: 5
|
|
18051
18135
|
};
|
|
18052
18136
|
var occupancyRecheckFramesField = {
|
|
@@ -20659,7 +20743,11 @@ object({
|
|
|
20659
20743
|
precision: number().int().min(0).max(10).optional()
|
|
20660
20744
|
});
|
|
20661
20745
|
DeviceType.Sensor;
|
|
20662
|
-
|
|
20746
|
+
/**
|
|
20747
|
+
* Ambient illuminance reading in lux. Drives Home Assistant `sensor`
|
|
20748
|
+
* entries with `device_class: illuminance`.
|
|
20749
|
+
*/
|
|
20750
|
+
var AmbientLightSensorStatusSchema = object({
|
|
20663
20751
|
/** Current illuminance in lux (lx). */
|
|
20664
20752
|
lux: number().min(0),
|
|
20665
20753
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -20932,7 +21020,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), CameraCredentialsSchem
|
|
|
20932
21020
|
kind: "query",
|
|
20933
21021
|
auth: "admin"
|
|
20934
21022
|
});
|
|
20935
|
-
|
|
21023
|
+
/**
|
|
21024
|
+
* Carbon-monoxide alarm sensor. Drives Home Assistant `binary_sensor`
|
|
21025
|
+
* entries with `device_class: carbon_monoxide`. Push-driven.
|
|
21026
|
+
*/
|
|
21027
|
+
var CarbonMonoxideStatusSchema = object({
|
|
20936
21028
|
detected: boolean(),
|
|
20937
21029
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
20938
21030
|
lastChangedAt: number()
|
|
@@ -21220,7 +21312,19 @@ Object.values(DeviceType), method(object({
|
|
|
21220
21312
|
kind: "mutation",
|
|
21221
21313
|
auth: "admin"
|
|
21222
21314
|
}), ConsumablesStatusSchema.extend({ lastFetchedAt: number() });
|
|
21223
|
-
|
|
21315
|
+
/**
|
|
21316
|
+
* Door / window / opening / garage / valve contact sensor. Boolean
|
|
21317
|
+
* "is the entry currently open" with the timestamp of the last
|
|
21318
|
+
* transition. Drives Home Assistant `binary_sensor` entries whose
|
|
21319
|
+
* `device_class` is `door`, `window`, `opening`, `garage`, or
|
|
21320
|
+
* `garage_door` — and any future native integration that needs
|
|
21321
|
+
* the same semantics.
|
|
21322
|
+
*
|
|
21323
|
+
* Push-driven: providers update the slice on transition events from
|
|
21324
|
+
* the upstream source (HA WebSocket `state_changed`, ZWave
|
|
21325
|
+
* `notification` …). Consumers read the slice; no polling.
|
|
21326
|
+
*/
|
|
21327
|
+
var ContactStatusSchema = object({
|
|
21224
21328
|
/** True when the entry is open; false when closed. */
|
|
21225
21329
|
entryOpen: boolean(),
|
|
21226
21330
|
/** Ms epoch of the last open↔closed transition. 0 if never observed. */
|
|
@@ -21819,7 +21923,15 @@ object({
|
|
|
21819
21923
|
deviceId: number(),
|
|
21820
21924
|
status: FeatureProbeStatusSchema
|
|
21821
21925
|
});
|
|
21822
|
-
|
|
21926
|
+
/**
|
|
21927
|
+
* Water leak / moisture sensor. Boolean "is liquid currently
|
|
21928
|
+
* detected" with the timestamp of the last transition. Drives Home
|
|
21929
|
+
* Assistant `binary_sensor` entries with `device_class: moisture`,
|
|
21930
|
+
* and any future native flood sensor.
|
|
21931
|
+
*
|
|
21932
|
+
* Push-driven from the upstream source.
|
|
21933
|
+
*/
|
|
21934
|
+
var FloodStatusSchema = object({
|
|
21823
21935
|
/** True when leak is currently detected. */
|
|
21824
21936
|
flooded: boolean(),
|
|
21825
21937
|
/** Ms epoch of the last flooded↔dry transition. 0 if never observed. */
|
|
@@ -21873,7 +21985,15 @@ DeviceType.Humidifier, method(object({
|
|
|
21873
21985
|
kind: "mutation",
|
|
21874
21986
|
auth: "admin"
|
|
21875
21987
|
});
|
|
21876
|
-
|
|
21988
|
+
/**
|
|
21989
|
+
* Single-metric humidity reading. Drives Home Assistant `sensor`
|
|
21990
|
+
* entries with `device_class: humidity`.
|
|
21991
|
+
*
|
|
21992
|
+
* Unit normalisation: percent. The canonical display unit (`%`) is a
|
|
21993
|
+
* descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
21994
|
+
* `sourceInfo`.
|
|
21995
|
+
*/
|
|
21996
|
+
var HumiditySensorStatusSchema = object({
|
|
21877
21997
|
/** Current relative humidity, 0..100. */
|
|
21878
21998
|
percent: number().min(0).max(100),
|
|
21879
21999
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -22495,7 +22615,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22495
22615
|
* tunnel always emits `https://` regardless. */
|
|
22496
22616
|
scheme: _enum(["http", "https"]).optional()
|
|
22497
22617
|
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" });
|
|
22498
|
-
object({
|
|
22618
|
+
var LockControlStatusSchema = object({
|
|
22499
22619
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22500
22620
|
* failure to reach the target — operator intervention required. */
|
|
22501
22621
|
state: _enum([
|
|
@@ -22833,7 +22953,19 @@ authKey: string().optional() }), object({
|
|
|
22833
22953
|
/** Human-readable error when `ok: false`. */
|
|
22834
22954
|
error: string().optional()
|
|
22835
22955
|
}), { kind: "mutation" });
|
|
22836
|
-
|
|
22956
|
+
/**
|
|
22957
|
+
* Hardware / firmware motion sensor cap — binary detected state plus
|
|
22958
|
+
* a timestamp of the last observation. Distinct from
|
|
22959
|
+
* `motion-detection.cap.ts` which owns the LOCAL ML motion pipeline;
|
|
22960
|
+
* `motion` is the lightweight readout from on-camera motion (Reolink
|
|
22961
|
+
* `GetMdState`, Baichuan push `type: motion`, ONVIF analytics).
|
|
22962
|
+
*
|
|
22963
|
+
* Native-motion providers also fan out to `detection.camera-native`
|
|
22964
|
+
* with `source: 'onboard'` so cross-cutting system services
|
|
22965
|
+
* (alert-center, advanced-notifier) can subscribe once and receive
|
|
22966
|
+
* motion from every camera.
|
|
22967
|
+
*/
|
|
22968
|
+
var MotionStatusSchema = object({
|
|
22837
22969
|
detected: boolean(),
|
|
22838
22970
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
22839
22971
|
lastDetectedAt: number().nullable(),
|
|
@@ -23945,7 +24077,7 @@ var GpsLocationSchema = object({
|
|
|
23945
24077
|
/** Reported accuracy in meters (lower = better). */
|
|
23946
24078
|
accuracyMeters: number().nonnegative()
|
|
23947
24079
|
});
|
|
23948
|
-
object({
|
|
24080
|
+
var PresenceStatusSchema = object({
|
|
23949
24081
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
23950
24082
|
state: string(),
|
|
23951
24083
|
/** Optional textual location label (zone name, city, address). Null
|
|
@@ -24361,7 +24493,7 @@ method(object({
|
|
|
24361
24493
|
toMs: number()
|
|
24362
24494
|
}), RecordingAvailabilitySchema, {
|
|
24363
24495
|
kind: "query",
|
|
24364
|
-
auth: "
|
|
24496
|
+
auth: "protected"
|
|
24365
24497
|
}), method(object({
|
|
24366
24498
|
deviceId: number(),
|
|
24367
24499
|
fromMs: number(),
|
|
@@ -24369,14 +24501,14 @@ method(object({
|
|
|
24369
24501
|
tzOffsetMinutes: number()
|
|
24370
24502
|
}), RecordingDaysSchema, {
|
|
24371
24503
|
kind: "query",
|
|
24372
|
-
auth: "
|
|
24504
|
+
auth: "protected"
|
|
24373
24505
|
}), method(object({
|
|
24374
24506
|
deviceId: number(),
|
|
24375
24507
|
fromMs: number(),
|
|
24376
24508
|
toMs: number()
|
|
24377
24509
|
}), RecordingManifestSchema, {
|
|
24378
24510
|
kind: "query",
|
|
24379
|
-
auth: "
|
|
24511
|
+
auth: "protected"
|
|
24380
24512
|
}), method(object({}), RecordingStorageUsageSchema, {
|
|
24381
24513
|
kind: "query",
|
|
24382
24514
|
auth: "admin"
|
|
@@ -24905,7 +25037,16 @@ DeviceType.Script, method(object({
|
|
|
24905
25037
|
kind: "mutation",
|
|
24906
25038
|
auth: "admin"
|
|
24907
25039
|
});
|
|
24908
|
-
|
|
25040
|
+
/**
|
|
25041
|
+
* Smoke alarm sensor — boolean "is smoke currently detected" with
|
|
25042
|
+
* timestamp of the last transition. Drives Home Assistant
|
|
25043
|
+
* `binary_sensor` entries with `device_class: smoke`.
|
|
25044
|
+
*
|
|
25045
|
+
* Push-driven: a smoke event is critical, so the slice updates
|
|
25046
|
+
* immediately on the upstream signal. Auto-clearing back to false is
|
|
25047
|
+
* provider-controlled (some alarms latch until manually reset).
|
|
25048
|
+
*/
|
|
25049
|
+
var SmokeStatusSchema = object({
|
|
24909
25050
|
detected: boolean(),
|
|
24910
25051
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
24911
25052
|
lastChangedAt: number()
|
|
@@ -25114,7 +25255,23 @@ object({
|
|
|
25114
25255
|
lastChangedAt: number()
|
|
25115
25256
|
});
|
|
25116
25257
|
DeviceType.Sensor;
|
|
25117
|
-
|
|
25258
|
+
/**
|
|
25259
|
+
* Single-metric temperature reading. Drives Home Assistant `sensor`
|
|
25260
|
+
* entries with `device_class: temperature` and any future native
|
|
25261
|
+
* thermometer.
|
|
25262
|
+
*
|
|
25263
|
+
* Unit normalisation: providers convert to Celsius before storing.
|
|
25264
|
+
* The slice value is always Celsius so cross-cap aggregators
|
|
25265
|
+
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
25266
|
+
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
25267
|
+
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
25268
|
+
* `sourceInfo`.
|
|
25269
|
+
*
|
|
25270
|
+
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
25271
|
+
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
25272
|
+
* changed" steady state.
|
|
25273
|
+
*/
|
|
25274
|
+
var TemperatureSensorStatusSchema = object({
|
|
25118
25275
|
/** Current temperature in Celsius. */
|
|
25119
25276
|
celsius: number(),
|
|
25120
25277
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
@@ -31375,6 +31532,1678 @@ Object.freeze({
|
|
|
31375
31532
|
access: "create"
|
|
31376
31533
|
}
|
|
31377
31534
|
});
|
|
31535
|
+
Object.freeze({
|
|
31536
|
+
"accessories.setChildHidden": [{
|
|
31537
|
+
name: "childDeviceId",
|
|
31538
|
+
form: "single",
|
|
31539
|
+
optional: false
|
|
31540
|
+
}, {
|
|
31541
|
+
name: "deviceId",
|
|
31542
|
+
form: "single",
|
|
31543
|
+
optional: false
|
|
31544
|
+
}],
|
|
31545
|
+
"addonSettings.getDeviceSettings": [{
|
|
31546
|
+
name: "deviceId",
|
|
31547
|
+
form: "single",
|
|
31548
|
+
optional: false
|
|
31549
|
+
}],
|
|
31550
|
+
"addonSettings.updateDeviceSettings": [{
|
|
31551
|
+
name: "deviceId",
|
|
31552
|
+
form: "single",
|
|
31553
|
+
optional: false
|
|
31554
|
+
}],
|
|
31555
|
+
"alarmPanel.arm": [{
|
|
31556
|
+
name: "deviceId",
|
|
31557
|
+
form: "single",
|
|
31558
|
+
optional: false
|
|
31559
|
+
}],
|
|
31560
|
+
"alarmPanel.disarm": [{
|
|
31561
|
+
name: "deviceId",
|
|
31562
|
+
form: "single",
|
|
31563
|
+
optional: false
|
|
31564
|
+
}],
|
|
31565
|
+
"alarmPanel.trigger": [{
|
|
31566
|
+
name: "deviceId",
|
|
31567
|
+
form: "single",
|
|
31568
|
+
optional: false
|
|
31569
|
+
}],
|
|
31570
|
+
"audioAnalysis.resolveDeviceSettings": [{
|
|
31571
|
+
name: "deviceId",
|
|
31572
|
+
form: "single",
|
|
31573
|
+
optional: false
|
|
31574
|
+
}],
|
|
31575
|
+
"audioAnalyzer.classify": [{
|
|
31576
|
+
name: "deviceId",
|
|
31577
|
+
form: "single",
|
|
31578
|
+
optional: true
|
|
31579
|
+
}],
|
|
31580
|
+
"audioMetrics.getCurrentSnapshot": [{
|
|
31581
|
+
name: "deviceId",
|
|
31582
|
+
form: "single",
|
|
31583
|
+
optional: false
|
|
31584
|
+
}],
|
|
31585
|
+
"audioMetrics.getHistory": [{
|
|
31586
|
+
name: "deviceId",
|
|
31587
|
+
form: "single",
|
|
31588
|
+
optional: false
|
|
31589
|
+
}],
|
|
31590
|
+
"automationControl.disable": [{
|
|
31591
|
+
name: "deviceId",
|
|
31592
|
+
form: "single",
|
|
31593
|
+
optional: false
|
|
31594
|
+
}],
|
|
31595
|
+
"automationControl.enable": [{
|
|
31596
|
+
name: "deviceId",
|
|
31597
|
+
form: "single",
|
|
31598
|
+
optional: false
|
|
31599
|
+
}],
|
|
31600
|
+
"automationControl.trigger": [{
|
|
31601
|
+
name: "deviceId",
|
|
31602
|
+
form: "single",
|
|
31603
|
+
optional: false
|
|
31604
|
+
}],
|
|
31605
|
+
"battery.wakeForStream": [{
|
|
31606
|
+
name: "deviceId",
|
|
31607
|
+
form: "single",
|
|
31608
|
+
optional: false
|
|
31609
|
+
}],
|
|
31610
|
+
"brightness.setBrightness": [{
|
|
31611
|
+
name: "deviceId",
|
|
31612
|
+
form: "single",
|
|
31613
|
+
optional: false
|
|
31614
|
+
}],
|
|
31615
|
+
"button.press": [{
|
|
31616
|
+
name: "deviceId",
|
|
31617
|
+
form: "single",
|
|
31618
|
+
optional: false
|
|
31619
|
+
}],
|
|
31620
|
+
"cameraCredentials.getCredentials": [{
|
|
31621
|
+
name: "deviceId",
|
|
31622
|
+
form: "single",
|
|
31623
|
+
optional: false
|
|
31624
|
+
}],
|
|
31625
|
+
"cameraStreams.getBrokerStreams": [{
|
|
31626
|
+
name: "deviceId",
|
|
31627
|
+
form: "single",
|
|
31628
|
+
optional: false
|
|
31629
|
+
}],
|
|
31630
|
+
"cameraStreams.getCameraStreams": [{
|
|
31631
|
+
name: "deviceId",
|
|
31632
|
+
form: "single",
|
|
31633
|
+
optional: false
|
|
31634
|
+
}],
|
|
31635
|
+
"cameraStreams.getProfileRtspEntries": [{
|
|
31636
|
+
name: "deviceId",
|
|
31637
|
+
form: "single",
|
|
31638
|
+
optional: false
|
|
31639
|
+
}],
|
|
31640
|
+
"cameraStreams.getRtspEntries": [{
|
|
31641
|
+
name: "deviceId",
|
|
31642
|
+
form: "single",
|
|
31643
|
+
optional: false
|
|
31644
|
+
}],
|
|
31645
|
+
"cameraStreams.pickStream": [{
|
|
31646
|
+
name: "deviceId",
|
|
31647
|
+
form: "single",
|
|
31648
|
+
optional: false
|
|
31649
|
+
}],
|
|
31650
|
+
"climateControl.setFanMode": [{
|
|
31651
|
+
name: "deviceId",
|
|
31652
|
+
form: "single",
|
|
31653
|
+
optional: false
|
|
31654
|
+
}],
|
|
31655
|
+
"climateControl.setMode": [{
|
|
31656
|
+
name: "deviceId",
|
|
31657
|
+
form: "single",
|
|
31658
|
+
optional: false
|
|
31659
|
+
}],
|
|
31660
|
+
"climateControl.setPreset": [{
|
|
31661
|
+
name: "deviceId",
|
|
31662
|
+
form: "single",
|
|
31663
|
+
optional: false
|
|
31664
|
+
}],
|
|
31665
|
+
"climateControl.setSwingHorizontal": [{
|
|
31666
|
+
name: "deviceId",
|
|
31667
|
+
form: "single",
|
|
31668
|
+
optional: false
|
|
31669
|
+
}],
|
|
31670
|
+
"climateControl.setSwingVertical": [{
|
|
31671
|
+
name: "deviceId",
|
|
31672
|
+
form: "single",
|
|
31673
|
+
optional: false
|
|
31674
|
+
}],
|
|
31675
|
+
"climateControl.setTarget": [{
|
|
31676
|
+
name: "deviceId",
|
|
31677
|
+
form: "single",
|
|
31678
|
+
optional: false
|
|
31679
|
+
}],
|
|
31680
|
+
"climateControl.setTargetHumidity": [{
|
|
31681
|
+
name: "deviceId",
|
|
31682
|
+
form: "single",
|
|
31683
|
+
optional: false
|
|
31684
|
+
}],
|
|
31685
|
+
"climateControl.setTargetRange": [{
|
|
31686
|
+
name: "deviceId",
|
|
31687
|
+
form: "single",
|
|
31688
|
+
optional: false
|
|
31689
|
+
}],
|
|
31690
|
+
"color.setColor": [{
|
|
31691
|
+
name: "deviceId",
|
|
31692
|
+
form: "single",
|
|
31693
|
+
optional: false
|
|
31694
|
+
}],
|
|
31695
|
+
"consumables.reset": [{
|
|
31696
|
+
name: "deviceId",
|
|
31697
|
+
form: "single",
|
|
31698
|
+
optional: false
|
|
31699
|
+
}],
|
|
31700
|
+
"control.setValue": [{
|
|
31701
|
+
name: "deviceId",
|
|
31702
|
+
form: "single",
|
|
31703
|
+
optional: false
|
|
31704
|
+
}],
|
|
31705
|
+
"cover.close": [{
|
|
31706
|
+
name: "deviceId",
|
|
31707
|
+
form: "single",
|
|
31708
|
+
optional: false
|
|
31709
|
+
}],
|
|
31710
|
+
"cover.open": [{
|
|
31711
|
+
name: "deviceId",
|
|
31712
|
+
form: "single",
|
|
31713
|
+
optional: false
|
|
31714
|
+
}],
|
|
31715
|
+
"cover.setPosition": [{
|
|
31716
|
+
name: "deviceId",
|
|
31717
|
+
form: "single",
|
|
31718
|
+
optional: false
|
|
31719
|
+
}],
|
|
31720
|
+
"cover.setTiltPosition": [{
|
|
31721
|
+
name: "deviceId",
|
|
31722
|
+
form: "single",
|
|
31723
|
+
optional: false
|
|
31724
|
+
}],
|
|
31725
|
+
"cover.stop": [{
|
|
31726
|
+
name: "deviceId",
|
|
31727
|
+
form: "single",
|
|
31728
|
+
optional: false
|
|
31729
|
+
}],
|
|
31730
|
+
"dayNight.getOptions": [{
|
|
31731
|
+
name: "deviceId",
|
|
31732
|
+
form: "single",
|
|
31733
|
+
optional: false
|
|
31734
|
+
}],
|
|
31735
|
+
"dayNight.setSettings": [{
|
|
31736
|
+
name: "deviceId",
|
|
31737
|
+
form: "single",
|
|
31738
|
+
optional: false
|
|
31739
|
+
}],
|
|
31740
|
+
"decoder.createSession": [{
|
|
31741
|
+
name: "deviceId",
|
|
31742
|
+
form: "single",
|
|
31743
|
+
optional: true
|
|
31744
|
+
}],
|
|
31745
|
+
"deviceAdoption.release": [{
|
|
31746
|
+
name: "camDeviceId",
|
|
31747
|
+
form: "single",
|
|
31748
|
+
optional: false
|
|
31749
|
+
}],
|
|
31750
|
+
"deviceAdoption.resync": [{
|
|
31751
|
+
name: "camDeviceId",
|
|
31752
|
+
form: "single",
|
|
31753
|
+
optional: false
|
|
31754
|
+
}],
|
|
31755
|
+
"deviceDiscovery.adoptDevice": [{
|
|
31756
|
+
name: "deviceId",
|
|
31757
|
+
form: "single",
|
|
31758
|
+
optional: false
|
|
31759
|
+
}],
|
|
31760
|
+
"deviceDiscovery.listDiscovered": [{
|
|
31761
|
+
name: "deviceId",
|
|
31762
|
+
form: "single",
|
|
31763
|
+
optional: false
|
|
31764
|
+
}],
|
|
31765
|
+
"deviceDiscovery.refreshDiscovery": [{
|
|
31766
|
+
name: "deviceId",
|
|
31767
|
+
form: "single",
|
|
31768
|
+
optional: false
|
|
31769
|
+
}],
|
|
31770
|
+
"deviceDiscovery.releaseDevice": [{
|
|
31771
|
+
name: "childDeviceId",
|
|
31772
|
+
form: "single",
|
|
31773
|
+
optional: false
|
|
31774
|
+
}, {
|
|
31775
|
+
name: "deviceId",
|
|
31776
|
+
form: "single",
|
|
31777
|
+
optional: false
|
|
31778
|
+
}],
|
|
31779
|
+
"deviceManager.adoptionRelease": [{
|
|
31780
|
+
name: "camDeviceId",
|
|
31781
|
+
form: "single",
|
|
31782
|
+
optional: false
|
|
31783
|
+
}],
|
|
31784
|
+
"deviceManager.adoptionResync": [{
|
|
31785
|
+
name: "camDeviceId",
|
|
31786
|
+
form: "single",
|
|
31787
|
+
optional: false
|
|
31788
|
+
}],
|
|
31789
|
+
"deviceManager.applyInitialMeta": [{
|
|
31790
|
+
name: "deviceId",
|
|
31791
|
+
form: "single",
|
|
31792
|
+
optional: false
|
|
31793
|
+
}, {
|
|
31794
|
+
name: "linkDeviceId",
|
|
31795
|
+
form: "single",
|
|
31796
|
+
optional: true
|
|
31797
|
+
}],
|
|
31798
|
+
"deviceManager.disable": [{
|
|
31799
|
+
name: "deviceId",
|
|
31800
|
+
form: "single",
|
|
31801
|
+
optional: false
|
|
31802
|
+
}],
|
|
31803
|
+
"deviceManager.enable": [{
|
|
31804
|
+
name: "deviceId",
|
|
31805
|
+
form: "single",
|
|
31806
|
+
optional: false
|
|
31807
|
+
}],
|
|
31808
|
+
"deviceManager.getBindings": [{
|
|
31809
|
+
name: "deviceId",
|
|
31810
|
+
form: "single",
|
|
31811
|
+
optional: false
|
|
31812
|
+
}],
|
|
31813
|
+
"deviceManager.getChildren": [{
|
|
31814
|
+
name: "parentDeviceId",
|
|
31815
|
+
form: "single",
|
|
31816
|
+
optional: false
|
|
31817
|
+
}],
|
|
31818
|
+
"deviceManager.getConfigSchema": [{
|
|
31819
|
+
name: "deviceId",
|
|
31820
|
+
form: "single",
|
|
31821
|
+
optional: false
|
|
31822
|
+
}],
|
|
31823
|
+
"deviceManager.getDevice": [{
|
|
31824
|
+
name: "deviceId",
|
|
31825
|
+
form: "single",
|
|
31826
|
+
optional: false
|
|
31827
|
+
}],
|
|
31828
|
+
"deviceManager.getDeviceAggregate": [{
|
|
31829
|
+
name: "deviceId",
|
|
31830
|
+
form: "single",
|
|
31831
|
+
optional: false
|
|
31832
|
+
}],
|
|
31833
|
+
"deviceManager.getDeviceLiveInfoAggregate": [{
|
|
31834
|
+
name: "deviceId",
|
|
31835
|
+
form: "single",
|
|
31836
|
+
optional: false
|
|
31837
|
+
}],
|
|
31838
|
+
"deviceManager.getDeviceSettingsAggregate": [{
|
|
31839
|
+
name: "deviceId",
|
|
31840
|
+
form: "single",
|
|
31841
|
+
optional: false
|
|
31842
|
+
}],
|
|
31843
|
+
"deviceManager.getDeviceStatusAggregate": [{
|
|
31844
|
+
name: "deviceId",
|
|
31845
|
+
form: "single",
|
|
31846
|
+
optional: false
|
|
31847
|
+
}],
|
|
31848
|
+
"deviceManager.getDeviceStatusAggregateBatch": [{
|
|
31849
|
+
name: "deviceIds",
|
|
31850
|
+
form: "array",
|
|
31851
|
+
optional: false
|
|
31852
|
+
}],
|
|
31853
|
+
"deviceManager.getLinkedDevices": [{
|
|
31854
|
+
name: "deviceId",
|
|
31855
|
+
form: "single",
|
|
31856
|
+
optional: false
|
|
31857
|
+
}],
|
|
31858
|
+
"deviceManager.getSettingsSchema": [{
|
|
31859
|
+
name: "deviceId",
|
|
31860
|
+
form: "single",
|
|
31861
|
+
optional: false
|
|
31862
|
+
}],
|
|
31863
|
+
"deviceManager.getStreamProfileMap": [{
|
|
31864
|
+
name: "deviceId",
|
|
31865
|
+
form: "single",
|
|
31866
|
+
optional: false
|
|
31867
|
+
}],
|
|
31868
|
+
"deviceManager.getStreamSources": [{
|
|
31869
|
+
name: "deviceId",
|
|
31870
|
+
form: "single",
|
|
31871
|
+
optional: false
|
|
31872
|
+
}],
|
|
31873
|
+
"deviceManager.getWireableFields": [{
|
|
31874
|
+
name: "deviceId",
|
|
31875
|
+
form: "single",
|
|
31876
|
+
optional: false
|
|
31877
|
+
}],
|
|
31878
|
+
"deviceManager.loadConfig": [{
|
|
31879
|
+
name: "deviceId",
|
|
31880
|
+
form: "single",
|
|
31881
|
+
optional: false
|
|
31882
|
+
}],
|
|
31883
|
+
"deviceManager.loadMeta": [{
|
|
31884
|
+
name: "deviceId",
|
|
31885
|
+
form: "single",
|
|
31886
|
+
optional: false
|
|
31887
|
+
}],
|
|
31888
|
+
"deviceManager.loadRuntimeState": [{
|
|
31889
|
+
name: "deviceId",
|
|
31890
|
+
form: "single",
|
|
31891
|
+
optional: false
|
|
31892
|
+
}],
|
|
31893
|
+
"deviceManager.persistConfig": [{
|
|
31894
|
+
name: "deviceId",
|
|
31895
|
+
form: "single",
|
|
31896
|
+
optional: false
|
|
31897
|
+
}],
|
|
31898
|
+
"deviceManager.probeStreams": [{
|
|
31899
|
+
name: "deviceId",
|
|
31900
|
+
form: "single",
|
|
31901
|
+
optional: false
|
|
31902
|
+
}],
|
|
31903
|
+
"deviceManager.registerDevice": [{
|
|
31904
|
+
name: "parentDeviceId",
|
|
31905
|
+
form: "single",
|
|
31906
|
+
optional: true
|
|
31907
|
+
}],
|
|
31908
|
+
"deviceManager.remove": [{
|
|
31909
|
+
name: "deviceId",
|
|
31910
|
+
form: "single",
|
|
31911
|
+
optional: false
|
|
31912
|
+
}],
|
|
31913
|
+
"deviceManager.removeDevice": [{
|
|
31914
|
+
name: "deviceId",
|
|
31915
|
+
form: "single",
|
|
31916
|
+
optional: false
|
|
31917
|
+
}],
|
|
31918
|
+
"deviceManager.runDeviceAction": [{
|
|
31919
|
+
name: "deviceId",
|
|
31920
|
+
form: "single",
|
|
31921
|
+
optional: false
|
|
31922
|
+
}],
|
|
31923
|
+
"deviceManager.setChildLayout": [{
|
|
31924
|
+
name: "deviceId",
|
|
31925
|
+
form: "single",
|
|
31926
|
+
optional: false
|
|
31927
|
+
}],
|
|
31928
|
+
"deviceManager.setDisabled": [{
|
|
31929
|
+
name: "deviceId",
|
|
31930
|
+
form: "single",
|
|
31931
|
+
optional: false
|
|
31932
|
+
}],
|
|
31933
|
+
"deviceManager.setDisplay": [{
|
|
31934
|
+
name: "deviceId",
|
|
31935
|
+
form: "single",
|
|
31936
|
+
optional: false
|
|
31937
|
+
}],
|
|
31938
|
+
"deviceManager.setIntegrationId": [{
|
|
31939
|
+
name: "deviceId",
|
|
31940
|
+
form: "single",
|
|
31941
|
+
optional: false
|
|
31942
|
+
}],
|
|
31943
|
+
"deviceManager.setLinkDeviceId": [{
|
|
31944
|
+
name: "deviceId",
|
|
31945
|
+
form: "single",
|
|
31946
|
+
optional: false
|
|
31947
|
+
}, {
|
|
31948
|
+
name: "linkDeviceId",
|
|
31949
|
+
form: "single",
|
|
31950
|
+
optional: true
|
|
31951
|
+
}],
|
|
31952
|
+
"deviceManager.setLocation": [{
|
|
31953
|
+
name: "deviceId",
|
|
31954
|
+
form: "single",
|
|
31955
|
+
optional: false
|
|
31956
|
+
}],
|
|
31957
|
+
"deviceManager.setMetadata": [{
|
|
31958
|
+
name: "deviceId",
|
|
31959
|
+
form: "single",
|
|
31960
|
+
optional: false
|
|
31961
|
+
}],
|
|
31962
|
+
"deviceManager.setName": [{
|
|
31963
|
+
name: "deviceId",
|
|
31964
|
+
form: "single",
|
|
31965
|
+
optional: false
|
|
31966
|
+
}],
|
|
31967
|
+
"deviceManager.setPrimaryChildEntityId": [{
|
|
31968
|
+
name: "deviceId",
|
|
31969
|
+
form: "single",
|
|
31970
|
+
optional: false
|
|
31971
|
+
}],
|
|
31972
|
+
"deviceManager.setRole": [{
|
|
31973
|
+
name: "deviceId",
|
|
31974
|
+
form: "single",
|
|
31975
|
+
optional: false
|
|
31976
|
+
}],
|
|
31977
|
+
"deviceManager.setStreamProfileMap": [{
|
|
31978
|
+
name: "deviceId",
|
|
31979
|
+
form: "single",
|
|
31980
|
+
optional: false
|
|
31981
|
+
}],
|
|
31982
|
+
"deviceManager.setType": [{
|
|
31983
|
+
name: "deviceId",
|
|
31984
|
+
form: "single",
|
|
31985
|
+
optional: false
|
|
31986
|
+
}],
|
|
31987
|
+
"deviceManager.setWrapperActive": [{
|
|
31988
|
+
name: "deviceId",
|
|
31989
|
+
form: "single",
|
|
31990
|
+
optional: false
|
|
31991
|
+
}],
|
|
31992
|
+
"deviceManager.testField": [{
|
|
31993
|
+
name: "deviceId",
|
|
31994
|
+
form: "single",
|
|
31995
|
+
optional: false
|
|
31996
|
+
}],
|
|
31997
|
+
"deviceManager.updateConfig": [{
|
|
31998
|
+
name: "deviceId",
|
|
31999
|
+
form: "single",
|
|
32000
|
+
optional: false
|
|
32001
|
+
}],
|
|
32002
|
+
"deviceManager.updateDeviceField": [{
|
|
32003
|
+
name: "deviceId",
|
|
32004
|
+
form: "single",
|
|
32005
|
+
optional: false
|
|
32006
|
+
}],
|
|
32007
|
+
"deviceManager.updateDeviceFieldsBatch": [{
|
|
32008
|
+
name: "deviceId",
|
|
32009
|
+
form: "single",
|
|
32010
|
+
optional: false
|
|
32011
|
+
}],
|
|
32012
|
+
"deviceOps.getConfigEntries": [{
|
|
32013
|
+
name: "deviceId",
|
|
32014
|
+
form: "single",
|
|
32015
|
+
optional: false
|
|
32016
|
+
}],
|
|
32017
|
+
"deviceOps.getRawState": [{
|
|
32018
|
+
name: "deviceId",
|
|
32019
|
+
form: "single",
|
|
32020
|
+
optional: false
|
|
32021
|
+
}],
|
|
32022
|
+
"deviceOps.getSettingsSchema": [{
|
|
32023
|
+
name: "deviceId",
|
|
32024
|
+
form: "single",
|
|
32025
|
+
optional: false
|
|
32026
|
+
}],
|
|
32027
|
+
"deviceOps.getStreamSources": [{
|
|
32028
|
+
name: "deviceId",
|
|
32029
|
+
form: "single",
|
|
32030
|
+
optional: false
|
|
32031
|
+
}],
|
|
32032
|
+
"deviceOps.removeDevice": [{
|
|
32033
|
+
name: "deviceId",
|
|
32034
|
+
form: "single",
|
|
32035
|
+
optional: false
|
|
32036
|
+
}],
|
|
32037
|
+
"deviceOps.runAction": [{
|
|
32038
|
+
name: "deviceId",
|
|
32039
|
+
form: "single",
|
|
32040
|
+
optional: false
|
|
32041
|
+
}],
|
|
32042
|
+
"deviceOps.setConfig": [{
|
|
32043
|
+
name: "deviceId",
|
|
32044
|
+
form: "single",
|
|
32045
|
+
optional: false
|
|
32046
|
+
}],
|
|
32047
|
+
"deviceState.getCapSlice": [{
|
|
32048
|
+
name: "deviceId",
|
|
32049
|
+
form: "single",
|
|
32050
|
+
optional: false
|
|
32051
|
+
}],
|
|
32052
|
+
"deviceState.getSnapshot": [{
|
|
32053
|
+
name: "deviceId",
|
|
32054
|
+
form: "single",
|
|
32055
|
+
optional: false
|
|
32056
|
+
}],
|
|
32057
|
+
"deviceState.setCapSlice": [{
|
|
32058
|
+
name: "deviceId",
|
|
32059
|
+
form: "single",
|
|
32060
|
+
optional: false
|
|
32061
|
+
}],
|
|
32062
|
+
"events.getEventClipUrl": [{
|
|
32063
|
+
name: "deviceId",
|
|
32064
|
+
form: "single",
|
|
32065
|
+
optional: false
|
|
32066
|
+
}],
|
|
32067
|
+
"events.getEvents": [{
|
|
32068
|
+
name: "deviceId",
|
|
32069
|
+
form: "single",
|
|
32070
|
+
optional: false
|
|
32071
|
+
}],
|
|
32072
|
+
"events.getEventThumbnail": [{
|
|
32073
|
+
name: "deviceId",
|
|
32074
|
+
form: "single",
|
|
32075
|
+
optional: false
|
|
32076
|
+
}],
|
|
32077
|
+
"faceGallery.getFaceByTrack": [{
|
|
32078
|
+
name: "deviceId",
|
|
32079
|
+
form: "single",
|
|
32080
|
+
optional: false
|
|
32081
|
+
}],
|
|
32082
|
+
"faceGallery.listRecentFaces": [{
|
|
32083
|
+
name: "deviceId",
|
|
32084
|
+
form: "single",
|
|
32085
|
+
optional: true
|
|
32086
|
+
}],
|
|
32087
|
+
"fanControl.setDirection": [{
|
|
32088
|
+
name: "deviceId",
|
|
32089
|
+
form: "single",
|
|
32090
|
+
optional: false
|
|
32091
|
+
}],
|
|
32092
|
+
"fanControl.setOscillating": [{
|
|
32093
|
+
name: "deviceId",
|
|
32094
|
+
form: "single",
|
|
32095
|
+
optional: false
|
|
32096
|
+
}],
|
|
32097
|
+
"fanControl.setPercentage": [{
|
|
32098
|
+
name: "deviceId",
|
|
32099
|
+
form: "single",
|
|
32100
|
+
optional: false
|
|
32101
|
+
}],
|
|
32102
|
+
"fanControl.setPreset": [{
|
|
32103
|
+
name: "deviceId",
|
|
32104
|
+
form: "single",
|
|
32105
|
+
optional: false
|
|
32106
|
+
}],
|
|
32107
|
+
"humidifier.setMode": [{
|
|
32108
|
+
name: "deviceId",
|
|
32109
|
+
form: "single",
|
|
32110
|
+
optional: false
|
|
32111
|
+
}],
|
|
32112
|
+
"humidifier.setOn": [{
|
|
32113
|
+
name: "deviceId",
|
|
32114
|
+
form: "single",
|
|
32115
|
+
optional: false
|
|
32116
|
+
}],
|
|
32117
|
+
"humidifier.setTargetHumidity": [{
|
|
32118
|
+
name: "deviceId",
|
|
32119
|
+
form: "single",
|
|
32120
|
+
optional: false
|
|
32121
|
+
}],
|
|
32122
|
+
"imageSettings.getOptions": [{
|
|
32123
|
+
name: "deviceId",
|
|
32124
|
+
form: "single",
|
|
32125
|
+
optional: false
|
|
32126
|
+
}],
|
|
32127
|
+
"imageSettings.setSettings": [{
|
|
32128
|
+
name: "deviceId",
|
|
32129
|
+
form: "single",
|
|
32130
|
+
optional: false
|
|
32131
|
+
}],
|
|
32132
|
+
"intercom.endTalkSession": [{
|
|
32133
|
+
name: "deviceId",
|
|
32134
|
+
form: "single",
|
|
32135
|
+
optional: false
|
|
32136
|
+
}],
|
|
32137
|
+
"intercom.handleAnswer": [{
|
|
32138
|
+
name: "deviceId",
|
|
32139
|
+
form: "single",
|
|
32140
|
+
optional: false
|
|
32141
|
+
}],
|
|
32142
|
+
"intercom.pushTalkAudio": [{
|
|
32143
|
+
name: "deviceId",
|
|
32144
|
+
form: "single",
|
|
32145
|
+
optional: false
|
|
32146
|
+
}],
|
|
32147
|
+
"intercom.startSession": [{
|
|
32148
|
+
name: "deviceId",
|
|
32149
|
+
form: "single",
|
|
32150
|
+
optional: false
|
|
32151
|
+
}],
|
|
32152
|
+
"intercom.startTalkSession": [{
|
|
32153
|
+
name: "deviceId",
|
|
32154
|
+
form: "single",
|
|
32155
|
+
optional: false
|
|
32156
|
+
}],
|
|
32157
|
+
"intercom.stopSession": [{
|
|
32158
|
+
name: "deviceId",
|
|
32159
|
+
form: "single",
|
|
32160
|
+
optional: false
|
|
32161
|
+
}],
|
|
32162
|
+
"lawnMowerControl.dock": [{
|
|
32163
|
+
name: "deviceId",
|
|
32164
|
+
form: "single",
|
|
32165
|
+
optional: false
|
|
32166
|
+
}],
|
|
32167
|
+
"lawnMowerControl.pause": [{
|
|
32168
|
+
name: "deviceId",
|
|
32169
|
+
form: "single",
|
|
32170
|
+
optional: false
|
|
32171
|
+
}],
|
|
32172
|
+
"lawnMowerControl.startMowing": [{
|
|
32173
|
+
name: "deviceId",
|
|
32174
|
+
form: "single",
|
|
32175
|
+
optional: false
|
|
32176
|
+
}],
|
|
32177
|
+
"lockControl.lock": [{
|
|
32178
|
+
name: "deviceId",
|
|
32179
|
+
form: "single",
|
|
32180
|
+
optional: false
|
|
32181
|
+
}],
|
|
32182
|
+
"lockControl.open": [{
|
|
32183
|
+
name: "deviceId",
|
|
32184
|
+
form: "single",
|
|
32185
|
+
optional: false
|
|
32186
|
+
}],
|
|
32187
|
+
"lockControl.unlock": [{
|
|
32188
|
+
name: "deviceId",
|
|
32189
|
+
form: "single",
|
|
32190
|
+
optional: false
|
|
32191
|
+
}],
|
|
32192
|
+
"mediaPlayer.next": [{
|
|
32193
|
+
name: "deviceId",
|
|
32194
|
+
form: "single",
|
|
32195
|
+
optional: false
|
|
32196
|
+
}],
|
|
32197
|
+
"mediaPlayer.pause": [{
|
|
32198
|
+
name: "deviceId",
|
|
32199
|
+
form: "single",
|
|
32200
|
+
optional: false
|
|
32201
|
+
}],
|
|
32202
|
+
"mediaPlayer.play": [{
|
|
32203
|
+
name: "deviceId",
|
|
32204
|
+
form: "single",
|
|
32205
|
+
optional: false
|
|
32206
|
+
}],
|
|
32207
|
+
"mediaPlayer.playMedia": [{
|
|
32208
|
+
name: "deviceId",
|
|
32209
|
+
form: "single",
|
|
32210
|
+
optional: false
|
|
32211
|
+
}],
|
|
32212
|
+
"mediaPlayer.previous": [{
|
|
32213
|
+
name: "deviceId",
|
|
32214
|
+
form: "single",
|
|
32215
|
+
optional: false
|
|
32216
|
+
}],
|
|
32217
|
+
"mediaPlayer.seek": [{
|
|
32218
|
+
name: "deviceId",
|
|
32219
|
+
form: "single",
|
|
32220
|
+
optional: false
|
|
32221
|
+
}],
|
|
32222
|
+
"mediaPlayer.selectSource": [{
|
|
32223
|
+
name: "deviceId",
|
|
32224
|
+
form: "single",
|
|
32225
|
+
optional: false
|
|
32226
|
+
}],
|
|
32227
|
+
"mediaPlayer.setMute": [{
|
|
32228
|
+
name: "deviceId",
|
|
32229
|
+
form: "single",
|
|
32230
|
+
optional: false
|
|
32231
|
+
}],
|
|
32232
|
+
"mediaPlayer.setRepeat": [{
|
|
32233
|
+
name: "deviceId",
|
|
32234
|
+
form: "single",
|
|
32235
|
+
optional: false
|
|
32236
|
+
}],
|
|
32237
|
+
"mediaPlayer.setShuffle": [{
|
|
32238
|
+
name: "deviceId",
|
|
32239
|
+
form: "single",
|
|
32240
|
+
optional: false
|
|
32241
|
+
}],
|
|
32242
|
+
"mediaPlayer.setVolume": [{
|
|
32243
|
+
name: "deviceId",
|
|
32244
|
+
form: "single",
|
|
32245
|
+
optional: false
|
|
32246
|
+
}],
|
|
32247
|
+
"mediaPlayer.stop": [{
|
|
32248
|
+
name: "deviceId",
|
|
32249
|
+
form: "single",
|
|
32250
|
+
optional: false
|
|
32251
|
+
}],
|
|
32252
|
+
"motion.isDetected": [{
|
|
32253
|
+
name: "deviceId",
|
|
32254
|
+
form: "single",
|
|
32255
|
+
optional: false
|
|
32256
|
+
}],
|
|
32257
|
+
"motionDetection.analyze": [{
|
|
32258
|
+
name: "deviceId",
|
|
32259
|
+
form: "single",
|
|
32260
|
+
optional: false
|
|
32261
|
+
}],
|
|
32262
|
+
"motionDetection.removeCamera": [{
|
|
32263
|
+
name: "deviceId",
|
|
32264
|
+
form: "single",
|
|
32265
|
+
optional: false
|
|
32266
|
+
}],
|
|
32267
|
+
"motionTrigger.setMotionTrigger": [{
|
|
32268
|
+
name: "deviceId",
|
|
32269
|
+
form: "single",
|
|
32270
|
+
optional: false
|
|
32271
|
+
}],
|
|
32272
|
+
"motionZones.getOptions": [{
|
|
32273
|
+
name: "deviceId",
|
|
32274
|
+
form: "single",
|
|
32275
|
+
optional: false
|
|
32276
|
+
}],
|
|
32277
|
+
"motionZones.setZone": [{
|
|
32278
|
+
name: "deviceId",
|
|
32279
|
+
form: "single",
|
|
32280
|
+
optional: false
|
|
32281
|
+
}],
|
|
32282
|
+
"nativeObjectDetection.setEnabled": [{
|
|
32283
|
+
name: "deviceId",
|
|
32284
|
+
form: "single",
|
|
32285
|
+
optional: false
|
|
32286
|
+
}],
|
|
32287
|
+
"networkQuality.getDeviceStats": [{
|
|
32288
|
+
name: "deviceId",
|
|
32289
|
+
form: "single",
|
|
32290
|
+
optional: false
|
|
32291
|
+
}],
|
|
32292
|
+
"networkQuality.reportClientStats": [{
|
|
32293
|
+
name: "deviceId",
|
|
32294
|
+
form: "single",
|
|
32295
|
+
optional: false
|
|
32296
|
+
}],
|
|
32297
|
+
"notificationRules.setDeviceMuted": [{
|
|
32298
|
+
name: "deviceId",
|
|
32299
|
+
form: "single",
|
|
32300
|
+
optional: false
|
|
32301
|
+
}],
|
|
32302
|
+
"notifier.cancel": [{
|
|
32303
|
+
name: "deviceId",
|
|
32304
|
+
form: "single",
|
|
32305
|
+
optional: false
|
|
32306
|
+
}],
|
|
32307
|
+
"notifier.send": [{
|
|
32308
|
+
name: "deviceId",
|
|
32309
|
+
form: "single",
|
|
32310
|
+
optional: false
|
|
32311
|
+
}],
|
|
32312
|
+
"osd.setOverlay": [{
|
|
32313
|
+
name: "deviceId",
|
|
32314
|
+
form: "single",
|
|
32315
|
+
optional: false
|
|
32316
|
+
}],
|
|
32317
|
+
"osdManager.clearSlotBinding": [{
|
|
32318
|
+
name: "deviceId",
|
|
32319
|
+
form: "single",
|
|
32320
|
+
optional: false
|
|
32321
|
+
}],
|
|
32322
|
+
"osdManager.copyDeviceConfiguration": [{
|
|
32323
|
+
name: "sourceDeviceId",
|
|
32324
|
+
form: "single",
|
|
32325
|
+
optional: false
|
|
32326
|
+
}, {
|
|
32327
|
+
name: "targetDeviceId",
|
|
32328
|
+
form: "single",
|
|
32329
|
+
optional: false
|
|
32330
|
+
}],
|
|
32331
|
+
"osdManager.getDeviceOsd": [{
|
|
32332
|
+
name: "deviceId",
|
|
32333
|
+
form: "single",
|
|
32334
|
+
optional: false
|
|
32335
|
+
}],
|
|
32336
|
+
"osdManager.getSourceCatalog": [{
|
|
32337
|
+
name: "deviceId",
|
|
32338
|
+
form: "single",
|
|
32339
|
+
optional: false
|
|
32340
|
+
}],
|
|
32341
|
+
"osdManager.previewSlot": [{
|
|
32342
|
+
name: "deviceId",
|
|
32343
|
+
form: "single",
|
|
32344
|
+
optional: false
|
|
32345
|
+
}],
|
|
32346
|
+
"osdManager.renderDevice": [{
|
|
32347
|
+
name: "deviceId",
|
|
32348
|
+
form: "single",
|
|
32349
|
+
optional: false
|
|
32350
|
+
}],
|
|
32351
|
+
"osdManager.setSlotBinding": [{
|
|
32352
|
+
name: "deviceId",
|
|
32353
|
+
form: "single",
|
|
32354
|
+
optional: false
|
|
32355
|
+
}],
|
|
32356
|
+
"petFeeder.callPet": [{
|
|
32357
|
+
name: "deviceId",
|
|
32358
|
+
form: "single",
|
|
32359
|
+
optional: false
|
|
32360
|
+
}],
|
|
32361
|
+
"petFeeder.cancelFeed": [{
|
|
32362
|
+
name: "deviceId",
|
|
32363
|
+
form: "single",
|
|
32364
|
+
optional: false
|
|
32365
|
+
}],
|
|
32366
|
+
"petFeeder.feed": [{
|
|
32367
|
+
name: "deviceId",
|
|
32368
|
+
form: "single",
|
|
32369
|
+
optional: false
|
|
32370
|
+
}],
|
|
32371
|
+
"petFeeder.markFoodReplenished": [{
|
|
32372
|
+
name: "deviceId",
|
|
32373
|
+
form: "single",
|
|
32374
|
+
optional: false
|
|
32375
|
+
}],
|
|
32376
|
+
"petFeeder.playSound": [{
|
|
32377
|
+
name: "deviceId",
|
|
32378
|
+
form: "single",
|
|
32379
|
+
optional: false
|
|
32380
|
+
}],
|
|
32381
|
+
"petFeeder.resetDesiccant": [{
|
|
32382
|
+
name: "deviceId",
|
|
32383
|
+
form: "single",
|
|
32384
|
+
optional: false
|
|
32385
|
+
}],
|
|
32386
|
+
"petFeeder.setChildLock": [{
|
|
32387
|
+
name: "deviceId",
|
|
32388
|
+
form: "single",
|
|
32389
|
+
optional: false
|
|
32390
|
+
}],
|
|
32391
|
+
"petFeeder.setFeedSound": [{
|
|
32392
|
+
name: "deviceId",
|
|
32393
|
+
form: "single",
|
|
32394
|
+
optional: false
|
|
32395
|
+
}],
|
|
32396
|
+
"petFeeder.setIndicatorLight": [{
|
|
32397
|
+
name: "deviceId",
|
|
32398
|
+
form: "single",
|
|
32399
|
+
optional: false
|
|
32400
|
+
}],
|
|
32401
|
+
"petFeeder.setVolume": [{
|
|
32402
|
+
name: "deviceId",
|
|
32403
|
+
form: "single",
|
|
32404
|
+
optional: false
|
|
32405
|
+
}],
|
|
32406
|
+
"pipelineAnalytics.clearTracks": [{
|
|
32407
|
+
name: "deviceId",
|
|
32408
|
+
form: "single",
|
|
32409
|
+
optional: false
|
|
32410
|
+
}],
|
|
32411
|
+
"pipelineAnalytics.completeRetrainTrack": [{
|
|
32412
|
+
name: "deviceId",
|
|
32413
|
+
form: "single",
|
|
32414
|
+
optional: false
|
|
32415
|
+
}],
|
|
32416
|
+
"pipelineAnalytics.deleteDeviceEvents": [{
|
|
32417
|
+
name: "deviceId",
|
|
32418
|
+
form: "single",
|
|
32419
|
+
optional: false
|
|
32420
|
+
}],
|
|
32421
|
+
"pipelineAnalytics.deleteTracks": [{
|
|
32422
|
+
name: "deviceId",
|
|
32423
|
+
form: "single",
|
|
32424
|
+
optional: false
|
|
32425
|
+
}],
|
|
32426
|
+
"pipelineAnalytics.deselectRetrainFrame": [{
|
|
32427
|
+
name: "deviceId",
|
|
32428
|
+
form: "single",
|
|
32429
|
+
optional: false
|
|
32430
|
+
}],
|
|
32431
|
+
"pipelineAnalytics.getActiveTracks": [{
|
|
32432
|
+
name: "deviceId",
|
|
32433
|
+
form: "single",
|
|
32434
|
+
optional: false
|
|
32435
|
+
}],
|
|
32436
|
+
"pipelineAnalytics.getAudioEvents": [{
|
|
32437
|
+
name: "deviceId",
|
|
32438
|
+
form: "single",
|
|
32439
|
+
optional: false
|
|
32440
|
+
}],
|
|
32441
|
+
"pipelineAnalytics.getEventDensity": [{
|
|
32442
|
+
name: "deviceId",
|
|
32443
|
+
form: "single",
|
|
32444
|
+
optional: false
|
|
32445
|
+
}],
|
|
32446
|
+
"pipelineAnalytics.getKeyEvents": [{
|
|
32447
|
+
name: "deviceId",
|
|
32448
|
+
form: "single",
|
|
32449
|
+
optional: false
|
|
32450
|
+
}],
|
|
32451
|
+
"pipelineAnalytics.getMotionEvents": [{
|
|
32452
|
+
name: "deviceId",
|
|
32453
|
+
form: "single",
|
|
32454
|
+
optional: false
|
|
32455
|
+
}],
|
|
32456
|
+
"pipelineAnalytics.getObjectEvents": [{
|
|
32457
|
+
name: "deviceId",
|
|
32458
|
+
form: "single",
|
|
32459
|
+
optional: false
|
|
32460
|
+
}],
|
|
32461
|
+
"pipelineAnalytics.getRetrainExportUrl": [{
|
|
32462
|
+
name: "deviceIds",
|
|
32463
|
+
form: "array",
|
|
32464
|
+
optional: true
|
|
32465
|
+
}],
|
|
32466
|
+
"pipelineAnalytics.getSensorEvents": [{
|
|
32467
|
+
name: "deviceId",
|
|
32468
|
+
form: "single",
|
|
32469
|
+
optional: false
|
|
32470
|
+
}],
|
|
32471
|
+
"pipelineAnalytics.getTrack": [{
|
|
32472
|
+
name: "deviceId",
|
|
32473
|
+
form: "single",
|
|
32474
|
+
optional: false
|
|
32475
|
+
}],
|
|
32476
|
+
"pipelineAnalytics.getTrainingExportSummary": [{
|
|
32477
|
+
name: "deviceIds",
|
|
32478
|
+
form: "array",
|
|
32479
|
+
optional: true
|
|
32480
|
+
}],
|
|
32481
|
+
"pipelineAnalytics.getTrainingExportUrl": [{
|
|
32482
|
+
name: "deviceIds",
|
|
32483
|
+
form: "array",
|
|
32484
|
+
optional: true
|
|
32485
|
+
}],
|
|
32486
|
+
"pipelineAnalytics.listEventKinds": [{
|
|
32487
|
+
name: "deviceId",
|
|
32488
|
+
form: "single",
|
|
32489
|
+
optional: false
|
|
32490
|
+
}],
|
|
32491
|
+
"pipelineAnalytics.listEventKindsBatch": [{
|
|
32492
|
+
name: "deviceIds",
|
|
32493
|
+
form: "array",
|
|
32494
|
+
optional: false
|
|
32495
|
+
}],
|
|
32496
|
+
"pipelineAnalytics.listOpsLog": [{
|
|
32497
|
+
name: "deviceId",
|
|
32498
|
+
form: "single",
|
|
32499
|
+
optional: true
|
|
32500
|
+
}],
|
|
32501
|
+
"pipelineAnalytics.listRecentTracks": [{
|
|
32502
|
+
name: "deviceIds",
|
|
32503
|
+
form: "array",
|
|
32504
|
+
optional: false
|
|
32505
|
+
}],
|
|
32506
|
+
"pipelineAnalytics.listRetrainStaging": [{
|
|
32507
|
+
name: "deviceIds",
|
|
32508
|
+
form: "array",
|
|
32509
|
+
optional: true
|
|
32510
|
+
}],
|
|
32511
|
+
"pipelineAnalytics.listTracks": [{
|
|
32512
|
+
name: "deviceId",
|
|
32513
|
+
form: "single",
|
|
32514
|
+
optional: false
|
|
32515
|
+
}],
|
|
32516
|
+
"pipelineAnalytics.proposeRetrainAnnotations": [{
|
|
32517
|
+
name: "deviceId",
|
|
32518
|
+
form: "single",
|
|
32519
|
+
optional: false
|
|
32520
|
+
}],
|
|
32521
|
+
"pipelineAnalytics.pruneEventsBefore": [{
|
|
32522
|
+
name: "deviceId",
|
|
32523
|
+
form: "single",
|
|
32524
|
+
optional: false
|
|
32525
|
+
}],
|
|
32526
|
+
"pipelineAnalytics.pruneTracksBefore": [{
|
|
32527
|
+
name: "deviceId",
|
|
32528
|
+
form: "single",
|
|
32529
|
+
optional: false
|
|
32530
|
+
}],
|
|
32531
|
+
"pipelineAnalytics.rebuildObjectEmbeddings": [{
|
|
32532
|
+
name: "deviceId",
|
|
32533
|
+
form: "single",
|
|
32534
|
+
optional: true
|
|
32535
|
+
}],
|
|
32536
|
+
"pipelineAnalytics.restageRetrainTrack": [{
|
|
32537
|
+
name: "deviceId",
|
|
32538
|
+
form: "single",
|
|
32539
|
+
optional: false
|
|
32540
|
+
}],
|
|
32541
|
+
"pipelineAnalytics.saveRetrainAnnotations": [{
|
|
32542
|
+
name: "deviceId",
|
|
32543
|
+
form: "single",
|
|
32544
|
+
optional: false
|
|
32545
|
+
}],
|
|
32546
|
+
"pipelineAnalytics.searchObjectEvents": [{
|
|
32547
|
+
name: "deviceId",
|
|
32548
|
+
form: "single",
|
|
32549
|
+
optional: true
|
|
32550
|
+
}],
|
|
32551
|
+
"pipelineAnalytics.selectRetrainFrames": [{
|
|
32552
|
+
name: "deviceId",
|
|
32553
|
+
form: "single",
|
|
32554
|
+
optional: false
|
|
32555
|
+
}],
|
|
32556
|
+
"pipelineAnalytics.setTrackFlags": [{
|
|
32557
|
+
name: "deviceId",
|
|
32558
|
+
form: "single",
|
|
32559
|
+
optional: false
|
|
32560
|
+
}],
|
|
32561
|
+
"pipelineAnalytics.wipeAllAnalytics": [{
|
|
32562
|
+
name: "deviceId",
|
|
32563
|
+
form: "single",
|
|
32564
|
+
optional: false
|
|
32565
|
+
}],
|
|
32566
|
+
"pipelineExecutor.runPipeline": [{
|
|
32567
|
+
name: "deviceId",
|
|
32568
|
+
form: "single",
|
|
32569
|
+
optional: true
|
|
32570
|
+
}],
|
|
32571
|
+
"pipelineExecutor.runPipelineBatch": [{
|
|
32572
|
+
name: "deviceId",
|
|
32573
|
+
form: "single",
|
|
32574
|
+
optional: true
|
|
32575
|
+
}],
|
|
32576
|
+
"pipelineOrchestrator.assignAudio": [{
|
|
32577
|
+
name: "deviceId",
|
|
32578
|
+
form: "single",
|
|
32579
|
+
optional: false
|
|
32580
|
+
}],
|
|
32581
|
+
"pipelineOrchestrator.assignPipeline": [{
|
|
32582
|
+
name: "deviceId",
|
|
32583
|
+
form: "single",
|
|
32584
|
+
optional: false
|
|
32585
|
+
}],
|
|
32586
|
+
"pipelineOrchestrator.getAudioAssignment": [{
|
|
32587
|
+
name: "deviceId",
|
|
32588
|
+
form: "single",
|
|
32589
|
+
optional: false
|
|
32590
|
+
}],
|
|
32591
|
+
"pipelineOrchestrator.getCameraMetrics": [{
|
|
32592
|
+
name: "deviceId",
|
|
32593
|
+
form: "single",
|
|
32594
|
+
optional: false
|
|
32595
|
+
}],
|
|
32596
|
+
"pipelineOrchestrator.getCameraSettings": [{
|
|
32597
|
+
name: "deviceId",
|
|
32598
|
+
form: "single",
|
|
32599
|
+
optional: false
|
|
32600
|
+
}],
|
|
32601
|
+
"pipelineOrchestrator.getCameraStatus": [{
|
|
32602
|
+
name: "deviceId",
|
|
32603
|
+
form: "single",
|
|
32604
|
+
optional: false
|
|
32605
|
+
}],
|
|
32606
|
+
"pipelineOrchestrator.getCameraStatuses": [{
|
|
32607
|
+
name: "deviceIds",
|
|
32608
|
+
form: "array",
|
|
32609
|
+
optional: true
|
|
32610
|
+
}],
|
|
32611
|
+
"pipelineOrchestrator.getCameraStepOverrides": [{
|
|
32612
|
+
name: "deviceId",
|
|
32613
|
+
form: "single",
|
|
32614
|
+
optional: false
|
|
32615
|
+
}],
|
|
32616
|
+
"pipelineOrchestrator.getCameraSwitches": [{
|
|
32617
|
+
name: "deviceId",
|
|
32618
|
+
form: "single",
|
|
32619
|
+
optional: false
|
|
32620
|
+
}],
|
|
32621
|
+
"pipelineOrchestrator.getPipelineAssignment": [{
|
|
32622
|
+
name: "deviceId",
|
|
32623
|
+
form: "single",
|
|
32624
|
+
optional: false
|
|
32625
|
+
}],
|
|
32626
|
+
"pipelineOrchestrator.getPipelineDevicePin": [{
|
|
32627
|
+
name: "deviceId",
|
|
32628
|
+
form: "single",
|
|
32629
|
+
optional: false
|
|
32630
|
+
}],
|
|
32631
|
+
"pipelineOrchestrator.resolvePipeline": [{
|
|
32632
|
+
name: "deviceId",
|
|
32633
|
+
form: "single",
|
|
32634
|
+
optional: false
|
|
32635
|
+
}],
|
|
32636
|
+
"pipelineOrchestrator.setCameraPipelineForAgent": [{
|
|
32637
|
+
name: "deviceId",
|
|
32638
|
+
form: "single",
|
|
32639
|
+
optional: false
|
|
32640
|
+
}],
|
|
32641
|
+
"pipelineOrchestrator.setCameraStepOverride": [{
|
|
32642
|
+
name: "deviceId",
|
|
32643
|
+
form: "single",
|
|
32644
|
+
optional: false
|
|
32645
|
+
}],
|
|
32646
|
+
"pipelineOrchestrator.setCameraStepToggle": [{
|
|
32647
|
+
name: "deviceId",
|
|
32648
|
+
form: "single",
|
|
32649
|
+
optional: false
|
|
32650
|
+
}],
|
|
32651
|
+
"pipelineOrchestrator.setCameraSwitch": [{
|
|
32652
|
+
name: "deviceId",
|
|
32653
|
+
form: "single",
|
|
32654
|
+
optional: false
|
|
32655
|
+
}],
|
|
32656
|
+
"pipelineOrchestrator.setPipelineDevicePin": [{
|
|
32657
|
+
name: "deviceId",
|
|
32658
|
+
form: "single",
|
|
32659
|
+
optional: false
|
|
32660
|
+
}],
|
|
32661
|
+
"pipelineOrchestrator.unassignAudio": [{
|
|
32662
|
+
name: "deviceId",
|
|
32663
|
+
form: "single",
|
|
32664
|
+
optional: false
|
|
32665
|
+
}],
|
|
32666
|
+
"pipelineOrchestrator.unassignPipeline": [{
|
|
32667
|
+
name: "deviceId",
|
|
32668
|
+
form: "single",
|
|
32669
|
+
optional: false
|
|
32670
|
+
}],
|
|
32671
|
+
"pipelineRunner.attachCamera": [{
|
|
32672
|
+
name: "deviceId",
|
|
32673
|
+
form: "single",
|
|
32674
|
+
optional: false
|
|
32675
|
+
}],
|
|
32676
|
+
"pipelineRunner.detachCamera": [{
|
|
32677
|
+
name: "deviceId",
|
|
32678
|
+
form: "single",
|
|
32679
|
+
optional: false
|
|
32680
|
+
}],
|
|
32681
|
+
"pipelineRunner.getCameraMetrics": [{
|
|
32682
|
+
name: "deviceId",
|
|
32683
|
+
form: "single",
|
|
32684
|
+
optional: false
|
|
32685
|
+
}],
|
|
32686
|
+
"pipelineRunner.reportMotion": [{
|
|
32687
|
+
name: "deviceId",
|
|
32688
|
+
form: "single",
|
|
32689
|
+
optional: false
|
|
32690
|
+
}],
|
|
32691
|
+
"pipelineRunner.runDetailSubtree": [{
|
|
32692
|
+
name: "deviceId",
|
|
32693
|
+
form: "single",
|
|
32694
|
+
optional: false
|
|
32695
|
+
}],
|
|
32696
|
+
"pipelineRunner.runStatelessStep": [{
|
|
32697
|
+
name: "sourceDeviceId",
|
|
32698
|
+
form: "single",
|
|
32699
|
+
optional: false
|
|
32700
|
+
}],
|
|
32701
|
+
"plateGallery.getPlateByTrack": [{
|
|
32702
|
+
name: "deviceId",
|
|
32703
|
+
form: "single",
|
|
32704
|
+
optional: false
|
|
32705
|
+
}],
|
|
32706
|
+
"plateGallery.listPlates": [{
|
|
32707
|
+
name: "deviceId",
|
|
32708
|
+
form: "single",
|
|
32709
|
+
optional: true
|
|
32710
|
+
}],
|
|
32711
|
+
"privacyMask.getOptions": [{
|
|
32712
|
+
name: "deviceId",
|
|
32713
|
+
form: "single",
|
|
32714
|
+
optional: false
|
|
32715
|
+
}],
|
|
32716
|
+
"privacyMask.setAudioEnabled": [{
|
|
32717
|
+
name: "deviceId",
|
|
32718
|
+
form: "single",
|
|
32719
|
+
optional: false
|
|
32720
|
+
}],
|
|
32721
|
+
"privacyMask.setMask": [{
|
|
32722
|
+
name: "deviceId",
|
|
32723
|
+
form: "single",
|
|
32724
|
+
optional: false
|
|
32725
|
+
}],
|
|
32726
|
+
"ptz.continuousMove": [{
|
|
32727
|
+
name: "deviceId",
|
|
32728
|
+
form: "single",
|
|
32729
|
+
optional: false
|
|
32730
|
+
}],
|
|
32731
|
+
"ptz.deletePreset": [{
|
|
32732
|
+
name: "deviceId",
|
|
32733
|
+
form: "single",
|
|
32734
|
+
optional: false
|
|
32735
|
+
}],
|
|
32736
|
+
"ptz.getOptions": [{
|
|
32737
|
+
name: "deviceId",
|
|
32738
|
+
form: "single",
|
|
32739
|
+
optional: false
|
|
32740
|
+
}],
|
|
32741
|
+
"ptz.getPosition": [{
|
|
32742
|
+
name: "deviceId",
|
|
32743
|
+
form: "single",
|
|
32744
|
+
optional: false
|
|
32745
|
+
}],
|
|
32746
|
+
"ptz.getPresets": [{
|
|
32747
|
+
name: "deviceId",
|
|
32748
|
+
form: "single",
|
|
32749
|
+
optional: false
|
|
32750
|
+
}],
|
|
32751
|
+
"ptz.goHome": [{
|
|
32752
|
+
name: "deviceId",
|
|
32753
|
+
form: "single",
|
|
32754
|
+
optional: false
|
|
32755
|
+
}],
|
|
32756
|
+
"ptz.goToPreset": [{
|
|
32757
|
+
name: "deviceId",
|
|
32758
|
+
form: "single",
|
|
32759
|
+
optional: false
|
|
32760
|
+
}],
|
|
32761
|
+
"ptz.move": [{
|
|
32762
|
+
name: "deviceId",
|
|
32763
|
+
form: "single",
|
|
32764
|
+
optional: false
|
|
32765
|
+
}],
|
|
32766
|
+
"ptz.savePreset": [{
|
|
32767
|
+
name: "deviceId",
|
|
32768
|
+
form: "single",
|
|
32769
|
+
optional: false
|
|
32770
|
+
}],
|
|
32771
|
+
"ptz.setAutofocus": [{
|
|
32772
|
+
name: "deviceId",
|
|
32773
|
+
form: "single",
|
|
32774
|
+
optional: false
|
|
32775
|
+
}],
|
|
32776
|
+
"ptz.stop": [{
|
|
32777
|
+
name: "deviceId",
|
|
32778
|
+
form: "single",
|
|
32779
|
+
optional: false
|
|
32780
|
+
}],
|
|
32781
|
+
"ptzAutotrack.getSettings": [{
|
|
32782
|
+
name: "deviceId",
|
|
32783
|
+
form: "single",
|
|
32784
|
+
optional: false
|
|
32785
|
+
}],
|
|
32786
|
+
"ptzAutotrack.getStatus": [{
|
|
32787
|
+
name: "deviceId",
|
|
32788
|
+
form: "single",
|
|
32789
|
+
optional: false
|
|
32790
|
+
}],
|
|
32791
|
+
"ptzAutotrack.setEnabled": [{
|
|
32792
|
+
name: "deviceId",
|
|
32793
|
+
form: "single",
|
|
32794
|
+
optional: false
|
|
32795
|
+
}],
|
|
32796
|
+
"ptzAutotrack.setSettings": [{
|
|
32797
|
+
name: "deviceId",
|
|
32798
|
+
form: "single",
|
|
32799
|
+
optional: false
|
|
32800
|
+
}],
|
|
32801
|
+
"reboot.reboot": [{
|
|
32802
|
+
name: "deviceId",
|
|
32803
|
+
form: "single",
|
|
32804
|
+
optional: false
|
|
32805
|
+
}],
|
|
32806
|
+
"recording.deleteFootprint": [{
|
|
32807
|
+
name: "deviceId",
|
|
32808
|
+
form: "single",
|
|
32809
|
+
optional: false
|
|
32810
|
+
}],
|
|
32811
|
+
"recording.getAvailability": [{
|
|
32812
|
+
name: "deviceId",
|
|
32813
|
+
form: "single",
|
|
32814
|
+
optional: false
|
|
32815
|
+
}],
|
|
32816
|
+
"recording.getDaysWithRecordings": [{
|
|
32817
|
+
name: "deviceId",
|
|
32818
|
+
form: "single",
|
|
32819
|
+
optional: false
|
|
32820
|
+
}],
|
|
32821
|
+
"recording.getDeviceConfig": [{
|
|
32822
|
+
name: "deviceId",
|
|
32823
|
+
form: "single",
|
|
32824
|
+
optional: false
|
|
32825
|
+
}],
|
|
32826
|
+
"recording.getPlaybackManifest": [{
|
|
32827
|
+
name: "deviceId",
|
|
32828
|
+
form: "single",
|
|
32829
|
+
optional: false
|
|
32830
|
+
}],
|
|
32831
|
+
"recording.listOpsLog": [{
|
|
32832
|
+
name: "deviceId",
|
|
32833
|
+
form: "single",
|
|
32834
|
+
optional: true
|
|
32835
|
+
}],
|
|
32836
|
+
"recording.locateSegment": [{
|
|
32837
|
+
name: "deviceId",
|
|
32838
|
+
form: "single",
|
|
32839
|
+
optional: false
|
|
32840
|
+
}],
|
|
32841
|
+
"recording.pruneFootage": [{
|
|
32842
|
+
name: "deviceId",
|
|
32843
|
+
form: "single",
|
|
32844
|
+
optional: false
|
|
32845
|
+
}],
|
|
32846
|
+
"recording.readGopBytes": [{
|
|
32847
|
+
name: "deviceId",
|
|
32848
|
+
form: "single",
|
|
32849
|
+
optional: false
|
|
32850
|
+
}],
|
|
32851
|
+
"recording.readSegmentBytes": [{
|
|
32852
|
+
name: "deviceId",
|
|
32853
|
+
form: "single",
|
|
32854
|
+
optional: false
|
|
32855
|
+
}],
|
|
32856
|
+
"recording.relocateFootage": [{
|
|
32857
|
+
name: "deviceId",
|
|
32858
|
+
form: "single",
|
|
32859
|
+
optional: true
|
|
32860
|
+
}],
|
|
32861
|
+
"recording.renderClip": [{
|
|
32862
|
+
name: "deviceId",
|
|
32863
|
+
form: "single",
|
|
32864
|
+
optional: false
|
|
32865
|
+
}],
|
|
32866
|
+
"recording.renderGif": [{
|
|
32867
|
+
name: "deviceId",
|
|
32868
|
+
form: "single",
|
|
32869
|
+
optional: false
|
|
32870
|
+
}],
|
|
32871
|
+
"recording.rescanStorage": [{
|
|
32872
|
+
name: "deviceId",
|
|
32873
|
+
form: "single",
|
|
32874
|
+
optional: false
|
|
32875
|
+
}],
|
|
32876
|
+
"recording.setDeviceConfig": [{
|
|
32877
|
+
name: "deviceId",
|
|
32878
|
+
form: "single",
|
|
32879
|
+
optional: false
|
|
32880
|
+
}],
|
|
32881
|
+
"recording.startStorageMigrationMove": [{
|
|
32882
|
+
name: "deviceId",
|
|
32883
|
+
form: "single",
|
|
32884
|
+
optional: true
|
|
32885
|
+
}],
|
|
32886
|
+
"recordingExport.createExport": [{
|
|
32887
|
+
name: "deviceId",
|
|
32888
|
+
form: "single",
|
|
32889
|
+
optional: false
|
|
32890
|
+
}],
|
|
32891
|
+
"recordingExport.listExports": [{
|
|
32892
|
+
name: "deviceId",
|
|
32893
|
+
form: "single",
|
|
32894
|
+
optional: true
|
|
32895
|
+
}],
|
|
32896
|
+
"sceneMonitor.captureReference": [{
|
|
32897
|
+
name: "deviceId",
|
|
32898
|
+
form: "single",
|
|
32899
|
+
optional: false
|
|
32900
|
+
}],
|
|
32901
|
+
"sceneMonitor.createScene": [{
|
|
32902
|
+
name: "deviceId",
|
|
32903
|
+
form: "single",
|
|
32904
|
+
optional: false
|
|
32905
|
+
}],
|
|
32906
|
+
"sceneMonitor.deleteReference": [{
|
|
32907
|
+
name: "deviceId",
|
|
32908
|
+
form: "single",
|
|
32909
|
+
optional: false
|
|
32910
|
+
}],
|
|
32911
|
+
"sceneMonitor.deleteScene": [{
|
|
32912
|
+
name: "deviceId",
|
|
32913
|
+
form: "single",
|
|
32914
|
+
optional: false
|
|
32915
|
+
}],
|
|
32916
|
+
"sceneMonitor.listScenes": [{
|
|
32917
|
+
name: "deviceId",
|
|
32918
|
+
form: "single",
|
|
32919
|
+
optional: false
|
|
32920
|
+
}],
|
|
32921
|
+
"sceneMonitor.recheckNow": [{
|
|
32922
|
+
name: "deviceId",
|
|
32923
|
+
form: "single",
|
|
32924
|
+
optional: false
|
|
32925
|
+
}],
|
|
32926
|
+
"sceneMonitor.updateScene": [{
|
|
32927
|
+
name: "deviceId",
|
|
32928
|
+
form: "single",
|
|
32929
|
+
optional: false
|
|
32930
|
+
}],
|
|
32931
|
+
"scriptRunner.run": [{
|
|
32932
|
+
name: "deviceId",
|
|
32933
|
+
form: "single",
|
|
32934
|
+
optional: false
|
|
32935
|
+
}],
|
|
32936
|
+
"scriptRunner.stop": [{
|
|
32937
|
+
name: "deviceId",
|
|
32938
|
+
form: "single",
|
|
32939
|
+
optional: false
|
|
32940
|
+
}],
|
|
32941
|
+
"snapshot.getSnapshot": [{
|
|
32942
|
+
name: "deviceId",
|
|
32943
|
+
form: "single",
|
|
32944
|
+
optional: false
|
|
32945
|
+
}],
|
|
32946
|
+
"snapshot.getSnapshotOverview": [{
|
|
32947
|
+
name: "deviceIds",
|
|
32948
|
+
form: "array",
|
|
32949
|
+
optional: false
|
|
32950
|
+
}],
|
|
32951
|
+
"snapshot.invalidateCache": [{
|
|
32952
|
+
name: "deviceId",
|
|
32953
|
+
form: "single",
|
|
32954
|
+
optional: false
|
|
32955
|
+
}],
|
|
32956
|
+
"streamBroker.acquireEgressTranscode": [{
|
|
32957
|
+
name: "deviceId",
|
|
32958
|
+
form: "single",
|
|
32959
|
+
optional: false
|
|
32960
|
+
}],
|
|
32961
|
+
"streamBroker.assignProfile": [{
|
|
32962
|
+
name: "deviceId",
|
|
32963
|
+
form: "single",
|
|
32964
|
+
optional: false
|
|
32965
|
+
}],
|
|
32966
|
+
"streamBroker.getDeviceAudioMute": [{
|
|
32967
|
+
name: "deviceId",
|
|
32968
|
+
form: "single",
|
|
32969
|
+
optional: false
|
|
32970
|
+
}],
|
|
32971
|
+
"streamBroker.getStreamWithCodec": [{
|
|
32972
|
+
name: "deviceId",
|
|
32973
|
+
form: "single",
|
|
32974
|
+
optional: false
|
|
32975
|
+
}],
|
|
32976
|
+
"streamBroker.produceEventMedia": [{
|
|
32977
|
+
name: "deviceId",
|
|
32978
|
+
form: "single",
|
|
32979
|
+
optional: false
|
|
32980
|
+
}],
|
|
32981
|
+
"streamBroker.publishCameraStream": [{
|
|
32982
|
+
name: "deviceId",
|
|
32983
|
+
form: "single",
|
|
32984
|
+
optional: false
|
|
32985
|
+
}],
|
|
32986
|
+
"streamBroker.renderPreBufferClip": [{
|
|
32987
|
+
name: "deviceId",
|
|
32988
|
+
form: "single",
|
|
32989
|
+
optional: false
|
|
32990
|
+
}],
|
|
32991
|
+
"streamBroker.restartProfile": [{
|
|
32992
|
+
name: "deviceId",
|
|
32993
|
+
form: "single",
|
|
32994
|
+
optional: false
|
|
32995
|
+
}],
|
|
32996
|
+
"streamBroker.retractCameraStream": [{
|
|
32997
|
+
name: "deviceId",
|
|
32998
|
+
form: "single",
|
|
32999
|
+
optional: false
|
|
33000
|
+
}],
|
|
33001
|
+
"streamBroker.setDeviceAudioMute": [{
|
|
33002
|
+
name: "deviceId",
|
|
33003
|
+
form: "single",
|
|
33004
|
+
optional: false
|
|
33005
|
+
}],
|
|
33006
|
+
"streamBroker.unassignProfile": [{
|
|
33007
|
+
name: "deviceId",
|
|
33008
|
+
form: "single",
|
|
33009
|
+
optional: false
|
|
33010
|
+
}],
|
|
33011
|
+
"streamCatalog.getCatalog": [{
|
|
33012
|
+
name: "deviceId",
|
|
33013
|
+
form: "single",
|
|
33014
|
+
optional: false
|
|
33015
|
+
}],
|
|
33016
|
+
"streamParams.getConfigSchema": [{
|
|
33017
|
+
name: "deviceId",
|
|
33018
|
+
form: "single",
|
|
33019
|
+
optional: false
|
|
33020
|
+
}],
|
|
33021
|
+
"streamParams.getOptions": [{
|
|
33022
|
+
name: "deviceId",
|
|
33023
|
+
form: "single",
|
|
33024
|
+
optional: false
|
|
33025
|
+
}],
|
|
33026
|
+
"streamParams.setProfile": [{
|
|
33027
|
+
name: "deviceId",
|
|
33028
|
+
form: "single",
|
|
33029
|
+
optional: false
|
|
33030
|
+
}],
|
|
33031
|
+
"switch.setState": [{
|
|
33032
|
+
name: "deviceId",
|
|
33033
|
+
form: "single",
|
|
33034
|
+
optional: false
|
|
33035
|
+
}],
|
|
33036
|
+
"vacuumControl.locate": [{
|
|
33037
|
+
name: "deviceId",
|
|
33038
|
+
form: "single",
|
|
33039
|
+
optional: false
|
|
33040
|
+
}],
|
|
33041
|
+
"vacuumControl.pause": [{
|
|
33042
|
+
name: "deviceId",
|
|
33043
|
+
form: "single",
|
|
33044
|
+
optional: false
|
|
33045
|
+
}],
|
|
33046
|
+
"vacuumControl.returnToBase": [{
|
|
33047
|
+
name: "deviceId",
|
|
33048
|
+
form: "single",
|
|
33049
|
+
optional: false
|
|
33050
|
+
}],
|
|
33051
|
+
"vacuumControl.setFanSpeed": [{
|
|
33052
|
+
name: "deviceId",
|
|
33053
|
+
form: "single",
|
|
33054
|
+
optional: false
|
|
33055
|
+
}],
|
|
33056
|
+
"vacuumControl.start": [{
|
|
33057
|
+
name: "deviceId",
|
|
33058
|
+
form: "single",
|
|
33059
|
+
optional: false
|
|
33060
|
+
}],
|
|
33061
|
+
"vacuumControl.stop": [{
|
|
33062
|
+
name: "deviceId",
|
|
33063
|
+
form: "single",
|
|
33064
|
+
optional: false
|
|
33065
|
+
}],
|
|
33066
|
+
"valve.close": [{
|
|
33067
|
+
name: "deviceId",
|
|
33068
|
+
form: "single",
|
|
33069
|
+
optional: false
|
|
33070
|
+
}],
|
|
33071
|
+
"valve.open": [{
|
|
33072
|
+
name: "deviceId",
|
|
33073
|
+
form: "single",
|
|
33074
|
+
optional: false
|
|
33075
|
+
}],
|
|
33076
|
+
"valve.setPosition": [{
|
|
33077
|
+
name: "deviceId",
|
|
33078
|
+
form: "single",
|
|
33079
|
+
optional: false
|
|
33080
|
+
}],
|
|
33081
|
+
"valve.stop": [{
|
|
33082
|
+
name: "deviceId",
|
|
33083
|
+
form: "single",
|
|
33084
|
+
optional: false
|
|
33085
|
+
}],
|
|
33086
|
+
"videoclips.getClipPlayback": [{
|
|
33087
|
+
name: "deviceId",
|
|
33088
|
+
form: "single",
|
|
33089
|
+
optional: false
|
|
33090
|
+
}],
|
|
33091
|
+
"videoclips.listClips": [{
|
|
33092
|
+
name: "deviceId",
|
|
33093
|
+
form: "single",
|
|
33094
|
+
optional: false
|
|
33095
|
+
}],
|
|
33096
|
+
"waterHeater.setAway": [{
|
|
33097
|
+
name: "deviceId",
|
|
33098
|
+
form: "single",
|
|
33099
|
+
optional: false
|
|
33100
|
+
}],
|
|
33101
|
+
"waterHeater.setOperationMode": [{
|
|
33102
|
+
name: "deviceId",
|
|
33103
|
+
form: "single",
|
|
33104
|
+
optional: false
|
|
33105
|
+
}],
|
|
33106
|
+
"waterHeater.setTargetTemp": [{
|
|
33107
|
+
name: "deviceId",
|
|
33108
|
+
form: "single",
|
|
33109
|
+
optional: false
|
|
33110
|
+
}],
|
|
33111
|
+
"webrtcSession.addIceCandidate": [{
|
|
33112
|
+
name: "deviceId",
|
|
33113
|
+
form: "single",
|
|
33114
|
+
optional: false
|
|
33115
|
+
}],
|
|
33116
|
+
"webrtcSession.closeSession": [{
|
|
33117
|
+
name: "deviceId",
|
|
33118
|
+
form: "single",
|
|
33119
|
+
optional: false
|
|
33120
|
+
}],
|
|
33121
|
+
"webrtcSession.createSession": [{
|
|
33122
|
+
name: "deviceId",
|
|
33123
|
+
form: "single",
|
|
33124
|
+
optional: false
|
|
33125
|
+
}],
|
|
33126
|
+
"webrtcSession.getIceCandidates": [{
|
|
33127
|
+
name: "deviceId",
|
|
33128
|
+
form: "single",
|
|
33129
|
+
optional: false
|
|
33130
|
+
}],
|
|
33131
|
+
"webrtcSession.getSessionState": [{
|
|
33132
|
+
name: "deviceId",
|
|
33133
|
+
form: "single",
|
|
33134
|
+
optional: false
|
|
33135
|
+
}],
|
|
33136
|
+
"webrtcSession.handleAnswer": [{
|
|
33137
|
+
name: "deviceId",
|
|
33138
|
+
form: "single",
|
|
33139
|
+
optional: false
|
|
33140
|
+
}],
|
|
33141
|
+
"webrtcSession.handleOffer": [{
|
|
33142
|
+
name: "deviceId",
|
|
33143
|
+
form: "single",
|
|
33144
|
+
optional: false
|
|
33145
|
+
}],
|
|
33146
|
+
"webrtcSession.hasAdaptiveBitrate": [{
|
|
33147
|
+
name: "deviceId",
|
|
33148
|
+
form: "single",
|
|
33149
|
+
optional: false
|
|
33150
|
+
}],
|
|
33151
|
+
"webrtcSession.listStreams": [{
|
|
33152
|
+
name: "deviceId",
|
|
33153
|
+
form: "single",
|
|
33154
|
+
optional: false
|
|
33155
|
+
}],
|
|
33156
|
+
"zoneAnalytics.getCameraHistory": [{
|
|
33157
|
+
name: "deviceId",
|
|
33158
|
+
form: "single",
|
|
33159
|
+
optional: false
|
|
33160
|
+
}],
|
|
33161
|
+
"zoneAnalytics.getCurrentSnapshot": [{
|
|
33162
|
+
name: "deviceId",
|
|
33163
|
+
form: "single",
|
|
33164
|
+
optional: false
|
|
33165
|
+
}],
|
|
33166
|
+
"zoneAnalytics.getUnzonedHistory": [{
|
|
33167
|
+
name: "deviceId",
|
|
33168
|
+
form: "single",
|
|
33169
|
+
optional: false
|
|
33170
|
+
}],
|
|
33171
|
+
"zoneAnalytics.getZoneHistory": [{
|
|
33172
|
+
name: "deviceId",
|
|
33173
|
+
form: "single",
|
|
33174
|
+
optional: false
|
|
33175
|
+
}],
|
|
33176
|
+
"zoneRules.listRules": [{
|
|
33177
|
+
name: "deviceId",
|
|
33178
|
+
form: "single",
|
|
33179
|
+
optional: false
|
|
33180
|
+
}],
|
|
33181
|
+
"zoneRules.setRules": [{
|
|
33182
|
+
name: "deviceId",
|
|
33183
|
+
form: "single",
|
|
33184
|
+
optional: false
|
|
33185
|
+
}],
|
|
33186
|
+
"zones.addZone": [{
|
|
33187
|
+
name: "deviceId",
|
|
33188
|
+
form: "single",
|
|
33189
|
+
optional: false
|
|
33190
|
+
}],
|
|
33191
|
+
"zones.listZones": [{
|
|
33192
|
+
name: "deviceId",
|
|
33193
|
+
form: "single",
|
|
33194
|
+
optional: false
|
|
33195
|
+
}],
|
|
33196
|
+
"zones.removeZone": [{
|
|
33197
|
+
name: "deviceId",
|
|
33198
|
+
form: "single",
|
|
33199
|
+
optional: false
|
|
33200
|
+
}],
|
|
33201
|
+
"zones.updateZone": [{
|
|
33202
|
+
name: "deviceId",
|
|
33203
|
+
form: "single",
|
|
33204
|
+
optional: false
|
|
33205
|
+
}]
|
|
33206
|
+
});
|
|
31378
33207
|
Object.freeze({
|
|
31379
33208
|
"broker": "broker",
|
|
31380
33209
|
"device-export": "device-export",
|
|
@@ -32094,7 +33923,7 @@ var Fmp4FragmentChild = class {
|
|
|
32094
33923
|
meta: {
|
|
32095
33924
|
sourceId: this.args.sourceId,
|
|
32096
33925
|
decodeHwAccel: requested,
|
|
32097
|
-
error: errMsg$
|
|
33926
|
+
error: errMsg$15(err)
|
|
32098
33927
|
}
|
|
32099
33928
|
});
|
|
32100
33929
|
this.killChild();
|
|
@@ -32273,7 +34102,7 @@ var Fmp4FragmentChild = class {
|
|
|
32273
34102
|
tags: { deviceId: this.args.deviceId },
|
|
32274
34103
|
meta: {
|
|
32275
34104
|
sourceId: this.args.sourceId,
|
|
32276
|
-
error: errMsg$
|
|
34105
|
+
error: errMsg$15(err)
|
|
32277
34106
|
}
|
|
32278
34107
|
});
|
|
32279
34108
|
}
|
|
@@ -78618,7 +80447,7 @@ function clearPairingFiles(accessoryUuid, logger) {
|
|
|
78618
80447
|
}
|
|
78619
80448
|
//#endregion
|
|
78620
80449
|
//#region src/hap-setup-uri.ts
|
|
78621
|
-
function errMsg$
|
|
80450
|
+
function errMsg$14(e) {
|
|
78622
80451
|
return e instanceof Error ? e.message : String(e);
|
|
78623
80452
|
}
|
|
78624
80453
|
/**
|
|
@@ -78645,11 +80474,79 @@ function firstExposedAccessorySetupUri(exposed, logger) {
|
|
|
78645
80474
|
try {
|
|
78646
80475
|
return first.setupURI();
|
|
78647
80476
|
} catch (err) {
|
|
78648
|
-
logger.debug("export-hap: setupURI failed on first exposed accessory", { meta: { error: errMsg$
|
|
80477
|
+
logger.debug("export-hap: setupURI failed on first exposed accessory", { meta: { error: errMsg$14(err) } });
|
|
78649
80478
|
return;
|
|
78650
80479
|
}
|
|
78651
80480
|
}
|
|
78652
80481
|
}
|
|
80482
|
+
//#endregion
|
|
80483
|
+
//#region src/mappers/builders/accessory-info.ts
|
|
80484
|
+
/**
|
|
80485
|
+
* `Service.AccessoryInformation` — the manufacturer / model / firmware /
|
|
80486
|
+
* serial block every HomeKit accessory carries.
|
|
80487
|
+
*
|
|
80488
|
+
* One implementation for both accessory shapes (camera and generic): the
|
|
80489
|
+
* fields come from the device's own metadata either way, and a second copy
|
|
80490
|
+
* would be a second answer to "what serial does this device publish".
|
|
80491
|
+
* Metadata is best-effort — a device that cannot answer still publishes.
|
|
80492
|
+
*/
|
|
80493
|
+
async function populateAccessoryInfo(accessory, proxy, displayName, modelFallback) {
|
|
80494
|
+
const info = accessory.getService(import_dist.Service.AccessoryInformation);
|
|
80495
|
+
if (!info) return;
|
|
80496
|
+
try {
|
|
80497
|
+
const device = await proxy.deviceManager?.getDevice({});
|
|
80498
|
+
const metadata = readMetadata(device);
|
|
80499
|
+
info.setCharacteristic(import_dist.Characteristic.Name, device?.name ?? displayName);
|
|
80500
|
+
info.setCharacteristic(import_dist.Characteristic.Manufacturer, stringOr(metadata?.manufacturer, "CamStack"));
|
|
80501
|
+
info.setCharacteristic(import_dist.Characteristic.Model, stringOr(metadata?.model, modelFallback));
|
|
80502
|
+
info.setCharacteristic(import_dist.Characteristic.FirmwareRevision, stringOr(metadata?.firmware, "0.0.0"));
|
|
80503
|
+
info.setCharacteristic(import_dist.Characteristic.SerialNumber, stringOr(metadata?.sn, `camstack-${proxy.deviceId}`));
|
|
80504
|
+
} catch {}
|
|
80505
|
+
}
|
|
80506
|
+
/** The four fields this module reads, or `null` — the device record's
|
|
80507
|
+
* `metadata` is an open bag and nothing else here depends on its shape. */
|
|
80508
|
+
function readMetadata(device) {
|
|
80509
|
+
const metadata = device?.metadata;
|
|
80510
|
+
if (metadata === null || typeof metadata !== "object") return {};
|
|
80511
|
+
const entries = new Map(Object.entries(metadata));
|
|
80512
|
+
return {
|
|
80513
|
+
model: stringOrNull(entries.get("model")),
|
|
80514
|
+
manufacturer: stringOrNull(entries.get("manufacturer")),
|
|
80515
|
+
firmware: stringOrNull(entries.get("firmware")),
|
|
80516
|
+
sn: stringOrNull(entries.get("sn"))
|
|
80517
|
+
};
|
|
80518
|
+
}
|
|
80519
|
+
function stringOrNull(value) {
|
|
80520
|
+
return typeof value === "string" ? value : null;
|
|
80521
|
+
}
|
|
80522
|
+
function stringOr(value, fallback) {
|
|
80523
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
80524
|
+
}
|
|
80525
|
+
//#endregion
|
|
80526
|
+
//#region src/mappers/builders/generic/characteristic-update.ts
|
|
80527
|
+
/**
|
|
80528
|
+
* Parse `status` with the capability's OWN Zod schema and turn it into
|
|
80529
|
+
* characteristic writes.
|
|
80530
|
+
*
|
|
80531
|
+
* Duck-typing the payload here would be the second source of truth about what a
|
|
80532
|
+
* cap reports; the schema is the first and only one. A payload that does not
|
|
80533
|
+
* match yields NO updates — never a partial or invented value — and the caller
|
|
80534
|
+
* logs the drop, because a sensor that silently stops moving is
|
|
80535
|
+
* indistinguishable from a sensor that never changed.
|
|
80536
|
+
*
|
|
80537
|
+
* Rows `.pick()` only the fields they read, so a provider omitting a timestamp
|
|
80538
|
+
* cannot silence a sensor.
|
|
80539
|
+
*/
|
|
80540
|
+
function reader(schema, toUpdates) {
|
|
80541
|
+
return (status) => {
|
|
80542
|
+
const parsed = schema.safeParse(status);
|
|
80543
|
+
return parsed.success ? toUpdates(parsed.data) : [];
|
|
80544
|
+
};
|
|
80545
|
+
}
|
|
80546
|
+
/** Push every update onto `service`. */
|
|
80547
|
+
function applyUpdates(service, updates) {
|
|
80548
|
+
for (const update of updates) service.updateCharacteristic(update.characteristic, update.value);
|
|
80549
|
+
}
|
|
78653
80550
|
/**
|
|
78654
80551
|
* hap-nodejs' `checkName` regex, verbatim.
|
|
78655
80552
|
*
|
|
@@ -78786,6 +80683,92 @@ function titleCase(raw) {
|
|
|
78786
80683
|
return raw.split(/[-_\s]+/u).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
78787
80684
|
}
|
|
78788
80685
|
//#endregion
|
|
80686
|
+
//#region src/mappers/builders/service-label.ts
|
|
80687
|
+
/**
|
|
80688
|
+
* The ONE place a secondary service on the camera accessory gets its label.
|
|
80689
|
+
*
|
|
80690
|
+
* A "secondary service" here is a Switch or Lightbulb published alongside the
|
|
80691
|
+
* camera on the same accessory — the privacy switch, each accessory child
|
|
80692
|
+
* (siren, floodlight), each PTZ action. iOS Home renders these as their own
|
|
80693
|
+
* controls, and the operator has seen them as "Interruttore 1", "Interruttore
|
|
80694
|
+
* 2" through three separate rounds of fixes.
|
|
80695
|
+
*
|
|
80696
|
+
* ## Why `Name` alone cannot rename anything
|
|
80697
|
+
*
|
|
80698
|
+
* Two facts about hap-nodejs 2.1.7, both measured against the installed copy
|
|
80699
|
+
* rather than reasoned about:
|
|
80700
|
+
*
|
|
80701
|
+
* 1. `accessory.addService(Type, displayName, subtype)` ALREADY writes
|
|
80702
|
+
* `displayName` to `Characteristic.Name` (`Service` constructor). So every
|
|
80703
|
+
* round of this bug — including the one that moved the label onto
|
|
80704
|
+
* `ConfiguredName` — shipped with `Name` correctly set. "iOS had no name
|
|
80705
|
+
* to render" was never true.
|
|
80706
|
+
* 2. The mDNS configuration number (`c#`) is a sha1 over
|
|
80707
|
+
* `internalHAPRepresentation(false)`, which OMITS characteristic VALUES.
|
|
80708
|
+
* Changing the string in `Name` therefore does not bump `c#`, a paired
|
|
80709
|
+
* controller gets no signal to re-read `/accessories`, and the name it
|
|
80710
|
+
* cached at first enumeration stands forever.
|
|
80711
|
+
*
|
|
80712
|
+
* `Name` is also declared `pr` only — paired read, no write, no notify. It is
|
|
80713
|
+
* the seed a controller seeds its database from once; it is not a channel.
|
|
80714
|
+
*
|
|
80715
|
+
* ## Why `ConfiguredName`
|
|
80716
|
+
*
|
|
80717
|
+
* `ConfiguredName` (`000000E3`) is declared `pr | pw | ev` — the only name
|
|
80718
|
+
* characteristic a controller may write and may subscribe to. It is what iOS
|
|
80719
|
+
* 16+ reads for a service the user can rename, and adding it CHANGES the
|
|
80720
|
+
* accessory structure, so `c#` does bump and the controller re-reads.
|
|
80721
|
+
*
|
|
80722
|
+
* It was removed once because hap-nodejs logged
|
|
80723
|
+
*
|
|
80724
|
+
* ```
|
|
80725
|
+
* Characteristic not in required or optional characteristic section for
|
|
80726
|
+
* service Switch. Adding anyway.
|
|
80727
|
+
* ```
|
|
80728
|
+
*
|
|
80729
|
+
* That line is a WARNING, not a rejection: `Service.getCharacteristic` calls
|
|
80730
|
+
* `addCharacteristic` unconditionally and only then emits the warning. The
|
|
80731
|
+
* characteristic was always present and always published. hap-nodejs'
|
|
80732
|
+
* per-service optional lists simply predate `ConfiguredName` being valid on
|
|
80733
|
+
* any service.
|
|
80734
|
+
*
|
|
80735
|
+
* Registering it with {@link Service.addOptionalCharacteristic} first takes
|
|
80736
|
+
* the branch above the warning, so the accessory still builds with ZERO
|
|
80737
|
+
* characteristic warnings — which is what `service-naming.spec.ts` asserts.
|
|
80738
|
+
*
|
|
80739
|
+
* ## Scope: EVERY service, including the sensors
|
|
80740
|
+
*
|
|
80741
|
+
* An earlier round applied this to Switch- and Lightbulb-shaped services only,
|
|
80742
|
+
* on the theory that `Service.MotionSensor` and `Service.Battery` are not
|
|
80743
|
+
* separately named tiles in iOS Home and that naming them would be a guess.
|
|
80744
|
+
*
|
|
80745
|
+
* That theory was never measured, and it had a cost: it left services on the
|
|
80746
|
+
* accessory whose name a paired controller could never be told about, and it
|
|
80747
|
+
* made "did `ConfiguredName` fix the operator's 'Interruttore N'?" unanswerable
|
|
80748
|
+
* — a negative result on a partial application proves nothing about the
|
|
80749
|
+
* mechanism. Every service this addon publishes now carries both
|
|
80750
|
+
* characteristics, on the camera accessory and on the generic one.
|
|
80751
|
+
*
|
|
80752
|
+
* The reasoning above is mechanism, not measurement: it says why `Name` alone
|
|
80753
|
+
* CANNOT work and why `ConfiguredName` is the only characteristic that can. It
|
|
80754
|
+
* does not prove iOS renders it on every service shape. That is an observation
|
|
80755
|
+
* only a re-paired controller can make — and after a change like this one, the
|
|
80756
|
+
* controller must be re-paired, because a cached accessory database is exactly
|
|
80757
|
+
* what the whole mechanism is about.
|
|
80758
|
+
*/
|
|
80759
|
+
/**
|
|
80760
|
+
* Publish `name` as both the immutable `Name` and the controller-visible
|
|
80761
|
+
* `ConfiguredName` of `service`.
|
|
80762
|
+
*
|
|
80763
|
+
* `name` must already be HAP-valid — build it with `service-names.ts`, which
|
|
80764
|
+
* cannot return a string hap-nodejs' `checkName` would warn about.
|
|
80765
|
+
*/
|
|
80766
|
+
function applyServiceLabel(service, name) {
|
|
80767
|
+
service.setCharacteristic(import_dist.Characteristic.Name, name);
|
|
80768
|
+
if (!service.optionalCharacteristics.some((characteristic) => characteristic.UUID === import_dist.Characteristic.ConfiguredName.UUID)) service.addOptionalCharacteristic(import_dist.Characteristic.ConfiguredName);
|
|
80769
|
+
service.setCharacteristic(import_dist.Characteristic.ConfiguredName, name);
|
|
80770
|
+
}
|
|
80771
|
+
//#endregion
|
|
78789
80772
|
//#region src/mappers/builders/battery.ts
|
|
78790
80773
|
/**
|
|
78791
80774
|
* Battery builder — surfaces a battery-operated camera's power state
|
|
@@ -78795,34 +80778,56 @@ function titleCase(raw) {
|
|
|
78795
80778
|
* subscribes to the runtime-state slice so iOS Home reflects level /
|
|
78796
80779
|
* charging changes pushed by the firmware without a poll loop.
|
|
78797
80780
|
*
|
|
80781
|
+
* The status→characteristic mapping is {@link batteryCharacteristicUpdates},
|
|
80782
|
+
* exported because the generic (non-camera) export path publishes the same
|
|
80783
|
+
* `Service.Battery` from the same cap — one derivation, two accessory shapes.
|
|
80784
|
+
*
|
|
80785
|
+
* Skipped silently when the `battery` cap is not bound — caller checks
|
|
80786
|
+
* cap presence before invoking this builder (see `camera-accessory.ts`).
|
|
80787
|
+
*/
|
|
80788
|
+
var LOW_BATTERY_THRESHOLD_PCT = 20;
|
|
80789
|
+
/**
|
|
78798
80790
|
* Mapping:
|
|
78799
80791
|
* - `BatteryStatus.percentage` (0..100) → `Characteristic.BatteryLevel`
|
|
78800
80792
|
* - `BatteryStatus.charging`:
|
|
78801
80793
|
* `'none'` → `ChargingState.NOT_CHARGING`
|
|
78802
80794
|
* `'dc' | 'solar'` → `ChargingState.CHARGING`
|
|
78803
80795
|
* - `percentage <= LOW_BATTERY_THRESHOLD_PCT` → `StatusLowBattery.LOW`
|
|
78804
|
-
*
|
|
78805
|
-
* Skipped silently when the `battery` cap is not bound — caller checks
|
|
78806
|
-
* cap presence before invoking this builder (see `camera-accessory.ts`).
|
|
78807
80796
|
*/
|
|
78808
|
-
var
|
|
80797
|
+
var batteryCharacteristicUpdates = reader(BatteryStatusSchema.pick({ percentage: true }).extend({ charging: BatteryStatusSchema.shape.charging.optional() }), (status) => {
|
|
80798
|
+
const pct = Math.max(0, Math.min(100, Math.round(status.percentage)));
|
|
80799
|
+
return [
|
|
80800
|
+
{
|
|
80801
|
+
characteristic: import_dist.Characteristic.BatteryLevel,
|
|
80802
|
+
value: pct
|
|
80803
|
+
},
|
|
80804
|
+
...status.charging === void 0 ? [] : [{
|
|
80805
|
+
characteristic: import_dist.Characteristic.ChargingState,
|
|
80806
|
+
value: status.charging === "none" ? import_dist.Characteristic.ChargingState.NOT_CHARGING : import_dist.Characteristic.ChargingState.CHARGING
|
|
80807
|
+
}],
|
|
80808
|
+
{
|
|
80809
|
+
characteristic: import_dist.Characteristic.StatusLowBattery,
|
|
80810
|
+
value: pct <= LOW_BATTERY_THRESHOLD_PCT ? import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
|
|
80811
|
+
}
|
|
80812
|
+
];
|
|
80813
|
+
});
|
|
78809
80814
|
async function buildBattery(bctx) {
|
|
78810
80815
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
78811
80816
|
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
78812
|
-
const
|
|
80817
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
80818
|
+
const service = accessory.addService(import_dist.Service.Battery, label);
|
|
80819
|
+
applyServiceLabel(service, label);
|
|
78813
80820
|
try {
|
|
78814
80821
|
const status = await proxy.battery?.getStatus({});
|
|
78815
|
-
if (status) applyToService(service, status);
|
|
80822
|
+
if (status !== void 0 && status !== null) applyToService(service, status);
|
|
78816
80823
|
} catch (err) {
|
|
78817
|
-
log.debug("export-hap: battery getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
80824
|
+
log.debug("export-hap: battery getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$13(err) } });
|
|
78818
80825
|
}
|
|
78819
80826
|
const unsubscribes = [];
|
|
78820
80827
|
if (proxy.state.battery) {
|
|
78821
80828
|
const unsub = proxy.state.battery.subscribe((value) => {
|
|
78822
80829
|
if (!value) return;
|
|
78823
|
-
|
|
78824
|
-
if (typeof status.percentage !== "number") return;
|
|
78825
|
-
applyToService(service, status);
|
|
80830
|
+
applyToService(service, value);
|
|
78826
80831
|
});
|
|
78827
80832
|
unsubscribes.push(unsub);
|
|
78828
80833
|
}
|
|
@@ -78833,14 +80838,9 @@ async function buildBattery(bctx) {
|
|
|
78833
80838
|
} };
|
|
78834
80839
|
}
|
|
78835
80840
|
function applyToService(service, status) {
|
|
78836
|
-
|
|
78837
|
-
service.updateCharacteristic(import_dist.Characteristic.BatteryLevel, pct);
|
|
78838
|
-
const chargingState = status.charging === "none" ? import_dist.Characteristic.ChargingState.NOT_CHARGING : import_dist.Characteristic.ChargingState.CHARGING;
|
|
78839
|
-
service.updateCharacteristic(import_dist.Characteristic.ChargingState, chargingState);
|
|
78840
|
-
const lowBattery = pct <= LOW_BATTERY_THRESHOLD_PCT ? import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
78841
|
-
service.updateCharacteristic(import_dist.Characteristic.StatusLowBattery, lowBattery);
|
|
80841
|
+
applyUpdates(service, batteryCharacteristicUpdates(status));
|
|
78842
80842
|
}
|
|
78843
|
-
function errMsg$
|
|
80843
|
+
function errMsg$13(err) {
|
|
78844
80844
|
return err instanceof Error ? err.message : String(err);
|
|
78845
80845
|
}
|
|
78846
80846
|
//#endregion
|
|
@@ -89592,7 +91592,7 @@ var VIDEO_LOOPBACK_RCVBUF_BYTES = 8 * 1024 * 1024;
|
|
|
89592
91592
|
* not burst size.
|
|
89593
91593
|
*/
|
|
89594
91594
|
var AUDIO_LOOPBACK_RCVBUF_BYTES = 1024 * 1024;
|
|
89595
|
-
function errMsg$
|
|
91595
|
+
function errMsg$12(err) {
|
|
89596
91596
|
return err instanceof Error ? err.message : String(err);
|
|
89597
91597
|
}
|
|
89598
91598
|
/**
|
|
@@ -89607,13 +91607,13 @@ function applyReceiveBuffer(socket, requestedBytes) {
|
|
|
89607
91607
|
try {
|
|
89608
91608
|
socket.setRecvBufferSize(requestedBytes);
|
|
89609
91609
|
} catch (err) {
|
|
89610
|
-
error = errMsg$
|
|
91610
|
+
error = errMsg$12(err);
|
|
89611
91611
|
}
|
|
89612
91612
|
let effectiveBytes = null;
|
|
89613
91613
|
try {
|
|
89614
91614
|
effectiveBytes = socket.getRecvBufferSize();
|
|
89615
91615
|
} catch (err) {
|
|
89616
|
-
if (error === null) error = errMsg$
|
|
91616
|
+
if (error === null) error = errMsg$12(err);
|
|
89617
91617
|
}
|
|
89618
91618
|
return {
|
|
89619
91619
|
requestedBytes,
|
|
@@ -89873,20 +91873,20 @@ function buildCameraStreamingDelegate(bctx, advertised) {
|
|
|
89873
91873
|
delegate: {
|
|
89874
91874
|
handleSnapshotRequest(request, callback) {
|
|
89875
91875
|
handleSnapshot(bctx, request).then((buf) => callback(void 0, buf)).catch((err) => {
|
|
89876
|
-
log.warn("export-hap: snapshot failed", { meta: { error: errMsg$
|
|
89877
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91876
|
+
log.warn("export-hap: snapshot failed", { meta: { error: errMsg$11(err) } });
|
|
91877
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89878
91878
|
});
|
|
89879
91879
|
},
|
|
89880
91880
|
prepareStream(request, callback) {
|
|
89881
91881
|
prepareStream(request, sessions, bctx).then((resp) => callback(void 0, resp)).catch((err) => {
|
|
89882
|
-
log.warn("export-hap: prepareStream failed", { meta: { error: errMsg$
|
|
89883
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91882
|
+
log.warn("export-hap: prepareStream failed", { meta: { error: errMsg$11(err) } });
|
|
91883
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89884
91884
|
});
|
|
89885
91885
|
},
|
|
89886
91886
|
handleStreamRequest(request, callback) {
|
|
89887
91887
|
handleStreamRequest(request, sessions, bctx, advertised).then(() => callback()).catch((err) => {
|
|
89888
|
-
log.warn("export-hap: handleStreamRequest failed", { meta: { error: errMsg$
|
|
89889
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91888
|
+
log.warn("export-hap: handleStreamRequest failed", { meta: { error: errMsg$11(err) } });
|
|
91889
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89890
91890
|
});
|
|
89891
91891
|
}
|
|
89892
91892
|
},
|
|
@@ -90002,7 +92002,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90002
92002
|
closeSocket(audioUdp);
|
|
90003
92003
|
closeSocket(videoLoopUdp);
|
|
90004
92004
|
closeSocket(audioLoopUdp);
|
|
90005
|
-
throw new Error(`export-hap: outbound SrtpSession init failed: ${errMsg$
|
|
92005
|
+
throw new Error(`export-hap: outbound SrtpSession init failed: ${errMsg$11(err)}`, { cause: err });
|
|
90006
92006
|
}
|
|
90007
92007
|
let upstreamAudioSrtp = null;
|
|
90008
92008
|
try {
|
|
@@ -90016,7 +92016,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90016
92016
|
profile: import_src.ProtectionProfileAes128CmHmacSha1_80
|
|
90017
92017
|
});
|
|
90018
92018
|
} catch (err) {
|
|
90019
|
-
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).warn("export-hap: SrtpSession init failed (upstream audio decrypt disabled)", { meta: { error: errMsg$
|
|
92019
|
+
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).warn("export-hap: SrtpSession init failed (upstream audio decrypt disabled)", { meta: { error: errMsg$11(err) } });
|
|
90020
92020
|
}
|
|
90021
92021
|
const videoSsrc = randomSsrc();
|
|
90022
92022
|
const audioSsrc = randomSsrc();
|
|
@@ -90133,7 +92133,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90133
92133
|
});
|
|
90134
92134
|
audioUdp.on("message", (packet, rinfo) => {
|
|
90135
92135
|
handleIncomingAudioRtp(session, packet, rinfo.address, bctx).catch((err) => {
|
|
90136
|
-
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).debug("export-hap: incoming-audio handler error (dropped)", { meta: { error: errMsg$
|
|
92136
|
+
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).debug("export-hap: incoming-audio handler error (dropped)", { meta: { error: errMsg$11(err) } });
|
|
90137
92137
|
});
|
|
90138
92138
|
});
|
|
90139
92139
|
logLoopbackBuffer(tagLog, request.sessionID, "video", videoLoop.buffer);
|
|
@@ -90329,7 +92329,7 @@ function readControllerRtcp(session, leg, packet, log) {
|
|
|
90329
92329
|
} catch (err) {
|
|
90330
92330
|
drop(session, "inbound-rtcp-decrypt-failed");
|
|
90331
92331
|
storeReceiverReports(session, leg, recordUnreadableRtcp(tally));
|
|
90332
|
-
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$
|
|
92332
|
+
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$11(err)}`, log);
|
|
90333
92333
|
return;
|
|
90334
92334
|
}
|
|
90335
92335
|
const outcome = ingestDecryptedRtcp(plaintext, tally);
|
|
@@ -91095,7 +93095,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91095
93095
|
session.upstreamRtpDecryptFailures += 1;
|
|
91096
93096
|
if (session.upstreamRtpDecryptFailures % 100 === 1) log.debug("export-hap: SRTP decrypt failed (rate-limited)", { meta: {
|
|
91097
93097
|
failures: session.upstreamRtpDecryptFailures,
|
|
91098
|
-
error: errMsg$
|
|
93098
|
+
error: errMsg$11(err)
|
|
91099
93099
|
} });
|
|
91100
93100
|
return;
|
|
91101
93101
|
}
|
|
@@ -91109,7 +93109,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91109
93109
|
rtpPayloadType = parsedRtp.header.payloadType;
|
|
91110
93110
|
} catch (err) {
|
|
91111
93111
|
drop(session, "upstream-parse-failed");
|
|
91112
|
-
log.debug("export-hap: RTP parse failed after decrypt", { meta: { error: errMsg$
|
|
93112
|
+
log.debug("export-hap: RTP parse failed after decrypt", { meta: { error: errMsg$11(err) } });
|
|
91113
93113
|
return;
|
|
91114
93114
|
}
|
|
91115
93115
|
const negotiatedAudioPt = session.lastStartParams?.audioPt;
|
|
@@ -91133,7 +93133,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91133
93133
|
if (session.intercomTalkSessionId === null) {
|
|
91134
93134
|
session.intercomTalkSessionId = "";
|
|
91135
93135
|
const opened = await openIntercomTalkSession(bctx).catch((err) => {
|
|
91136
|
-
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$
|
|
93136
|
+
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$11(err) } });
|
|
91137
93137
|
return null;
|
|
91138
93138
|
});
|
|
91139
93139
|
if (opened) {
|
|
@@ -91160,7 +93160,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91160
93160
|
drop(session, "upstream-push-failed");
|
|
91161
93161
|
log.debug("export-hap: intercom.pushTalkAudio failed (will re-open on next frame)", { meta: {
|
|
91162
93162
|
sequenceNumber: session.intercomPcmSequence,
|
|
91163
|
-
error: errMsg$
|
|
93163
|
+
error: errMsg$11(err)
|
|
91164
93164
|
} });
|
|
91165
93165
|
session.intercomTalkSessionId = null;
|
|
91166
93166
|
}
|
|
@@ -91192,7 +93192,7 @@ async function closeIntercomTalkSession(session, bctx) {
|
|
|
91192
93192
|
} catch (err) {
|
|
91193
93193
|
log.debug("export-hap: intercom.endTalkSession failed (continuing)", { meta: {
|
|
91194
93194
|
sessionId,
|
|
91195
|
-
error: errMsg$
|
|
93195
|
+
error: errMsg$11(err)
|
|
91196
93196
|
} });
|
|
91197
93197
|
}
|
|
91198
93198
|
}
|
|
@@ -91217,7 +93217,7 @@ function closeSocket(udp) {
|
|
|
91217
93217
|
function randomSsrc() {
|
|
91218
93218
|
return Math.floor(Math.random() * 2147483646) + 1 | 0;
|
|
91219
93219
|
}
|
|
91220
|
-
function errMsg$
|
|
93220
|
+
function errMsg$11(err) {
|
|
91221
93221
|
return err instanceof Error ? err.message : String(err);
|
|
91222
93222
|
}
|
|
91223
93223
|
//#endregion
|
|
@@ -91301,14 +93301,14 @@ async function buildDoorbell(input) {
|
|
|
91301
93301
|
}
|
|
91302
93302
|
log.info("export-hap: doorbell SINGLE_PRESS pushed to HomeKit", { meta: { ...delivery } });
|
|
91303
93303
|
} catch (err) {
|
|
91304
|
-
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$
|
|
93304
|
+
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$10(err) } });
|
|
91305
93305
|
}
|
|
91306
93306
|
});
|
|
91307
93307
|
return { async dispose() {
|
|
91308
93308
|
unsubscribe();
|
|
91309
93309
|
} };
|
|
91310
93310
|
}
|
|
91311
|
-
function errMsg$
|
|
93311
|
+
function errMsg$10(err) {
|
|
91312
93312
|
return err instanceof Error ? err.message : String(err);
|
|
91313
93313
|
}
|
|
91314
93314
|
//#endregion
|
|
@@ -91348,13 +93348,15 @@ var RESET_DEBOUNCE_MS = 5e3;
|
|
|
91348
93348
|
*/
|
|
91349
93349
|
async function buildMotionSensor(bctx, existing = null) {
|
|
91350
93350
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
91351
|
-
const
|
|
93351
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
93352
|
+
const motionService = existing ?? accessory.addService(import_dist.Service.MotionSensor, label);
|
|
93353
|
+
applyServiceLabel(motionService, label);
|
|
91352
93354
|
motionService.setCharacteristic(import_dist.Characteristic.MotionDetected, false);
|
|
91353
93355
|
try {
|
|
91354
93356
|
const detected = await proxy.motion?.isDetected({});
|
|
91355
93357
|
if (typeof detected === "boolean") motionService.updateCharacteristic(import_dist.Characteristic.MotionDetected, detected);
|
|
91356
93358
|
} catch (err) {
|
|
91357
|
-
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: initial motion hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
93359
|
+
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: initial motion hydrate failed (non-fatal)", { meta: { error: errMsg$9(err) } });
|
|
91358
93360
|
}
|
|
91359
93361
|
let resetTimer = null;
|
|
91360
93362
|
const armReset = () => {
|
|
@@ -91388,82 +93390,10 @@ async function buildMotionSensor(bctx, existing = null) {
|
|
|
91388
93390
|
}
|
|
91389
93391
|
} };
|
|
91390
93392
|
}
|
|
91391
|
-
function errMsg$
|
|
93393
|
+
function errMsg$9(err) {
|
|
91392
93394
|
return err instanceof Error ? err.message : String(err);
|
|
91393
93395
|
}
|
|
91394
93396
|
//#endregion
|
|
91395
|
-
//#region src/mappers/builders/service-label.ts
|
|
91396
|
-
/**
|
|
91397
|
-
* The ONE place a secondary service on the camera accessory gets its label.
|
|
91398
|
-
*
|
|
91399
|
-
* A "secondary service" here is a Switch or Lightbulb published alongside the
|
|
91400
|
-
* camera on the same accessory — the privacy switch, each accessory child
|
|
91401
|
-
* (siren, floodlight), each PTZ action. iOS Home renders these as their own
|
|
91402
|
-
* controls, and the operator has seen them as "Interruttore 1", "Interruttore
|
|
91403
|
-
* 2" through three separate rounds of fixes.
|
|
91404
|
-
*
|
|
91405
|
-
* ## Why `Name` alone cannot rename anything
|
|
91406
|
-
*
|
|
91407
|
-
* Two facts about hap-nodejs 2.1.7, both measured against the installed copy
|
|
91408
|
-
* rather than reasoned about:
|
|
91409
|
-
*
|
|
91410
|
-
* 1. `accessory.addService(Type, displayName, subtype)` ALREADY writes
|
|
91411
|
-
* `displayName` to `Characteristic.Name` (`Service` constructor). So every
|
|
91412
|
-
* round of this bug — including the one that moved the label onto
|
|
91413
|
-
* `ConfiguredName` — shipped with `Name` correctly set. "iOS had no name
|
|
91414
|
-
* to render" was never true.
|
|
91415
|
-
* 2. The mDNS configuration number (`c#`) is a sha1 over
|
|
91416
|
-
* `internalHAPRepresentation(false)`, which OMITS characteristic VALUES.
|
|
91417
|
-
* Changing the string in `Name` therefore does not bump `c#`, a paired
|
|
91418
|
-
* controller gets no signal to re-read `/accessories`, and the name it
|
|
91419
|
-
* cached at first enumeration stands forever.
|
|
91420
|
-
*
|
|
91421
|
-
* `Name` is also declared `pr` only — paired read, no write, no notify. It is
|
|
91422
|
-
* the seed a controller seeds its database from once; it is not a channel.
|
|
91423
|
-
*
|
|
91424
|
-
* ## Why `ConfiguredName`
|
|
91425
|
-
*
|
|
91426
|
-
* `ConfiguredName` (`000000E3`) is declared `pr | pw | ev` — the only name
|
|
91427
|
-
* characteristic a controller may write and may subscribe to. It is what iOS
|
|
91428
|
-
* 16+ reads for a service the user can rename, and adding it CHANGES the
|
|
91429
|
-
* accessory structure, so `c#` does bump and the controller re-reads.
|
|
91430
|
-
*
|
|
91431
|
-
* It was removed once because hap-nodejs logged
|
|
91432
|
-
*
|
|
91433
|
-
* ```
|
|
91434
|
-
* Characteristic not in required or optional characteristic section for
|
|
91435
|
-
* service Switch. Adding anyway.
|
|
91436
|
-
* ```
|
|
91437
|
-
*
|
|
91438
|
-
* That line is a WARNING, not a rejection: `Service.getCharacteristic` calls
|
|
91439
|
-
* `addCharacteristic` unconditionally and only then emits the warning. The
|
|
91440
|
-
* characteristic was always present and always published. hap-nodejs'
|
|
91441
|
-
* per-service optional lists simply predate `ConfiguredName` being valid on
|
|
91442
|
-
* any service.
|
|
91443
|
-
*
|
|
91444
|
-
* Registering it with {@link Service.addOptionalCharacteristic} first takes
|
|
91445
|
-
* the branch above the warning, so the accessory still builds with ZERO
|
|
91446
|
-
* characteristic warnings — which is what `service-naming.spec.ts` asserts.
|
|
91447
|
-
*
|
|
91448
|
-
* ## Scope
|
|
91449
|
-
*
|
|
91450
|
-
* Switch- and Lightbulb-shaped services only. `Service.MotionSensor` on a
|
|
91451
|
-
* camera accessory is not a separately named tile in iOS Home, so giving it a
|
|
91452
|
-
* writable name would be a guess, and this module does not guess.
|
|
91453
|
-
*/
|
|
91454
|
-
/**
|
|
91455
|
-
* Publish `name` as both the immutable `Name` and the controller-visible
|
|
91456
|
-
* `ConfiguredName` of `service`.
|
|
91457
|
-
*
|
|
91458
|
-
* `name` must already be HAP-valid — build it with `service-names.ts`, which
|
|
91459
|
-
* cannot return a string hap-nodejs' `checkName` would warn about.
|
|
91460
|
-
*/
|
|
91461
|
-
function applyServiceLabel(service, name) {
|
|
91462
|
-
service.setCharacteristic(import_dist.Characteristic.Name, name);
|
|
91463
|
-
if (!service.optionalCharacteristics.some((characteristic) => characteristic.UUID === import_dist.Characteristic.ConfiguredName.UUID)) service.addOptionalCharacteristic(import_dist.Characteristic.ConfiguredName);
|
|
91464
|
-
service.setCharacteristic(import_dist.Characteristic.ConfiguredName, name);
|
|
91465
|
-
}
|
|
91466
|
-
//#endregion
|
|
91467
93397
|
//#region src/mappers/builders/privacy-switch.ts
|
|
91468
93398
|
/**
|
|
91469
93399
|
* Privacy-mask switch builder — turns the camstack `privacy-mask` cap's
|
|
@@ -91491,7 +93421,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91491
93421
|
const status = await proxy.privacyMask?.getStatus({});
|
|
91492
93422
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91493
93423
|
} catch (err) {
|
|
91494
|
-
log.debug("export-hap: privacy-mask getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
93424
|
+
log.debug("export-hap: privacy-mask getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$8(err) } });
|
|
91495
93425
|
}
|
|
91496
93426
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91497
93427
|
const enabled = value === true;
|
|
@@ -91500,7 +93430,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91500
93430
|
} catch (err) {
|
|
91501
93431
|
log.warn("export-hap: privacy-mask setMask failed", { meta: {
|
|
91502
93432
|
enabled,
|
|
91503
|
-
error: errMsg$
|
|
93433
|
+
error: errMsg$8(err)
|
|
91504
93434
|
} });
|
|
91505
93435
|
}
|
|
91506
93436
|
});
|
|
@@ -91518,7 +93448,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91518
93448
|
} catch {}
|
|
91519
93449
|
} };
|
|
91520
93450
|
}
|
|
91521
|
-
function errMsg$
|
|
93451
|
+
function errMsg$8(err) {
|
|
91522
93452
|
return err instanceof Error ? err.message : String(err);
|
|
91523
93453
|
}
|
|
91524
93454
|
//#endregion
|
|
@@ -91609,7 +93539,7 @@ async function buildPtz(bctx) {
|
|
|
91609
93539
|
} catch (err) {
|
|
91610
93540
|
log.warn("export-hap: ptz.goToPreset failed", { meta: {
|
|
91611
93541
|
presetId: preset.id,
|
|
91612
|
-
error: errMsg$
|
|
93542
|
+
error: errMsg$7(err)
|
|
91613
93543
|
} });
|
|
91614
93544
|
}
|
|
91615
93545
|
armReset(() => service.updateCharacteristic(import_dist.Characteristic.On, false), MOMENTARY_RESET_MS);
|
|
@@ -91627,14 +93557,14 @@ async function buildPtz(bctx) {
|
|
|
91627
93557
|
try {
|
|
91628
93558
|
await proxy.ptz?.stop({});
|
|
91629
93559
|
} catch (err) {
|
|
91630
|
-
log.debug("export-hap: ptz.stop failed (non-fatal)", { meta: { error: errMsg$
|
|
93560
|
+
log.debug("export-hap: ptz.stop failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91631
93561
|
}
|
|
91632
93562
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91633
93563
|
}, options.ptzPulseMs);
|
|
91634
93564
|
} catch (err) {
|
|
91635
93565
|
log.warn("export-hap: ptz.continuousMove failed", { meta: {
|
|
91636
93566
|
dir: dir.label,
|
|
91637
|
-
error: errMsg$
|
|
93567
|
+
error: errMsg$7(err)
|
|
91638
93568
|
} });
|
|
91639
93569
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91640
93570
|
}
|
|
@@ -91657,7 +93587,7 @@ async function readPresets(bctx) {
|
|
|
91657
93587
|
name: p.name
|
|
91658
93588
|
}));
|
|
91659
93589
|
} catch (err) {
|
|
91660
|
-
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$
|
|
93590
|
+
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91661
93591
|
return [];
|
|
91662
93592
|
}
|
|
91663
93593
|
}
|
|
@@ -91672,7 +93602,7 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91672
93602
|
const status = await proxy.ptzAutotrack.getStatus({});
|
|
91673
93603
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91674
93604
|
} catch (err) {
|
|
91675
|
-
log.debug("export-hap: ptzAutotrack.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
93605
|
+
log.debug("export-hap: ptzAutotrack.getStatus failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91676
93606
|
}
|
|
91677
93607
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91678
93608
|
const enabled = value === true;
|
|
@@ -91681,13 +93611,13 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91681
93611
|
} catch (err) {
|
|
91682
93612
|
log.warn("export-hap: ptzAutotrack.setEnabled failed", { meta: {
|
|
91683
93613
|
enabled,
|
|
91684
|
-
error: errMsg$
|
|
93614
|
+
error: errMsg$7(err)
|
|
91685
93615
|
} });
|
|
91686
93616
|
}
|
|
91687
93617
|
});
|
|
91688
93618
|
return { async dispose() {} };
|
|
91689
93619
|
}
|
|
91690
|
-
function errMsg$
|
|
93620
|
+
function errMsg$7(err) {
|
|
91691
93621
|
return err instanceof Error ? err.message : String(err);
|
|
91692
93622
|
}
|
|
91693
93623
|
//#endregion
|
|
@@ -92718,13 +94648,13 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92718
94648
|
const switchStatus = await proxy.switch?.getStatus({});
|
|
92719
94649
|
if (switchStatus && typeof switchStatus.on === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, switchStatus.on);
|
|
92720
94650
|
} catch (err) {
|
|
92721
|
-
log.debug("export-hap: child switch.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
94651
|
+
log.debug("export-hap: child switch.getStatus failed (non-fatal)", { meta: { error: errMsg$6(err) } });
|
|
92722
94652
|
}
|
|
92723
94653
|
if (useLightbulb) try {
|
|
92724
94654
|
const status = await proxy.brightness?.getStatus({});
|
|
92725
94655
|
if (status && typeof status.percentage === "number") service.updateCharacteristic(import_dist.Characteristic.Brightness, status.percentage);
|
|
92726
94656
|
} catch (err) {
|
|
92727
|
-
log.debug("export-hap: child brightness.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
94657
|
+
log.debug("export-hap: child brightness.getStatus failed (non-fatal)", { meta: { error: errMsg$6(err) } });
|
|
92728
94658
|
}
|
|
92729
94659
|
if (hasSwitch) service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
92730
94660
|
const on = value === true;
|
|
@@ -92733,7 +94663,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92733
94663
|
} catch (err) {
|
|
92734
94664
|
log.warn("export-hap: child switch.setState failed", { meta: {
|
|
92735
94665
|
on,
|
|
92736
|
-
error: errMsg$
|
|
94666
|
+
error: errMsg$6(err)
|
|
92737
94667
|
} });
|
|
92738
94668
|
}
|
|
92739
94669
|
});
|
|
@@ -92745,7 +94675,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92745
94675
|
} catch (err) {
|
|
92746
94676
|
log.warn("export-hap: child brightness.setBrightness failed", { meta: {
|
|
92747
94677
|
percentage,
|
|
92748
|
-
error: errMsg$
|
|
94678
|
+
error: errMsg$6(err)
|
|
92749
94679
|
} });
|
|
92750
94680
|
}
|
|
92751
94681
|
});
|
|
@@ -92768,7 +94698,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92768
94698
|
} catch {}
|
|
92769
94699
|
} };
|
|
92770
94700
|
}
|
|
92771
|
-
function errMsg$
|
|
94701
|
+
function errMsg$6(err) {
|
|
92772
94702
|
return err instanceof Error ? err.message : String(err);
|
|
92773
94703
|
}
|
|
92774
94704
|
//#endregion
|
|
@@ -92795,7 +94725,7 @@ async function buildChildServicesFor(input) {
|
|
|
92795
94725
|
for (const h of handles) try {
|
|
92796
94726
|
await h.dispose();
|
|
92797
94727
|
} catch (err) {
|
|
92798
|
-
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: child service dispose failed (continuing)", { meta: { error: errMsg$
|
|
94728
|
+
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: child service dispose failed (continuing)", { meta: { error: errMsg$5(err) } });
|
|
92799
94729
|
}
|
|
92800
94730
|
} };
|
|
92801
94731
|
}
|
|
@@ -92811,7 +94741,7 @@ async function listChildren(ctx, parentNumericId) {
|
|
|
92811
94741
|
features: Array.isArray(c.features) ? c.features.filter((f) => typeof f === "string") : []
|
|
92812
94742
|
}));
|
|
92813
94743
|
} catch (err) {
|
|
92814
|
-
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: deviceManager.getChildren failed (non-fatal)", { meta: { error: errMsg$
|
|
94744
|
+
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: deviceManager.getChildren failed (non-fatal)", { meta: { error: errMsg$5(err) } });
|
|
92815
94745
|
return [];
|
|
92816
94746
|
}
|
|
92817
94747
|
}
|
|
@@ -92827,10 +94757,76 @@ function asDeviceType(raw) {
|
|
|
92827
94757
|
for (const value of Object.values(DeviceType)) if (value === lower) return value;
|
|
92828
94758
|
return DeviceType.Generic;
|
|
92829
94759
|
}
|
|
92830
|
-
function errMsg$
|
|
94760
|
+
function errMsg$5(err) {
|
|
92831
94761
|
return err instanceof Error ? err.message : String(err);
|
|
92832
94762
|
}
|
|
92833
94763
|
//#endregion
|
|
94764
|
+
//#region src/mappers/kind.ts
|
|
94765
|
+
/**
|
|
94766
|
+
* Which orchestrator a device gets, which device types the picker offers, and
|
|
94767
|
+
* what a device's HomeKit accessory is called on the wire.
|
|
94768
|
+
*
|
|
94769
|
+
* Separate from `index.ts` (the factory registry) only so the orchestrators can
|
|
94770
|
+
* import the uuid convention without importing the registry that imports them.
|
|
94771
|
+
*/
|
|
94772
|
+
var SUPPORTED_MAPPER_KINDS = ["camera", "generic"];
|
|
94773
|
+
/**
|
|
94774
|
+
* The device types the Export picker offers, and the answer to the cap's
|
|
94775
|
+
* `listSupportedDeviceKinds`.
|
|
94776
|
+
*
|
|
94777
|
+
* A UI FILTER, nothing more. Whether a device actually exports anything is
|
|
94778
|
+
* decided by its capabilities (`rowsForCaps` in the capability table) — the
|
|
94779
|
+
* doctrine the HA exporter writes down, and the thing that confined the
|
|
94780
|
+
* previous exporter to cameras when it was ignored.
|
|
94781
|
+
*
|
|
94782
|
+
* Tier A: the types whose HomeKit services exist today and need no
|
|
94783
|
+
* feature-dependent shape. `cover`, `climate`, `fan`, `valve`, `humidifier`,
|
|
94784
|
+
* `water-heater`, `alarm-panel`, `button` and `media-player` are deliberately
|
|
94785
|
+
* absent — each needs a service whose semantics the table cannot yet honour,
|
|
94786
|
+
* and offering the tab before the mapping exists is how an operator gets an
|
|
94787
|
+
* accessory that pairs and does nothing.
|
|
94788
|
+
*/
|
|
94789
|
+
var HAP_EXPORTABLE_DEVICE_TYPES = [
|
|
94790
|
+
DeviceType.Camera,
|
|
94791
|
+
DeviceType.Light,
|
|
94792
|
+
DeviceType.Switch,
|
|
94793
|
+
DeviceType.Siren,
|
|
94794
|
+
DeviceType.Sensor,
|
|
94795
|
+
DeviceType.Lock,
|
|
94796
|
+
DeviceType.Presence,
|
|
94797
|
+
DeviceType.Generic
|
|
94798
|
+
];
|
|
94799
|
+
/**
|
|
94800
|
+
* Resolve the orchestrator for a device from its TYPE.
|
|
94801
|
+
*
|
|
94802
|
+
* `null` means "no Export tab": the picker must not offer a type no
|
|
94803
|
+
* orchestrator can build. An UNKNOWN type (the device-manager read failed) maps
|
|
94804
|
+
* to `camera` — that is what every entry persisted before this function existed
|
|
94805
|
+
* carries, and a transient API failure must never re-shape an exposed camera.
|
|
94806
|
+
*/
|
|
94807
|
+
function pickMapperKind(deviceType) {
|
|
94808
|
+
if (deviceType === null || deviceType === void 0 || deviceType.length === 0) return "camera";
|
|
94809
|
+
if (deviceType === DeviceType.Camera) return "camera";
|
|
94810
|
+
return HAP_EXPORTABLE_DEVICE_TYPES.find((type) => type === deviceType) === void 0 ? null : "generic";
|
|
94811
|
+
}
|
|
94812
|
+
/**
|
|
94813
|
+
* The deterministic HAP accessory UUID for a device.
|
|
94814
|
+
*
|
|
94815
|
+
* One function, because three call sites depend on the answer agreeing: the
|
|
94816
|
+
* orchestrator that builds the accessory, `unexposeDevice` (which wipes
|
|
94817
|
+
* hap-nodejs' pairing blobs by uuid, on a device whose mapper is already gone)
|
|
94818
|
+
* and the export sync state that records it.
|
|
94819
|
+
*
|
|
94820
|
+
* The camera namespace is FROZEN — every camera paired to date derives its MAC
|
|
94821
|
+
* from `sha256("camstack:hap:" + uuid)` of this exact string, and changing it
|
|
94822
|
+
* would make every paired camera a stranger. Generic devices get their own
|
|
94823
|
+
* namespace so a switch and a camera that happen to share a device id are not
|
|
94824
|
+
* the same HomeKit accessory.
|
|
94825
|
+
*/
|
|
94826
|
+
function accessoryUuidFor(kind, deviceId) {
|
|
94827
|
+
return import_dist.uuid.generate(kind === "camera" ? `camstack:camera:${deviceId}` : `camstack:device:${deviceId}`);
|
|
94828
|
+
}
|
|
94829
|
+
//#endregion
|
|
92834
94830
|
//#region src/mappers/camera-accessory.ts
|
|
92835
94831
|
/**
|
|
92836
94832
|
* Camera accessory orchestrator — given a camstack deviceId, builds one
|
|
@@ -92863,9 +94859,9 @@ async function buildCameraAccessory(input) {
|
|
|
92863
94859
|
const capNames = new Set(proxy.binding?.entries.map((e) => e.capName) ?? []);
|
|
92864
94860
|
const isDoorbell = capNames.has("doorbell");
|
|
92865
94861
|
const category = isDoorbell ? import_dist.Categories.VIDEO_DOORBELL : import_dist.Categories.IP_CAMERA;
|
|
92866
|
-
const accessory = new import_dist.Accessory(displayName,
|
|
94862
|
+
const accessory = new import_dist.Accessory(displayName, accessoryUuidFor("camera", numericId));
|
|
92867
94863
|
accessory.category = category;
|
|
92868
|
-
await populateAccessoryInfo(accessory, proxy, displayName);
|
|
94864
|
+
await populateAccessoryInfo(accessory, proxy, displayName, "Camera");
|
|
92869
94865
|
const bctx = {
|
|
92870
94866
|
ctx,
|
|
92871
94867
|
accessory,
|
|
@@ -92923,55 +94919,462 @@ async function buildCameraAccessory(input) {
|
|
|
92923
94919
|
for (const h of handles) try {
|
|
92924
94920
|
await h.dispose();
|
|
92925
94921
|
} catch (err) {
|
|
92926
|
-
log.debug("export-hap: builder dispose failed (continuing)", { meta: { error: errMsg$
|
|
94922
|
+
log.debug("export-hap: builder dispose failed (continuing)", { meta: { error: errMsg$4(err) } });
|
|
92927
94923
|
}
|
|
92928
94924
|
try {
|
|
92929
94925
|
await streams.dispose();
|
|
92930
94926
|
} catch (err) {
|
|
92931
|
-
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$
|
|
94927
|
+
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$4(err) } });
|
|
92932
94928
|
}
|
|
92933
94929
|
}
|
|
92934
94930
|
};
|
|
92935
94931
|
}
|
|
92936
|
-
|
|
92937
|
-
|
|
92938
|
-
|
|
94932
|
+
function errMsg$4(err) {
|
|
94933
|
+
return err instanceof Error ? err.message : String(err);
|
|
94934
|
+
}
|
|
94935
|
+
//#endregion
|
|
94936
|
+
//#region src/mappers/builders/generic/lock.ts
|
|
94937
|
+
/**
|
|
94938
|
+
* `lock-control` → `Service.LockMechanism`.
|
|
94939
|
+
*
|
|
94940
|
+
* Not a sensor row: HomeKit models a lock as two characteristics, a CURRENT
|
|
94941
|
+
* state the accessory owns and a TARGET state the controller writes, and the
|
|
94942
|
+
* cap's five states do not map onto either one alone.
|
|
94943
|
+
*
|
|
94944
|
+
* ```
|
|
94945
|
+
* cap state LockCurrentState LockTargetState
|
|
94946
|
+
* locked SECURED SECURED
|
|
94947
|
+
* unlocked UNSECURED UNSECURED
|
|
94948
|
+
* locking UNKNOWN SECURED (in flight — do not claim SECURED)
|
|
94949
|
+
* unlocking UNKNOWN UNSECURED
|
|
94950
|
+
* jammed JAMMED unchanged (the controller's intent stands)
|
|
94951
|
+
* ```
|
|
94952
|
+
*
|
|
94953
|
+
* Reporting SECURED while the bolt is still moving is the failure worth naming:
|
|
94954
|
+
* iOS renders the lock as closed, the operator walks away, and the motor stalls
|
|
94955
|
+
* behind them. `UNKNOWN` is the honest answer for an in-flight transition.
|
|
94956
|
+
*
|
|
94957
|
+
* `open` (the cap's third method, for locks with a latch/buzzer) has no HomeKit
|
|
94958
|
+
* counterpart on this service and is deliberately not wired — a Switch that
|
|
94959
|
+
* silently buzzes a door open would be a second knob nobody asked for.
|
|
94960
|
+
*/
|
|
94961
|
+
var SUBTYPE = "lock-control";
|
|
94962
|
+
var readLock = reader(LockControlStatusSchema.pick({ state: true }), (status) => {
|
|
94963
|
+
const current = status.state === "locked" ? import_dist.Characteristic.LockCurrentState.SECURED : status.state === "unlocked" ? import_dist.Characteristic.LockCurrentState.UNSECURED : status.state === "jammed" ? import_dist.Characteristic.LockCurrentState.JAMMED : import_dist.Characteristic.LockCurrentState.UNKNOWN;
|
|
94964
|
+
const target = status.state === "locked" || status.state === "locking" ? import_dist.Characteristic.LockTargetState.SECURED : status.state === "unlocked" || status.state === "unlocking" ? import_dist.Characteristic.LockTargetState.UNSECURED : null;
|
|
94965
|
+
return [{
|
|
94966
|
+
characteristic: import_dist.Characteristic.LockCurrentState,
|
|
94967
|
+
value: current
|
|
94968
|
+
}, ...target === null ? [] : [{
|
|
94969
|
+
characteristic: import_dist.Characteristic.LockTargetState,
|
|
94970
|
+
value: target
|
|
94971
|
+
}]];
|
|
94972
|
+
});
|
|
94973
|
+
async function buildLockMechanism(bctx) {
|
|
94974
|
+
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
94975
|
+
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
94976
|
+
const label = hapServiceName([displayName], `Lock ${numericDeviceId}`);
|
|
94977
|
+
const service = accessory.addService(import_dist.Service.LockMechanism, label, SUBTYPE);
|
|
94978
|
+
applyServiceLabel(service, label);
|
|
94979
|
+
const apply = (status, source) => {
|
|
94980
|
+
const updates = readLock(status);
|
|
94981
|
+
if (updates.length === 0) {
|
|
94982
|
+
log.debug("export-hap: lock status did not match the cap schema — no update", { meta: { source } });
|
|
94983
|
+
return;
|
|
94984
|
+
}
|
|
94985
|
+
applyUpdates(service, updates);
|
|
94986
|
+
};
|
|
92939
94987
|
try {
|
|
92940
|
-
const
|
|
92941
|
-
|
|
92942
|
-
|
|
92943
|
-
|
|
92944
|
-
|
|
92945
|
-
|
|
92946
|
-
|
|
92947
|
-
|
|
94988
|
+
const status = await proxy.lockControl?.getStatus({});
|
|
94989
|
+
if (status !== void 0 && status !== null) apply(status, "getStatus");
|
|
94990
|
+
} catch (err) {
|
|
94991
|
+
log.debug("export-hap: lock getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$3(err) } });
|
|
94992
|
+
}
|
|
94993
|
+
service.getCharacteristic(import_dist.Characteristic.LockTargetState).onSet(async (value) => {
|
|
94994
|
+
const secure = value === import_dist.Characteristic.LockTargetState.SECURED;
|
|
94995
|
+
try {
|
|
94996
|
+
if (secure) await proxy.lockControl?.lock({});
|
|
94997
|
+
else await proxy.lockControl?.unlock({});
|
|
94998
|
+
} catch (err) {
|
|
94999
|
+
log.warn("export-hap: lock command failed", { meta: {
|
|
95000
|
+
secure,
|
|
95001
|
+
error: errMsg$3(err)
|
|
95002
|
+
} });
|
|
95003
|
+
}
|
|
95004
|
+
});
|
|
95005
|
+
const unsubscribe = proxy.state.lockControl?.subscribe((value) => {
|
|
95006
|
+
if (value === void 0 || value === null) return;
|
|
95007
|
+
apply(value, "slice");
|
|
95008
|
+
}) ?? null;
|
|
95009
|
+
return { async dispose() {
|
|
95010
|
+
try {
|
|
95011
|
+
unsubscribe?.();
|
|
95012
|
+
} catch {}
|
|
95013
|
+
} };
|
|
92948
95014
|
}
|
|
92949
|
-
function
|
|
92950
|
-
return
|
|
95015
|
+
function errMsg$3(err) {
|
|
95016
|
+
return err instanceof Error ? err.message : String(err);
|
|
95017
|
+
}
|
|
95018
|
+
//#endregion
|
|
95019
|
+
//#region src/mappers/builders/generic/sensor-service.ts
|
|
95020
|
+
async function buildSensorService(input) {
|
|
95021
|
+
const { bctx, spec, name, subtype } = input;
|
|
95022
|
+
const { ctx, accessory, proxy, numericDeviceId } = bctx;
|
|
95023
|
+
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
95024
|
+
const label = hapServiceName([name], spec.label);
|
|
95025
|
+
const service = spec.addService(accessory, label, subtype);
|
|
95026
|
+
applyServiceLabel(service, label);
|
|
95027
|
+
const apply = (status, source) => {
|
|
95028
|
+
const updates = spec.read(status);
|
|
95029
|
+
if (updates.length === 0) {
|
|
95030
|
+
log.debug("export-hap: sensor status did not match the cap schema — no update", { meta: {
|
|
95031
|
+
subtype,
|
|
95032
|
+
source
|
|
95033
|
+
} });
|
|
95034
|
+
return;
|
|
95035
|
+
}
|
|
95036
|
+
applyUpdates(service, updates);
|
|
95037
|
+
};
|
|
95038
|
+
try {
|
|
95039
|
+
const status = await spec.getStatus(proxy);
|
|
95040
|
+
if (status !== void 0 && status !== null) apply(status, "getStatus");
|
|
95041
|
+
} catch (err) {
|
|
95042
|
+
log.debug("export-hap: sensor getStatus hydrate failed (non-fatal)", { meta: {
|
|
95043
|
+
subtype,
|
|
95044
|
+
error: errMsg$2(err)
|
|
95045
|
+
} });
|
|
95046
|
+
}
|
|
95047
|
+
const unsubscribe = spec.subscribe(proxy, (value) => {
|
|
95048
|
+
if (value === void 0 || value === null) return;
|
|
95049
|
+
apply(value, "slice");
|
|
95050
|
+
});
|
|
95051
|
+
return { async dispose() {
|
|
95052
|
+
try {
|
|
95053
|
+
unsubscribe?.();
|
|
95054
|
+
} catch {}
|
|
95055
|
+
} };
|
|
95056
|
+
}
|
|
95057
|
+
function errMsg$2(err) {
|
|
95058
|
+
return err instanceof Error ? err.message : String(err);
|
|
95059
|
+
}
|
|
95060
|
+
//#endregion
|
|
95061
|
+
//#region src/mappers/builders/generic/cap-service-table.ts
|
|
95062
|
+
/**
|
|
95063
|
+
* The capability→HomeKit-service TABLE for non-camera devices.
|
|
95064
|
+
*
|
|
95065
|
+
* This is the whole coverage decision for the generic export path, and it is
|
|
95066
|
+
* deliberately a table rather than a `switch` on `DeviceType`. The doctrine is
|
|
95067
|
+
* the one the Home Assistant exporter already writes down: *coverage is decided
|
|
95068
|
+
* by a device's capabilities, not its type — restricting the picker by type is
|
|
95069
|
+
* what confined the previous exporter to cameras.* A `DeviceType` here is only
|
|
95070
|
+
* ever a UI filter (`HAP_EXPORTABLE_DEVICE_TYPES`) or an icon
|
|
95071
|
+
* (`ACCESSORY_CATEGORY_BY_TYPE`); it never decides whether something exports.
|
|
95072
|
+
*
|
|
95073
|
+
* The shape mirrors `CAP_ENTITY_MAP` in
|
|
95074
|
+
* `packages/addon-provider-homeassistant/src/ha-export/entity-catalog.ts` so
|
|
95075
|
+
* the two can be unified later — one row per capability, keyed by the cap name
|
|
95076
|
+
* exactly as it appears in `proxy.binding.entries[].capName`.
|
|
95077
|
+
*
|
|
95078
|
+
* ## Reading a status
|
|
95079
|
+
*
|
|
95080
|
+
* Every row parses the cap's OWN Zod status schema rather than duck-typing the
|
|
95081
|
+
* payload, and it `.pick()`s only the fields it reads: a provider that omits a
|
|
95082
|
+
* timestamp must not silence a sensor, and a provider that sends the wrong
|
|
95083
|
+
* shape must not write a garbage characteristic. A parse that fails yields no
|
|
95084
|
+
* updates, and the builder that owns the service logs the drop — silence reads
|
|
95085
|
+
* as "the sensor never changed".
|
|
95086
|
+
*
|
|
95087
|
+
* ## Tier
|
|
95088
|
+
*
|
|
95089
|
+
* Tier A only: the caps whose HomeKit service exists today and needs no
|
|
95090
|
+
* feature-dependent shape (`cover` needs `cover-positionable`, `climate-control`
|
|
95091
|
+
* needs the dual-setpoint split, `alarm-panel` must survive a refused `arm`).
|
|
95092
|
+
* Those are a separate task; adding a row here must never mean adding a service
|
|
95093
|
+
* whose semantics this file cannot fully honour.
|
|
95094
|
+
*/
|
|
95095
|
+
/** HomeKit's floor for `CurrentAmbientLightLevel`; 0 lux is out of range. */
|
|
95096
|
+
var MIN_LUX = 1e-4;
|
|
95097
|
+
var SENSOR_SPECS = {
|
|
95098
|
+
contact: {
|
|
95099
|
+
label: "Contact",
|
|
95100
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.ContactSensor, name, subtype),
|
|
95101
|
+
getStatus: (proxy) => proxy.contact?.getStatus({}),
|
|
95102
|
+
subscribe: (proxy, onValue) => proxy.state.contact?.subscribe(onValue) ?? null,
|
|
95103
|
+
read: reader(ContactStatusSchema.pick({ entryOpen: true }), (status) => [{
|
|
95104
|
+
characteristic: import_dist.Characteristic.ContactSensorState,
|
|
95105
|
+
value: status.entryOpen ? import_dist.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : import_dist.Characteristic.ContactSensorState.CONTACT_DETECTED
|
|
95106
|
+
}])
|
|
95107
|
+
},
|
|
95108
|
+
motion: {
|
|
95109
|
+
label: "Motion",
|
|
95110
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.MotionSensor, name, subtype),
|
|
95111
|
+
getStatus: (proxy) => proxy.motion?.getStatus({}),
|
|
95112
|
+
subscribe: (proxy, onValue) => proxy.state.motion?.subscribe(onValue) ?? null,
|
|
95113
|
+
read: reader(MotionStatusSchema.pick({ detected: true }), (status) => [{
|
|
95114
|
+
characteristic: import_dist.Characteristic.MotionDetected,
|
|
95115
|
+
value: status.detected
|
|
95116
|
+
}])
|
|
95117
|
+
},
|
|
95118
|
+
presence: {
|
|
95119
|
+
label: "Presence",
|
|
95120
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.OccupancySensor, name, subtype),
|
|
95121
|
+
getStatus: (proxy) => proxy.presence?.getStatus({}),
|
|
95122
|
+
subscribe: (proxy, onValue) => proxy.state.presence?.subscribe(onValue) ?? null,
|
|
95123
|
+
read: reader(PresenceStatusSchema.pick({ state: true }), (status) => [{
|
|
95124
|
+
characteristic: import_dist.Characteristic.OccupancyDetected,
|
|
95125
|
+
value: status.state === "home" ? import_dist.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED : import_dist.Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED
|
|
95126
|
+
}])
|
|
95127
|
+
},
|
|
95128
|
+
smoke: {
|
|
95129
|
+
label: "Smoke",
|
|
95130
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.SmokeSensor, name, subtype),
|
|
95131
|
+
getStatus: (proxy) => proxy.smoke?.getStatus({}),
|
|
95132
|
+
subscribe: (proxy, onValue) => proxy.state.smoke?.subscribe(onValue) ?? null,
|
|
95133
|
+
read: reader(SmokeStatusSchema.pick({ detected: true }), (status) => [{
|
|
95134
|
+
characteristic: import_dist.Characteristic.SmokeDetected,
|
|
95135
|
+
value: status.detected ? import_dist.Characteristic.SmokeDetected.SMOKE_DETECTED : import_dist.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED
|
|
95136
|
+
}])
|
|
95137
|
+
},
|
|
95138
|
+
"carbon-monoxide": {
|
|
95139
|
+
label: "Carbon monoxide",
|
|
95140
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.CarbonMonoxideSensor, name, subtype),
|
|
95141
|
+
getStatus: (proxy) => proxy.carbonMonoxide?.getStatus({}),
|
|
95142
|
+
subscribe: (proxy, onValue) => proxy.state.carbonMonoxide?.subscribe(onValue) ?? null,
|
|
95143
|
+
read: reader(CarbonMonoxideStatusSchema.pick({ detected: true }), (status) => [{
|
|
95144
|
+
characteristic: import_dist.Characteristic.CarbonMonoxideDetected,
|
|
95145
|
+
value: status.detected ? import_dist.Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL : import_dist.Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL
|
|
95146
|
+
}])
|
|
95147
|
+
},
|
|
95148
|
+
flood: {
|
|
95149
|
+
label: "Leak",
|
|
95150
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.LeakSensor, name, subtype),
|
|
95151
|
+
getStatus: (proxy) => proxy.flood?.getStatus({}),
|
|
95152
|
+
subscribe: (proxy, onValue) => proxy.state.flood?.subscribe(onValue) ?? null,
|
|
95153
|
+
read: reader(FloodStatusSchema.pick({ flooded: true }), (status) => [{
|
|
95154
|
+
characteristic: import_dist.Characteristic.LeakDetected,
|
|
95155
|
+
value: status.flooded ? import_dist.Characteristic.LeakDetected.LEAK_DETECTED : import_dist.Characteristic.LeakDetected.LEAK_NOT_DETECTED
|
|
95156
|
+
}])
|
|
95157
|
+
},
|
|
95158
|
+
"temperature-sensor": {
|
|
95159
|
+
label: "Temperature",
|
|
95160
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.TemperatureSensor, name, subtype),
|
|
95161
|
+
getStatus: (proxy) => proxy.temperatureSensor?.getStatus({}),
|
|
95162
|
+
subscribe: (proxy, onValue) => proxy.state.temperatureSensor?.subscribe(onValue) ?? null,
|
|
95163
|
+
read: reader(TemperatureSensorStatusSchema.pick({ celsius: true }), (status) => [{
|
|
95164
|
+
characteristic: import_dist.Characteristic.CurrentTemperature,
|
|
95165
|
+
value: status.celsius
|
|
95166
|
+
}])
|
|
95167
|
+
},
|
|
95168
|
+
"humidity-sensor": {
|
|
95169
|
+
label: "Humidity",
|
|
95170
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.HumiditySensor, name, subtype),
|
|
95171
|
+
getStatus: (proxy) => proxy.humiditySensor?.getStatus({}),
|
|
95172
|
+
subscribe: (proxy, onValue) => proxy.state.humiditySensor?.subscribe(onValue) ?? null,
|
|
95173
|
+
read: reader(HumiditySensorStatusSchema.pick({ percent: true }), (status) => [{
|
|
95174
|
+
characteristic: import_dist.Characteristic.CurrentRelativeHumidity,
|
|
95175
|
+
value: status.percent
|
|
95176
|
+
}])
|
|
95177
|
+
},
|
|
95178
|
+
"ambient-light-sensor": {
|
|
95179
|
+
label: "Light level",
|
|
95180
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.LightSensor, name, subtype),
|
|
95181
|
+
getStatus: (proxy) => proxy.ambientLightSensor?.getStatus({}),
|
|
95182
|
+
subscribe: (proxy, onValue) => proxy.state.ambientLightSensor?.subscribe(onValue) ?? null,
|
|
95183
|
+
read: reader(AmbientLightSensorStatusSchema.pick({ lux: true }), (status) => [{
|
|
95184
|
+
characteristic: import_dist.Characteristic.CurrentAmbientLightLevel,
|
|
95185
|
+
value: Math.max(MIN_LUX, status.lux)
|
|
95186
|
+
}])
|
|
95187
|
+
}
|
|
95188
|
+
};
|
|
95189
|
+
/**
|
|
95190
|
+
* The `battery` row reads like a sensor but writes three characteristics from
|
|
95191
|
+
* one status, and the camera path publishes the SAME service from the same cap.
|
|
95192
|
+
* The derivation therefore lives once, in `battery.ts`, and both callers read
|
|
95193
|
+
* it from there.
|
|
95194
|
+
*/
|
|
95195
|
+
var BATTERY_SPEC = {
|
|
95196
|
+
label: "Battery",
|
|
95197
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.Battery, name, subtype),
|
|
95198
|
+
getStatus: (proxy) => proxy.battery?.getStatus({}),
|
|
95199
|
+
subscribe: (proxy, onValue) => proxy.state.battery?.subscribe(onValue) ?? null,
|
|
95200
|
+
read: batteryCharacteristicUpdates
|
|
95201
|
+
};
|
|
95202
|
+
function sensorRow(capName, spec) {
|
|
95203
|
+
return {
|
|
95204
|
+
caps: [capName],
|
|
95205
|
+
label: spec.label,
|
|
95206
|
+
build: ({ bctx, name }) => buildSensorService({
|
|
95207
|
+
bctx,
|
|
95208
|
+
spec,
|
|
95209
|
+
name,
|
|
95210
|
+
subtype: capName
|
|
95211
|
+
})
|
|
95212
|
+
};
|
|
95213
|
+
}
|
|
95214
|
+
/**
|
|
95215
|
+
* THE table. Order matters only for one thing: the first row that matches a
|
|
95216
|
+
* device decides the accessory's category when the device's own type does not.
|
|
95217
|
+
*/
|
|
95218
|
+
var HAP_CAP_SERVICES = [
|
|
95219
|
+
{
|
|
95220
|
+
caps: ["switch", "brightness"],
|
|
95221
|
+
label: "Power",
|
|
95222
|
+
build: ({ bctx, name, deviceType }) => buildChildSwitch({
|
|
95223
|
+
...bctx,
|
|
95224
|
+
displayName: name
|
|
95225
|
+
}, "switch", deviceType)
|
|
95226
|
+
},
|
|
95227
|
+
{
|
|
95228
|
+
caps: ["lock-control"],
|
|
95229
|
+
label: "Lock",
|
|
95230
|
+
build: ({ bctx, name }) => buildLockMechanism({
|
|
95231
|
+
...bctx,
|
|
95232
|
+
displayName: name
|
|
95233
|
+
})
|
|
95234
|
+
},
|
|
95235
|
+
{
|
|
95236
|
+
caps: ["battery"],
|
|
95237
|
+
label: BATTERY_SPEC.label,
|
|
95238
|
+
build: ({ bctx, name }) => buildSensorService({
|
|
95239
|
+
bctx,
|
|
95240
|
+
spec: BATTERY_SPEC,
|
|
95241
|
+
name,
|
|
95242
|
+
subtype: "battery"
|
|
95243
|
+
})
|
|
95244
|
+
},
|
|
95245
|
+
...Object.entries(SENSOR_SPECS).map(([capName, spec]) => sensorRow(capName, spec))
|
|
95246
|
+
];
|
|
95247
|
+
/**
|
|
95248
|
+
* The rows a device's bound capabilities select, in table order.
|
|
95249
|
+
*
|
|
95250
|
+
* A row matches when ANY of its caps is bound: a lamp with `switch` but no
|
|
95251
|
+
* `brightness` is still a Lightbulb-shaped row, and the builder degrades on its
|
|
95252
|
+
* own.
|
|
95253
|
+
*/
|
|
95254
|
+
function rowsForCaps(capNames) {
|
|
95255
|
+
return HAP_CAP_SERVICES.filter((row) => row.caps.some((cap) => capNames.has(cap)));
|
|
95256
|
+
}
|
|
95257
|
+
/**
|
|
95258
|
+
* The HomeKit accessory category per camstack device type — the icon iOS Home
|
|
95259
|
+
* draws and nothing else. Absent = `OTHER`, which is a valid accessory that
|
|
95260
|
+
* simply gets the generic tile.
|
|
95261
|
+
*
|
|
95262
|
+
* This is NOT a coverage decision. `HAP_EXPORTABLE_DEVICE_TYPES` (in
|
|
95263
|
+
* `mappers/index.ts`) filters the picker; whether a device exports anything is
|
|
95264
|
+
* decided by `rowsForCaps`.
|
|
95265
|
+
*/
|
|
95266
|
+
var ACCESSORY_CATEGORY_BY_TYPE = {
|
|
95267
|
+
[DeviceType.Switch]: import_dist.Categories.SWITCH,
|
|
95268
|
+
[DeviceType.Light]: import_dist.Categories.LIGHTBULB,
|
|
95269
|
+
[DeviceType.Siren]: import_dist.Categories.SWITCH,
|
|
95270
|
+
[DeviceType.Lock]: import_dist.Categories.DOOR_LOCK,
|
|
95271
|
+
[DeviceType.Sensor]: import_dist.Categories.SENSOR,
|
|
95272
|
+
[DeviceType.Presence]: import_dist.Categories.SENSOR
|
|
95273
|
+
};
|
|
95274
|
+
function accessoryCategoryFor(deviceType) {
|
|
95275
|
+
return ACCESSORY_CATEGORY_BY_TYPE[deviceType] ?? import_dist.Categories.OTHER;
|
|
95276
|
+
}
|
|
95277
|
+
//#endregion
|
|
95278
|
+
//#region src/mappers/generic-accessory.ts
|
|
95279
|
+
/**
|
|
95280
|
+
* Generic accessory orchestrator — every camstack device that is NOT a camera.
|
|
95281
|
+
*
|
|
95282
|
+
* Same three steps as the camera orchestrator, and deliberately nothing more:
|
|
95283
|
+
* 1. resolve the typed `DeviceProxy`,
|
|
95284
|
+
* 2. select rows from the capability→service table with the device's BOUND
|
|
95285
|
+
* capabilities (never its type — see `builders/generic/cap-service-table.ts`),
|
|
95286
|
+
* 3. let each row add its service to one `Accessory`.
|
|
95287
|
+
*
|
|
95288
|
+
* It publishes standalone, like the camera path: one accessory, one mDNS
|
|
95289
|
+
* advertisement, one pairing, the shared setup code. This addon is not a
|
|
95290
|
+
* bridge — that decision predates this file and is not re-litigated here.
|
|
95291
|
+
*
|
|
95292
|
+
* ## It REFUSES rather than publishing an empty tile
|
|
95293
|
+
*
|
|
95294
|
+
* A device whose capabilities select no row throws. The alternative is an
|
|
95295
|
+
* accessory carrying nothing but `AccessoryInformation`: it pairs, it appears
|
|
95296
|
+
* in the Home app, it does nothing, and the operator has no way to tell that
|
|
95297
|
+
* from a broken integration. The Export tab is gated on the same question
|
|
95298
|
+
* (`hap-export.addon.ts`), so reaching this throw means the device changed
|
|
95299
|
+
* shape between the tab rendering and the toggle landing.
|
|
95300
|
+
*/
|
|
95301
|
+
async function buildGenericAccessory(input) {
|
|
95302
|
+
const { ctx, deviceId, displayName, options } = input;
|
|
95303
|
+
const numericId = Number.parseInt(deviceId, 10);
|
|
95304
|
+
if (!Number.isFinite(numericId)) throw new Error(`export-hap: cannot map device '${deviceId}' — id is not numeric`);
|
|
95305
|
+
const log = ctx.logger.withTags({ deviceId: numericId });
|
|
95306
|
+
const proxy = await ctx.fetchDevice(numericId);
|
|
95307
|
+
const capNames = new Set(proxy.binding?.entries.map((e) => e.capName) ?? []);
|
|
95308
|
+
const rows = rowsForCaps(capNames);
|
|
95309
|
+
if (rows.length === 0) throw new Error(`export-hap: device ${numericId} carries no capability HomeKit can export (bound: ${[...capNames].toSorted().join(", ") || "none"})`);
|
|
95310
|
+
const deviceType = await resolveDeviceType(proxy);
|
|
95311
|
+
const accessory = new import_dist.Accessory(displayName, accessoryUuidFor("generic", numericId));
|
|
95312
|
+
accessory.category = accessoryCategoryFor(deviceType);
|
|
95313
|
+
await populateAccessoryInfo(accessory, proxy, displayName, "Accessory");
|
|
95314
|
+
const bctx = {
|
|
95315
|
+
ctx,
|
|
95316
|
+
accessory,
|
|
95317
|
+
proxy,
|
|
95318
|
+
numericDeviceId: numericId,
|
|
95319
|
+
displayName,
|
|
95320
|
+
options
|
|
95321
|
+
};
|
|
95322
|
+
const handles = [];
|
|
95323
|
+
for (const row of rows) {
|
|
95324
|
+
const name = rows.length === 1 ? displayName : row.label;
|
|
95325
|
+
handles.push(await row.build({
|
|
95326
|
+
bctx,
|
|
95327
|
+
name,
|
|
95328
|
+
deviceType
|
|
95329
|
+
}));
|
|
95330
|
+
}
|
|
95331
|
+
log.info("export-hap: built generic accessory", { meta: {
|
|
95332
|
+
deviceType,
|
|
95333
|
+
services: rows.map((row) => row.caps[0]).join(",")
|
|
95334
|
+
} });
|
|
95335
|
+
return {
|
|
95336
|
+
accessory,
|
|
95337
|
+
accessories: [accessory],
|
|
95338
|
+
async dispose() {
|
|
95339
|
+
for (const handle of handles) try {
|
|
95340
|
+
await handle.dispose();
|
|
95341
|
+
} catch (err) {
|
|
95342
|
+
log.debug("export-hap: generic builder dispose failed (continuing)", { meta: { error: errMsg$1(err) } });
|
|
95343
|
+
}
|
|
95344
|
+
}
|
|
95345
|
+
};
|
|
95346
|
+
}
|
|
95347
|
+
/**
|
|
95348
|
+
* The device's own type, or `Generic`.
|
|
95349
|
+
*
|
|
95350
|
+
* Used ONLY where one capability has two HomeKit shapes — a siren's
|
|
95351
|
+
* `brightness` is alarm volume, not luminosity — and for the accessory's icon.
|
|
95352
|
+
* `Generic` is the safe reading: it is the shape `child-switch.ts` already
|
|
95353
|
+
* treats as "a lamp if it dims, a switch otherwise".
|
|
95354
|
+
*/
|
|
95355
|
+
async function resolveDeviceType(proxy) {
|
|
95356
|
+
try {
|
|
95357
|
+
const device = await proxy.deviceManager?.getDevice({});
|
|
95358
|
+
const raw = typeof device?.type === "string" ? device.type.toLowerCase() : "";
|
|
95359
|
+
return Object.values(DeviceType).find((value) => value === raw) ?? DeviceType.Generic;
|
|
95360
|
+
} catch {
|
|
95361
|
+
return DeviceType.Generic;
|
|
95362
|
+
}
|
|
92951
95363
|
}
|
|
92952
95364
|
function errMsg$1(err) {
|
|
92953
95365
|
return err instanceof Error ? err.message : String(err);
|
|
92954
95366
|
}
|
|
92955
95367
|
//#endregion
|
|
92956
95368
|
//#region src/mappers/index.ts
|
|
92957
|
-
var
|
|
92958
|
-
|
|
95369
|
+
var REGISTRY = {
|
|
95370
|
+
camera: buildCameraAccessory,
|
|
95371
|
+
generic: buildGenericAccessory
|
|
95372
|
+
};
|
|
92959
95373
|
function getMapperFactory(kind) {
|
|
92960
95374
|
const factory = REGISTRY[kind];
|
|
92961
95375
|
if (!factory) throw new Error(`export-hap: no mapper registered for kind '${kind}'`);
|
|
92962
95376
|
return factory;
|
|
92963
95377
|
}
|
|
92964
|
-
/**
|
|
92965
|
-
* Resolve the best-fit mapper kind. With Round 2, the operator just
|
|
92966
|
-
* picks a camera and the orchestrator does the rest — the single
|
|
92967
|
-
* `camera` kind is returned unconditionally. We keep the function
|
|
92968
|
-
* signature for backwards-compat with the addon's existing
|
|
92969
|
-
* `exposeDevice` flow and to leave room for future device types (NVR,
|
|
92970
|
-
* climate sensor, ...).
|
|
92971
|
-
*/
|
|
92972
|
-
function pickMapperKind(_capabilities) {
|
|
92973
|
-
return "camera";
|
|
92974
|
-
}
|
|
92975
95378
|
//#endregion
|
|
92976
95379
|
//#region src/mappers/builders/stream-hwaccel-memo.ts
|
|
92977
95380
|
/**
|
|
@@ -93109,8 +95512,8 @@ function syncStateToJson(map) {
|
|
|
93109
95512
|
//#endregion
|
|
93110
95513
|
//#region src/hap-export.addon.ts
|
|
93111
95514
|
/**
|
|
93112
|
-
* HomeKit (HAP) export addon — publishes
|
|
93113
|
-
*
|
|
95515
|
+
* HomeKit (HAP) export addon — publishes selected camstack devices as
|
|
95516
|
+
* standalone HomeKit accessories.
|
|
93114
95517
|
*
|
|
93115
95518
|
* Operator flow:
|
|
93116
95519
|
* 1. Install + enable the addon (hub-only, group `export-hap`).
|
|
@@ -93121,10 +95524,10 @@ function syncStateToJson(map) {
|
|
|
93121
95524
|
* 3. The setup URI (`X-HM://…`) is logged AND surfaced via the
|
|
93122
95525
|
* `getStatus` cap method so the wizard UI can render the QR.
|
|
93123
95526
|
* 4. Operator opens iOS Home → + → scan QR → enter pincode.
|
|
93124
|
-
* 5. Operator hits "Expose to HomeKit" on individual camstack
|
|
93125
|
-
*
|
|
93126
|
-
*
|
|
93127
|
-
*
|
|
95527
|
+
* 5. Operator hits "Expose to HomeKit" on individual camstack devices.
|
|
95528
|
+
* Each exposed device is published as its OWN accessory with its own
|
|
95529
|
+
* mDNS advertisement, sharing one setup code — cameras cannot be
|
|
95530
|
+
* bridged, so nothing is.
|
|
93128
95531
|
*
|
|
93129
95532
|
* Exception — the hap-nodejs library's own `HAPStorage` lives under
|
|
93130
95533
|
* `ctx.dataDir/hap-store/`. That directory is library-internal: the
|
|
@@ -93133,23 +95536,20 @@ function syncStateToJson(map) {
|
|
|
93133
95536
|
* I/O for addon-owned data") explicitly scopes itself to OUR own data;
|
|
93134
95537
|
* library-managed blobs are out of scope.
|
|
93135
95538
|
*
|
|
93136
|
-
*
|
|
93137
|
-
*
|
|
93138
|
-
*
|
|
93139
|
-
*
|
|
93140
|
-
*
|
|
93141
|
-
*
|
|
93142
|
-
*
|
|
93143
|
-
*
|
|
93144
|
-
*
|
|
93145
|
-
*
|
|
93146
|
-
*
|
|
93147
|
-
*
|
|
93148
|
-
*
|
|
93149
|
-
*
|
|
93150
|
-
* What's deferred to Round 3+: cam→browser audio Opus transcode,
|
|
93151
|
-
* intercom upload bridge, HomeKit Secure Video, recording, native
|
|
93152
|
-
* H.264 stream tap (currently uses RTSP + ffmpeg copy).
|
|
95539
|
+
* ## Two accessory shapes, one rule about coverage
|
|
95540
|
+
*
|
|
95541
|
+
* `exposeDevice({deviceId})` resolves the device's TYPE to a mapper kind
|
|
95542
|
+
* (`mappers/kind.ts`) and hands off:
|
|
95543
|
+
*
|
|
95544
|
+
* - a camera → `mappers/camera-accessory.ts`: streams, HKSV recording,
|
|
95545
|
+
* doorbell, motion, intercom, PTZ, battery, privacy and every accessory
|
|
95546
|
+
* child, all as services on one accessory;
|
|
95547
|
+
* - anything else → `mappers/generic-accessory.ts`, driven by the
|
|
95548
|
+
* capability→service table in `mappers/builders/generic/`.
|
|
95549
|
+
*
|
|
95550
|
+
* The type only picks the SHAPE. What a device publishes is decided by the
|
|
95551
|
+
* capabilities bound to it — restricting coverage by type is precisely what
|
|
95552
|
+
* confined this exporter to cameras for its first three rounds.
|
|
93153
95553
|
*/
|
|
93154
95554
|
var DEFAULT_DEVICE_SETTINGS = {
|
|
93155
95555
|
streamPreference: "auto",
|
|
@@ -93192,7 +95592,6 @@ var DEFAULT_CONFIG = {
|
|
|
93192
95592
|
fixedPin: "",
|
|
93193
95593
|
interfaceName: "",
|
|
93194
95594
|
ptzPulseMs: 400,
|
|
93195
|
-
hksvPreview: false,
|
|
93196
95595
|
identity: {
|
|
93197
95596
|
username: "",
|
|
93198
95597
|
pincode: "",
|
|
@@ -93308,7 +95707,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93308
95707
|
...setup ? { setup } : {}
|
|
93309
95708
|
};
|
|
93310
95709
|
},
|
|
93311
|
-
listSupportedDeviceKinds: async () => [...
|
|
95710
|
+
listSupportedDeviceKinds: async () => [...HAP_EXPORTABLE_DEVICE_TYPES],
|
|
93312
95711
|
listExposedDevices: async () => Array.from(this.exposed.entries()).map(([deviceId, m]) => {
|
|
93313
95712
|
const entry = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93314
95713
|
return {
|
|
@@ -93393,9 +95792,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93393
95792
|
log.debug("export-hap: device already exposed — refreshing capabilities");
|
|
93394
95793
|
await this.detachMapper(deviceId);
|
|
93395
95794
|
}
|
|
93396
|
-
const
|
|
93397
|
-
|
|
93398
|
-
|
|
95795
|
+
const summary = await this.fetchDeviceSummary(numericId);
|
|
95796
|
+
const mapperKind = pickMapperKind(summary?.type);
|
|
95797
|
+
if (!mapperKind) throw new Error(`export-hap: device ${numericId} has type '${summary?.type ?? "unknown"}', which HomeKit export does not support`);
|
|
95798
|
+
const displayName = summary?.name ?? `Device ${deviceId}`;
|
|
93399
95799
|
const previous = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93400
95800
|
const baseEntry = carryForward({
|
|
93401
95801
|
deviceId,
|
|
@@ -93425,10 +95825,11 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93425
95825
|
async unexposeDevice(deviceId, options = {}) {
|
|
93426
95826
|
const numericId = Number.parseInt(deviceId, 10);
|
|
93427
95827
|
const log = this.ctx.logger.withTags({ deviceId: numericId });
|
|
95828
|
+
const mapperKind = this.findEntry(numericId)?.mapperKind ?? "camera";
|
|
93428
95829
|
await this.detachMapper(deviceId);
|
|
93429
95830
|
const next = this.config.exposed.filter((e) => e.deviceId !== deviceId);
|
|
93430
95831
|
if (next.length !== this.config.exposed.length) await this.updateGlobalSettings({ exposed: next });
|
|
93431
|
-
if (options.clearPairing !== false) clearPairingFiles(
|
|
95832
|
+
if (options.clearPairing !== false) clearPairingFiles(accessoryUuidFor(mapperKind, numericId), this.ctx.logger);
|
|
93432
95833
|
await this.forgetFingerprint(numericId);
|
|
93433
95834
|
log.info("export-hap: unexposed device");
|
|
93434
95835
|
}
|
|
@@ -93478,10 +95879,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93478
95879
|
/** Pending rebuild timers per deviceId — used to debounce. Cleared
|
|
93479
95880
|
* on detach so stale timers can't republish a removed accessory. */
|
|
93480
95881
|
pendingRebuildTimers = /* @__PURE__ */ new Map();
|
|
93481
|
-
/** Deterministic HAP accessory UUID for a device
|
|
93482
|
-
*
|
|
95882
|
+
/** Deterministic HAP accessory UUID for a device — the same one the
|
|
95883
|
+
* orchestrator built it with (`mappers/kind.ts`). */
|
|
93483
95884
|
hapAccessoryUuid(deviceId) {
|
|
93484
|
-
return
|
|
95885
|
+
return accessoryUuidFor(this.findEntry(deviceId)?.mapperKind ?? "camera", deviceId);
|
|
93485
95886
|
}
|
|
93486
95887
|
/**
|
|
93487
95888
|
* Export fingerprint of a device from its PERSISTED features + type
|
|
@@ -93640,31 +96041,79 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93640
96041
|
log.warn("export-hap: reconcile rebuild failed", { meta: { error: errMsg(err) } });
|
|
93641
96042
|
}
|
|
93642
96043
|
}
|
|
93643
|
-
|
|
93644
|
-
|
|
93645
|
-
|
|
96044
|
+
/**
|
|
96045
|
+
* The device's name and type, or `null` when the registry cannot answer.
|
|
96046
|
+
*
|
|
96047
|
+
* `null` is NOT "no such device" — it also covers a transient API failure,
|
|
96048
|
+
* and every caller treats it as "keep doing what was already being done"
|
|
96049
|
+
* rather than re-shaping an exposed accessory on incomplete information.
|
|
96050
|
+
*/
|
|
96051
|
+
async fetchDeviceSummary(deviceId) {
|
|
96052
|
+
if (!Number.isFinite(deviceId)) return null;
|
|
96053
|
+
try {
|
|
96054
|
+
const device = await this.ctx.api.deviceManager?.getDevice.query({ deviceId });
|
|
96055
|
+
if (!device) return null;
|
|
96056
|
+
return {
|
|
96057
|
+
name: device.name,
|
|
96058
|
+
type: device.type
|
|
96059
|
+
};
|
|
96060
|
+
} catch (err) {
|
|
96061
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: deviceManager.getDevice failed", { meta: { error: errMsg(err) } });
|
|
96062
|
+
return null;
|
|
96063
|
+
}
|
|
96064
|
+
}
|
|
96065
|
+
/**
|
|
96066
|
+
* Which accessory shape this device would get, or `null` for "no Export tab".
|
|
96067
|
+
*
|
|
96068
|
+
* Two questions, in this order, because they fail differently:
|
|
96069
|
+
* 1. the TYPE — a type with no orchestrator is refused outright;
|
|
96070
|
+
* 2. for a non-camera, the CAPABILITIES — a device carrying nothing the
|
|
96071
|
+
* table maps would publish an accessory that pairs and does nothing, and
|
|
96072
|
+
* the operator cannot tell that from a broken integration.
|
|
96073
|
+
*
|
|
96074
|
+
* A camera skips (2): the camera orchestrator has always published on type
|
|
96075
|
+
* alone, and adding a cap gate here would be a new way for an existing camera
|
|
96076
|
+
* to lose its Export tab. A registry that cannot answer at all also resolves
|
|
96077
|
+
* to `camera`, which is what every persisted entry predating this method
|
|
96078
|
+
* carries.
|
|
96079
|
+
*/
|
|
96080
|
+
async resolveExportKind(deviceId) {
|
|
96081
|
+
const summary = await this.fetchDeviceSummary(deviceId);
|
|
96082
|
+
const kind = pickMapperKind(summary?.type);
|
|
96083
|
+
if (kind === null) return null;
|
|
96084
|
+
if (kind === "camera") return "camera";
|
|
93646
96085
|
try {
|
|
93647
|
-
const
|
|
93648
|
-
|
|
96086
|
+
const proxy = await this.ctx.fetchDevice(deviceId);
|
|
96087
|
+
const capNames = new Set(proxy.binding?.entries.map((entry) => entry.capName) ?? []);
|
|
96088
|
+
if (rowsForCaps(capNames).length > 0) return "generic";
|
|
96089
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: no Export tab — no mapped caps", { meta: {
|
|
96090
|
+
type: summary?.type,
|
|
96091
|
+
caps: [...capNames].toSorted().join(",")
|
|
96092
|
+
} });
|
|
96093
|
+
return null;
|
|
93649
96094
|
} catch (err) {
|
|
93650
|
-
this.ctx.logger.withTags({ deviceId
|
|
96095
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: no Export tab — capability read failed", { meta: {
|
|
96096
|
+
type: summary?.type,
|
|
96097
|
+
error: errMsg(err)
|
|
96098
|
+
} });
|
|
96099
|
+
return null;
|
|
93651
96100
|
}
|
|
93652
|
-
return `Device ${deviceId}`;
|
|
93653
96101
|
}
|
|
93654
96102
|
globalSettingsSchema() {
|
|
93655
96103
|
return this.schema({ sections: [{
|
|
93656
96104
|
id: "export-hap",
|
|
93657
96105
|
title: "HomeKit Export",
|
|
93658
|
-
description: "Publishes
|
|
96106
|
+
description: "Publishes each exposed device as its own HomeKit accessory, sharing one setup code. Mark a device \"Expose to HomeKit\" on its settings page, then add it in the iOS Home app (+ → Add Accessory) and enter the setup code shown there.",
|
|
93659
96107
|
columns: 1,
|
|
93660
96108
|
fields: [
|
|
93661
96109
|
this.field({
|
|
93662
96110
|
type: "text",
|
|
93663
96111
|
key: "bridgeName",
|
|
93664
|
-
label: "
|
|
93665
|
-
description: "
|
|
96112
|
+
label: "Installation name",
|
|
96113
|
+
description: "Legacy. Every exposed device is published STANDALONE, under its own camstack device name — HomeKit cameras cannot be bridged, so nothing is bridged. This string only seeds the identity generated on first boot and is not shown in iOS Home.",
|
|
93666
96114
|
default: DEFAULT_CONFIG.bridgeName,
|
|
93667
|
-
requiresRestart: true
|
|
96115
|
+
requiresRestart: true,
|
|
96116
|
+
placement: { tab: "advanced" }
|
|
93668
96117
|
}),
|
|
93669
96118
|
this.field({
|
|
93670
96119
|
type: "number",
|
|
@@ -93700,21 +96149,13 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93700
96149
|
description: "Duration of a single pan/tilt momentary command issued by the PTZ direction switches in Apple Home. Lower = finer steps; higher = bigger sweeps per tap. Defaults to 400ms.",
|
|
93701
96150
|
default: DEFAULT_CONFIG.ptzPulseMs,
|
|
93702
96151
|
placement: { tab: "advanced" }
|
|
93703
|
-
}),
|
|
93704
|
-
this.field({
|
|
93705
|
-
type: "boolean",
|
|
93706
|
-
key: "hksvPreview",
|
|
93707
|
-
label: "HKSV Developer Preview (experimental)",
|
|
93708
|
-
description: "Advertise Apple's HomeKit Secure Video Developer Preview services — HEVC streaming and the WebRTC stream-management surface — alongside the classic camera profile. Apple published this specification on 2026-06-03 and no shipping controller is known to negotiate it yet. Leave off unless you are testing against a preview build: advertising unknown services to a paired controller can disturb the classic path that works today.",
|
|
93709
|
-
default: DEFAULT_CONFIG.hksvPreview,
|
|
93710
|
-
requiresRestart: true,
|
|
93711
|
-
placement: { tab: "advanced" }
|
|
93712
96152
|
})
|
|
93713
96153
|
]
|
|
93714
96154
|
}] });
|
|
93715
96155
|
}
|
|
93716
96156
|
async buildDeviceSettingsContribution(deviceId) {
|
|
93717
|
-
|
|
96157
|
+
const kind = await this.resolveExportKind(deviceId);
|
|
96158
|
+
if (kind === null) return null;
|
|
93718
96159
|
const entry = this.findEntry(deviceId);
|
|
93719
96160
|
const settings = entry?.settings ?? DEFAULT_DEVICE_SETTINGS;
|
|
93720
96161
|
const enabled = entry !== null;
|
|
@@ -93731,6 +96172,32 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93731
96172
|
} catch (err) {
|
|
93732
96173
|
this.ctx.logger.withTags({ deviceId }).debug("export-hap: setupURI failed for per-device contribution", { meta: { error: errMsg(err) } });
|
|
93733
96174
|
}
|
|
96175
|
+
const cameraFields = kind !== "camera" ? [] : [{
|
|
96176
|
+
type: "select",
|
|
96177
|
+
key: streamPreferenceKey,
|
|
96178
|
+
label: "Source stream (HomeKit)",
|
|
96179
|
+
description: "Which camstack profile slot HomeKit pulls. Auto = the broker picks the slot closest to 1080p at session start.",
|
|
96180
|
+
options: HAP_STREAM_PREFERENCE_OPTIONS,
|
|
96181
|
+
required: true,
|
|
96182
|
+
value: settings.streamPreference,
|
|
96183
|
+
showWhen: {
|
|
96184
|
+
field: enabledKey,
|
|
96185
|
+
equals: true
|
|
96186
|
+
},
|
|
96187
|
+
immediate: true
|
|
96188
|
+
}, {
|
|
96189
|
+
type: "boolean",
|
|
96190
|
+
key: hksvKey,
|
|
96191
|
+
label: "HomeKit recording (Secure Video)",
|
|
96192
|
+
description: "Offer “Stream and Allow Recording” in iOS Home. Requires iCloud+ and a home hub. Keeps a continuous 8s prebuffer for this camera (~0.7% of one CPU core, H.264 sources only).",
|
|
96193
|
+
style: "switch",
|
|
96194
|
+
value: resolveHksvRecording(settings),
|
|
96195
|
+
showWhen: {
|
|
96196
|
+
field: enabledKey,
|
|
96197
|
+
equals: true
|
|
96198
|
+
},
|
|
96199
|
+
immediate: true
|
|
96200
|
+
}];
|
|
93734
96201
|
return {
|
|
93735
96202
|
tabs: [{
|
|
93736
96203
|
id: "export",
|
|
@@ -93741,7 +96208,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93741
96208
|
sections: [{
|
|
93742
96209
|
id: "export-hap",
|
|
93743
96210
|
title: "HomeKit Export",
|
|
93744
|
-
description: "
|
|
96211
|
+
description: kind === "camera" ? "Publish this camera as its own HomeKit accessory so iOS Home can pair with it." : "Publish this device as its own HomeKit accessory. Its capabilities decide which controls iOS Home shows.",
|
|
93745
96212
|
tab: "export",
|
|
93746
96213
|
columns: 1,
|
|
93747
96214
|
order: 10,
|
|
@@ -93757,7 +96224,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93757
96224
|
type: "qr-code",
|
|
93758
96225
|
key: "__hap-pair-qr",
|
|
93759
96226
|
label: paired ? "Pairing QR (already paired)" : "Pairing QR",
|
|
93760
|
-
caption: paired ? `Already paired with at least one device. Scan from another iPhone / iPad to add it there too. Setup code: ${this.config.identity.pincode}.` : `Scan with the iOS Camera app to pair this
|
|
96227
|
+
caption: paired ? `Already paired with at least one device. Scan from another iPhone / iPad to add it there too. Setup code: ${this.config.identity.pincode}.` : `Scan with the iOS Camera app to pair this accessory in HomeKit. Setup code: ${this.config.identity.pincode}.`,
|
|
93761
96228
|
value: qrValue,
|
|
93762
96229
|
size: 192,
|
|
93763
96230
|
alt: `HomeKit pairing QR for ${name}`,
|
|
@@ -93770,38 +96237,12 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93770
96237
|
type: "boolean",
|
|
93771
96238
|
key: enabledKey,
|
|
93772
96239
|
label: "Expose to HomeKit",
|
|
93773
|
-
description: "Toggle to publish this
|
|
96240
|
+
description: "Toggle to publish this device as a HomeKit accessory.",
|
|
93774
96241
|
style: "switch",
|
|
93775
96242
|
value: enabled,
|
|
93776
96243
|
immediate: true
|
|
93777
96244
|
},
|
|
93778
|
-
|
|
93779
|
-
type: "select",
|
|
93780
|
-
key: streamPreferenceKey,
|
|
93781
|
-
label: "Source stream (HomeKit)",
|
|
93782
|
-
description: "Which camstack profile slot HomeKit pulls. Auto = the broker picks the slot closest to 1080p at session start.",
|
|
93783
|
-
options: HAP_STREAM_PREFERENCE_OPTIONS,
|
|
93784
|
-
required: true,
|
|
93785
|
-
value: settings.streamPreference,
|
|
93786
|
-
showWhen: {
|
|
93787
|
-
field: enabledKey,
|
|
93788
|
-
equals: true
|
|
93789
|
-
},
|
|
93790
|
-
immediate: true
|
|
93791
|
-
},
|
|
93792
|
-
{
|
|
93793
|
-
type: "boolean",
|
|
93794
|
-
key: hksvKey,
|
|
93795
|
-
label: "HomeKit recording (Secure Video)",
|
|
93796
|
-
description: "Offer “Stream and Allow Recording” in iOS Home. Requires iCloud+ and a home hub. Keeps a continuous 8s prebuffer for this camera (~0.7% of one CPU core, H.264 sources only).",
|
|
93797
|
-
style: "switch",
|
|
93798
|
-
value: resolveHksvRecording(settings),
|
|
93799
|
-
showWhen: {
|
|
93800
|
-
field: enabledKey,
|
|
93801
|
-
equals: true
|
|
93802
|
-
},
|
|
93803
|
-
immediate: true
|
|
93804
|
-
}
|
|
96245
|
+
...cameraFields
|
|
93805
96246
|
]
|
|
93806
96247
|
}]
|
|
93807
96248
|
};
|
|
@@ -93867,23 +96308,6 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93867
96308
|
}
|
|
93868
96309
|
return { success: true };
|
|
93869
96310
|
}
|
|
93870
|
-
/**
|
|
93871
|
-
* Camera-only gate — HomeKit export only knows how to mirror cameras
|
|
93872
|
-
* today. Mirrors the snapshot addon's source-side filter so the
|
|
93873
|
-
* device-details page doesn't render an "Export" tab on lights /
|
|
93874
|
-
* switches / sensors.
|
|
93875
|
-
*/
|
|
93876
|
-
async isCameraDevice(deviceId) {
|
|
93877
|
-
const api = this.ctx.api;
|
|
93878
|
-
if (!api.deviceManager) return true;
|
|
93879
|
-
try {
|
|
93880
|
-
const dev = await api.deviceManager.getDevice.query({ deviceId });
|
|
93881
|
-
if (!dev) return true;
|
|
93882
|
-
return dev.type === DeviceType.Camera;
|
|
93883
|
-
} catch {
|
|
93884
|
-
return true;
|
|
93885
|
-
}
|
|
93886
|
-
}
|
|
93887
96311
|
findEntry(deviceId) {
|
|
93888
96312
|
const id = String(deviceId);
|
|
93889
96313
|
return this.config.exposed.find((e) => e.deviceId === id) ?? null;
|