@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
|
@@ -3070,6 +3070,9 @@ function handlePipeResult(left, next, ctx) {
|
|
|
3070
3070
|
fallback: left.fallback
|
|
3071
3071
|
}, ctx);
|
|
3072
3072
|
}
|
|
3073
|
+
var $ZodPreprocess = /*@__PURE__*/ $constructor("$ZodPreprocess", (inst, def) => {
|
|
3074
|
+
$ZodPipe.init(inst, def);
|
|
3075
|
+
});
|
|
3073
3076
|
var $ZodReadonly = /*@__PURE__*/ $constructor("$ZodReadonly", (inst, def) => {
|
|
3074
3077
|
$ZodType.init(inst, def);
|
|
3075
3078
|
defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
|
|
@@ -5255,6 +5258,10 @@ function pipe(in_, out) {
|
|
|
5255
5258
|
out
|
|
5256
5259
|
});
|
|
5257
5260
|
}
|
|
5261
|
+
var ZodPreprocess = /*@__PURE__*/ $constructor("ZodPreprocess", (inst, def) => {
|
|
5262
|
+
ZodPipe.init(inst, def);
|
|
5263
|
+
$ZodPreprocess.init(inst, def);
|
|
5264
|
+
});
|
|
5258
5265
|
var ZodReadonly = /*@__PURE__*/ $constructor("ZodReadonly", (inst, def) => {
|
|
5259
5266
|
$ZodReadonly.init(inst, def);
|
|
5260
5267
|
ZodType.init(inst, def);
|
|
@@ -5313,6 +5320,13 @@ function _instanceof(cls, params = {}) {
|
|
|
5313
5320
|
};
|
|
5314
5321
|
return inst;
|
|
5315
5322
|
}
|
|
5323
|
+
function preprocess(fn, schema) {
|
|
5324
|
+
return new ZodPreprocess({
|
|
5325
|
+
type: "pipe",
|
|
5326
|
+
in: transform(fn),
|
|
5327
|
+
out: schema
|
|
5328
|
+
});
|
|
5329
|
+
}
|
|
5316
5330
|
//#endregion
|
|
5317
5331
|
//#region ../../node_modules/zod/v4/classic/compat.js
|
|
5318
5332
|
/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
|
|
@@ -7589,7 +7603,7 @@ import { errMsg } from '@camstack/types'
|
|
|
7589
7603
|
* Extract a human-readable message from an unknown error value.
|
|
7590
7604
|
* Replaces the ubiquitous `errMsg(err)` pattern.
|
|
7591
7605
|
*/
|
|
7592
|
-
function errMsg$
|
|
7606
|
+
function errMsg$15(err) {
|
|
7593
7607
|
if (err instanceof Error) return err.message;
|
|
7594
7608
|
if (typeof err === "string") return err;
|
|
7595
7609
|
return String(err);
|
|
@@ -15991,7 +16005,87 @@ var MethodAccessSchema = _enum([
|
|
|
15991
16005
|
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
15992
16006
|
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
15993
16007
|
var CapScopeSchema = _enum(["device", "system"]);
|
|
15994
|
-
|
|
16008
|
+
/**
|
|
16009
|
+
* DeviceSelector (scope model v3 — 2026-08-12).
|
|
16010
|
+
*
|
|
16011
|
+
* A `device` grant no longer carries a frozen list of deviceIds. It carries
|
|
16012
|
+
* a SELECTOR the matcher resolves against the live fleet, so the grant can be
|
|
16013
|
+
* DYNAMIC: a `types:['camera']` selector automatically covers a camera added
|
|
16014
|
+
* AFTER the grant was minted — no re-grant, no re-login.
|
|
16015
|
+
*
|
|
16016
|
+
* - `all` — every device in the deployment. The broad viewer/operator
|
|
16017
|
+
* lever without a `category` grant (a `category` grant also covers device
|
|
16018
|
+
* caps that carry no deviceId; `all` is specifically the device set).
|
|
16019
|
+
* - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
|
|
16020
|
+
* grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
|
|
16021
|
+
* is NOT covered until the grant is edited.
|
|
16022
|
+
* - `types` — every device of a `DeviceType` (e.g. every `camera`).
|
|
16023
|
+
* DYNAMIC. A device that changes type, or a new device of the type,
|
|
16024
|
+
* re-resolves on the next request.
|
|
16025
|
+
* - `locations` — every device whose operator-assigned `location` label is
|
|
16026
|
+
* in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
|
|
16027
|
+
* null/unset location matches NO `locations` selector.
|
|
16028
|
+
*/
|
|
16029
|
+
var DeviceSelectorSchema = discriminatedUnion("kind", [
|
|
16030
|
+
object({ kind: literal("all") }),
|
|
16031
|
+
object({
|
|
16032
|
+
kind: literal("ids"),
|
|
16033
|
+
ids: array(number().int()).min(1)
|
|
16034
|
+
}),
|
|
16035
|
+
object({
|
|
16036
|
+
kind: literal("types"),
|
|
16037
|
+
types: array(_enum(DeviceType)).min(1)
|
|
16038
|
+
}),
|
|
16039
|
+
object({
|
|
16040
|
+
kind: literal("locations"),
|
|
16041
|
+
locations: array(string().min(1)).min(1)
|
|
16042
|
+
})
|
|
16043
|
+
]);
|
|
16044
|
+
var DeviceTokenScopeSchema = object({
|
|
16045
|
+
type: literal("device"),
|
|
16046
|
+
/** The device SET this grant covers — resolved against the live fleet. */
|
|
16047
|
+
selector: DeviceSelectorSchema,
|
|
16048
|
+
access: array(MethodAccessSchema).min(1),
|
|
16049
|
+
/**
|
|
16050
|
+
* Whether a grant on a PARENT device transparently covers its accessory
|
|
16051
|
+
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
16052
|
+
* Direction is parent → children ONLY.
|
|
16053
|
+
*
|
|
16054
|
+
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
16055
|
+
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
16056
|
+
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
16057
|
+
* operator must grant on the child, not inherit from the parent). Set it
|
|
16058
|
+
* explicitly to override that default per grant.
|
|
16059
|
+
*/
|
|
16060
|
+
includeLinked: boolean().optional()
|
|
16061
|
+
});
|
|
16062
|
+
/**
|
|
16063
|
+
* v2 → v3 lazy migration. A pre-v3 `device` grant carried
|
|
16064
|
+
* `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
|
|
16065
|
+
* `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
|
|
16066
|
+
* EVERY parse path — stored records AND the JWT-carried scope arrays
|
|
16067
|
+
* normalised at the request boundary ({@link normalizeTokenScopes} in
|
|
16068
|
+
* `device-selector.ts`). Chosen over a one-time DB migration because a
|
|
16069
|
+
* migration cannot reach a JWT already in a client's hands; parse-time
|
|
16070
|
+
* migration covers both without a flag day. No cast — the raw object is read
|
|
16071
|
+
* through `Reflect.get` (its static type is `unknown`).
|
|
16072
|
+
*/
|
|
16073
|
+
function migrateLegacyTokenScope(raw) {
|
|
16074
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
16075
|
+
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
16076
|
+
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
16077
|
+
const targets = Reflect.get(raw, "targets");
|
|
16078
|
+
if (!Array.isArray(targets)) return raw;
|
|
16079
|
+
return {
|
|
16080
|
+
type: "device",
|
|
16081
|
+
selector: {
|
|
16082
|
+
kind: "ids",
|
|
16083
|
+
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
16084
|
+
},
|
|
16085
|
+
access: Reflect.get(raw, "access")
|
|
16086
|
+
};
|
|
16087
|
+
}
|
|
16088
|
+
var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
|
|
15995
16089
|
object({
|
|
15996
16090
|
type: literal("category"),
|
|
15997
16091
|
target: CapScopeSchema,
|
|
@@ -16007,18 +16101,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
|
|
|
16007
16101
|
target: string(),
|
|
16008
16102
|
access: array(MethodAccessSchema).min(1)
|
|
16009
16103
|
}),
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
/**
|
|
16013
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
16014
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
16015
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
16016
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
16017
|
-
*/
|
|
16018
|
-
targets: array(string()).min(1),
|
|
16019
|
-
access: array(MethodAccessSchema).min(1)
|
|
16020
|
-
})
|
|
16021
|
-
]);
|
|
16104
|
+
DeviceTokenScopeSchema
|
|
16105
|
+
]));
|
|
16022
16106
|
object({
|
|
16023
16107
|
id: string(),
|
|
16024
16108
|
username: string(),
|
|
@@ -18034,7 +18118,7 @@ var detectionFpsField = {
|
|
|
18034
18118
|
var occupancyRecheckSecField = {
|
|
18035
18119
|
min: 0,
|
|
18036
18120
|
max: 300,
|
|
18037
|
-
default:
|
|
18121
|
+
default: 300,
|
|
18038
18122
|
step: 5
|
|
18039
18123
|
};
|
|
18040
18124
|
var occupancyRecheckFramesField = {
|
|
@@ -20647,7 +20731,11 @@ object({
|
|
|
20647
20731
|
precision: number().int().min(0).max(10).optional()
|
|
20648
20732
|
});
|
|
20649
20733
|
DeviceType.Sensor;
|
|
20650
|
-
|
|
20734
|
+
/**
|
|
20735
|
+
* Ambient illuminance reading in lux. Drives Home Assistant `sensor`
|
|
20736
|
+
* entries with `device_class: illuminance`.
|
|
20737
|
+
*/
|
|
20738
|
+
var AmbientLightSensorStatusSchema = object({
|
|
20651
20739
|
/** Current illuminance in lux (lx). */
|
|
20652
20740
|
lux: number().min(0),
|
|
20653
20741
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -20920,7 +21008,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), CameraCredentialsSchem
|
|
|
20920
21008
|
kind: "query",
|
|
20921
21009
|
auth: "admin"
|
|
20922
21010
|
});
|
|
20923
|
-
|
|
21011
|
+
/**
|
|
21012
|
+
* Carbon-monoxide alarm sensor. Drives Home Assistant `binary_sensor`
|
|
21013
|
+
* entries with `device_class: carbon_monoxide`. Push-driven.
|
|
21014
|
+
*/
|
|
21015
|
+
var CarbonMonoxideStatusSchema = object({
|
|
20924
21016
|
detected: boolean(),
|
|
20925
21017
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
20926
21018
|
lastChangedAt: number()
|
|
@@ -21208,7 +21300,19 @@ Object.values(DeviceType), method(object({
|
|
|
21208
21300
|
kind: "mutation",
|
|
21209
21301
|
auth: "admin"
|
|
21210
21302
|
}), ConsumablesStatusSchema.extend({ lastFetchedAt: number() });
|
|
21211
|
-
|
|
21303
|
+
/**
|
|
21304
|
+
* Door / window / opening / garage / valve contact sensor. Boolean
|
|
21305
|
+
* "is the entry currently open" with the timestamp of the last
|
|
21306
|
+
* transition. Drives Home Assistant `binary_sensor` entries whose
|
|
21307
|
+
* `device_class` is `door`, `window`, `opening`, `garage`, or
|
|
21308
|
+
* `garage_door` — and any future native integration that needs
|
|
21309
|
+
* the same semantics.
|
|
21310
|
+
*
|
|
21311
|
+
* Push-driven: providers update the slice on transition events from
|
|
21312
|
+
* the upstream source (HA WebSocket `state_changed`, ZWave
|
|
21313
|
+
* `notification` …). Consumers read the slice; no polling.
|
|
21314
|
+
*/
|
|
21315
|
+
var ContactStatusSchema = object({
|
|
21212
21316
|
/** True when the entry is open; false when closed. */
|
|
21213
21317
|
entryOpen: boolean(),
|
|
21214
21318
|
/** Ms epoch of the last open↔closed transition. 0 if never observed. */
|
|
@@ -21807,7 +21911,15 @@ object({
|
|
|
21807
21911
|
deviceId: number(),
|
|
21808
21912
|
status: FeatureProbeStatusSchema
|
|
21809
21913
|
});
|
|
21810
|
-
|
|
21914
|
+
/**
|
|
21915
|
+
* Water leak / moisture sensor. Boolean "is liquid currently
|
|
21916
|
+
* detected" with the timestamp of the last transition. Drives Home
|
|
21917
|
+
* Assistant `binary_sensor` entries with `device_class: moisture`,
|
|
21918
|
+
* and any future native flood sensor.
|
|
21919
|
+
*
|
|
21920
|
+
* Push-driven from the upstream source.
|
|
21921
|
+
*/
|
|
21922
|
+
var FloodStatusSchema = object({
|
|
21811
21923
|
/** True when leak is currently detected. */
|
|
21812
21924
|
flooded: boolean(),
|
|
21813
21925
|
/** Ms epoch of the last flooded↔dry transition. 0 if never observed. */
|
|
@@ -21861,7 +21973,15 @@ DeviceType.Humidifier, method(object({
|
|
|
21861
21973
|
kind: "mutation",
|
|
21862
21974
|
auth: "admin"
|
|
21863
21975
|
});
|
|
21864
|
-
|
|
21976
|
+
/**
|
|
21977
|
+
* Single-metric humidity reading. Drives Home Assistant `sensor`
|
|
21978
|
+
* entries with `device_class: humidity`.
|
|
21979
|
+
*
|
|
21980
|
+
* Unit normalisation: percent. The canonical display unit (`%`) is a
|
|
21981
|
+
* descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
21982
|
+
* `sourceInfo`.
|
|
21983
|
+
*/
|
|
21984
|
+
var HumiditySensorStatusSchema = object({
|
|
21865
21985
|
/** Current relative humidity, 0..100. */
|
|
21866
21986
|
percent: number().min(0).max(100),
|
|
21867
21987
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -22483,7 +22603,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22483
22603
|
* tunnel always emits `https://` regardless. */
|
|
22484
22604
|
scheme: _enum(["http", "https"]).optional()
|
|
22485
22605
|
}), 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" });
|
|
22486
|
-
object({
|
|
22606
|
+
var LockControlStatusSchema = object({
|
|
22487
22607
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22488
22608
|
* failure to reach the target — operator intervention required. */
|
|
22489
22609
|
state: _enum([
|
|
@@ -22821,7 +22941,19 @@ authKey: string().optional() }), object({
|
|
|
22821
22941
|
/** Human-readable error when `ok: false`. */
|
|
22822
22942
|
error: string().optional()
|
|
22823
22943
|
}), { kind: "mutation" });
|
|
22824
|
-
|
|
22944
|
+
/**
|
|
22945
|
+
* Hardware / firmware motion sensor cap — binary detected state plus
|
|
22946
|
+
* a timestamp of the last observation. Distinct from
|
|
22947
|
+
* `motion-detection.cap.ts` which owns the LOCAL ML motion pipeline;
|
|
22948
|
+
* `motion` is the lightweight readout from on-camera motion (Reolink
|
|
22949
|
+
* `GetMdState`, Baichuan push `type: motion`, ONVIF analytics).
|
|
22950
|
+
*
|
|
22951
|
+
* Native-motion providers also fan out to `detection.camera-native`
|
|
22952
|
+
* with `source: 'onboard'` so cross-cutting system services
|
|
22953
|
+
* (alert-center, advanced-notifier) can subscribe once and receive
|
|
22954
|
+
* motion from every camera.
|
|
22955
|
+
*/
|
|
22956
|
+
var MotionStatusSchema = object({
|
|
22825
22957
|
detected: boolean(),
|
|
22826
22958
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
22827
22959
|
lastDetectedAt: number().nullable(),
|
|
@@ -23933,7 +24065,7 @@ var GpsLocationSchema = object({
|
|
|
23933
24065
|
/** Reported accuracy in meters (lower = better). */
|
|
23934
24066
|
accuracyMeters: number().nonnegative()
|
|
23935
24067
|
});
|
|
23936
|
-
object({
|
|
24068
|
+
var PresenceStatusSchema = object({
|
|
23937
24069
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
23938
24070
|
state: string(),
|
|
23939
24071
|
/** Optional textual location label (zone name, city, address). Null
|
|
@@ -24349,7 +24481,7 @@ method(object({
|
|
|
24349
24481
|
toMs: number()
|
|
24350
24482
|
}), RecordingAvailabilitySchema, {
|
|
24351
24483
|
kind: "query",
|
|
24352
|
-
auth: "
|
|
24484
|
+
auth: "protected"
|
|
24353
24485
|
}), method(object({
|
|
24354
24486
|
deviceId: number(),
|
|
24355
24487
|
fromMs: number(),
|
|
@@ -24357,14 +24489,14 @@ method(object({
|
|
|
24357
24489
|
tzOffsetMinutes: number()
|
|
24358
24490
|
}), RecordingDaysSchema, {
|
|
24359
24491
|
kind: "query",
|
|
24360
|
-
auth: "
|
|
24492
|
+
auth: "protected"
|
|
24361
24493
|
}), method(object({
|
|
24362
24494
|
deviceId: number(),
|
|
24363
24495
|
fromMs: number(),
|
|
24364
24496
|
toMs: number()
|
|
24365
24497
|
}), RecordingManifestSchema, {
|
|
24366
24498
|
kind: "query",
|
|
24367
|
-
auth: "
|
|
24499
|
+
auth: "protected"
|
|
24368
24500
|
}), method(object({}), RecordingStorageUsageSchema, {
|
|
24369
24501
|
kind: "query",
|
|
24370
24502
|
auth: "admin"
|
|
@@ -24893,7 +25025,16 @@ DeviceType.Script, method(object({
|
|
|
24893
25025
|
kind: "mutation",
|
|
24894
25026
|
auth: "admin"
|
|
24895
25027
|
});
|
|
24896
|
-
|
|
25028
|
+
/**
|
|
25029
|
+
* Smoke alarm sensor — boolean "is smoke currently detected" with
|
|
25030
|
+
* timestamp of the last transition. Drives Home Assistant
|
|
25031
|
+
* `binary_sensor` entries with `device_class: smoke`.
|
|
25032
|
+
*
|
|
25033
|
+
* Push-driven: a smoke event is critical, so the slice updates
|
|
25034
|
+
* immediately on the upstream signal. Auto-clearing back to false is
|
|
25035
|
+
* provider-controlled (some alarms latch until manually reset).
|
|
25036
|
+
*/
|
|
25037
|
+
var SmokeStatusSchema = object({
|
|
24897
25038
|
detected: boolean(),
|
|
24898
25039
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
24899
25040
|
lastChangedAt: number()
|
|
@@ -25102,7 +25243,23 @@ object({
|
|
|
25102
25243
|
lastChangedAt: number()
|
|
25103
25244
|
});
|
|
25104
25245
|
DeviceType.Sensor;
|
|
25105
|
-
|
|
25246
|
+
/**
|
|
25247
|
+
* Single-metric temperature reading. Drives Home Assistant `sensor`
|
|
25248
|
+
* entries with `device_class: temperature` and any future native
|
|
25249
|
+
* thermometer.
|
|
25250
|
+
*
|
|
25251
|
+
* Unit normalisation: providers convert to Celsius before storing.
|
|
25252
|
+
* The slice value is always Celsius so cross-cap aggregators
|
|
25253
|
+
* (climate-control's `currentTemp`, energy analytics) can compose
|
|
25254
|
+
* without per-source unit fixups. The canonical display unit (`°C`) is
|
|
25255
|
+
* a descriptor constant in the UI (ROLE_DESCRIPTOR), not stored in
|
|
25256
|
+
* `sourceInfo`.
|
|
25257
|
+
*
|
|
25258
|
+
* Status `lastFetchedAt` lets staleness-aware consumers detect a
|
|
25259
|
+
* frozen feed (provider hung) distinct from a "temperature hasn't
|
|
25260
|
+
* changed" steady state.
|
|
25261
|
+
*/
|
|
25262
|
+
var TemperatureSensorStatusSchema = object({
|
|
25106
25263
|
/** Current temperature in Celsius. */
|
|
25107
25264
|
celsius: number(),
|
|
25108
25265
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
@@ -31363,6 +31520,1678 @@ Object.freeze({
|
|
|
31363
31520
|
access: "create"
|
|
31364
31521
|
}
|
|
31365
31522
|
});
|
|
31523
|
+
Object.freeze({
|
|
31524
|
+
"accessories.setChildHidden": [{
|
|
31525
|
+
name: "childDeviceId",
|
|
31526
|
+
form: "single",
|
|
31527
|
+
optional: false
|
|
31528
|
+
}, {
|
|
31529
|
+
name: "deviceId",
|
|
31530
|
+
form: "single",
|
|
31531
|
+
optional: false
|
|
31532
|
+
}],
|
|
31533
|
+
"addonSettings.getDeviceSettings": [{
|
|
31534
|
+
name: "deviceId",
|
|
31535
|
+
form: "single",
|
|
31536
|
+
optional: false
|
|
31537
|
+
}],
|
|
31538
|
+
"addonSettings.updateDeviceSettings": [{
|
|
31539
|
+
name: "deviceId",
|
|
31540
|
+
form: "single",
|
|
31541
|
+
optional: false
|
|
31542
|
+
}],
|
|
31543
|
+
"alarmPanel.arm": [{
|
|
31544
|
+
name: "deviceId",
|
|
31545
|
+
form: "single",
|
|
31546
|
+
optional: false
|
|
31547
|
+
}],
|
|
31548
|
+
"alarmPanel.disarm": [{
|
|
31549
|
+
name: "deviceId",
|
|
31550
|
+
form: "single",
|
|
31551
|
+
optional: false
|
|
31552
|
+
}],
|
|
31553
|
+
"alarmPanel.trigger": [{
|
|
31554
|
+
name: "deviceId",
|
|
31555
|
+
form: "single",
|
|
31556
|
+
optional: false
|
|
31557
|
+
}],
|
|
31558
|
+
"audioAnalysis.resolveDeviceSettings": [{
|
|
31559
|
+
name: "deviceId",
|
|
31560
|
+
form: "single",
|
|
31561
|
+
optional: false
|
|
31562
|
+
}],
|
|
31563
|
+
"audioAnalyzer.classify": [{
|
|
31564
|
+
name: "deviceId",
|
|
31565
|
+
form: "single",
|
|
31566
|
+
optional: true
|
|
31567
|
+
}],
|
|
31568
|
+
"audioMetrics.getCurrentSnapshot": [{
|
|
31569
|
+
name: "deviceId",
|
|
31570
|
+
form: "single",
|
|
31571
|
+
optional: false
|
|
31572
|
+
}],
|
|
31573
|
+
"audioMetrics.getHistory": [{
|
|
31574
|
+
name: "deviceId",
|
|
31575
|
+
form: "single",
|
|
31576
|
+
optional: false
|
|
31577
|
+
}],
|
|
31578
|
+
"automationControl.disable": [{
|
|
31579
|
+
name: "deviceId",
|
|
31580
|
+
form: "single",
|
|
31581
|
+
optional: false
|
|
31582
|
+
}],
|
|
31583
|
+
"automationControl.enable": [{
|
|
31584
|
+
name: "deviceId",
|
|
31585
|
+
form: "single",
|
|
31586
|
+
optional: false
|
|
31587
|
+
}],
|
|
31588
|
+
"automationControl.trigger": [{
|
|
31589
|
+
name: "deviceId",
|
|
31590
|
+
form: "single",
|
|
31591
|
+
optional: false
|
|
31592
|
+
}],
|
|
31593
|
+
"battery.wakeForStream": [{
|
|
31594
|
+
name: "deviceId",
|
|
31595
|
+
form: "single",
|
|
31596
|
+
optional: false
|
|
31597
|
+
}],
|
|
31598
|
+
"brightness.setBrightness": [{
|
|
31599
|
+
name: "deviceId",
|
|
31600
|
+
form: "single",
|
|
31601
|
+
optional: false
|
|
31602
|
+
}],
|
|
31603
|
+
"button.press": [{
|
|
31604
|
+
name: "deviceId",
|
|
31605
|
+
form: "single",
|
|
31606
|
+
optional: false
|
|
31607
|
+
}],
|
|
31608
|
+
"cameraCredentials.getCredentials": [{
|
|
31609
|
+
name: "deviceId",
|
|
31610
|
+
form: "single",
|
|
31611
|
+
optional: false
|
|
31612
|
+
}],
|
|
31613
|
+
"cameraStreams.getBrokerStreams": [{
|
|
31614
|
+
name: "deviceId",
|
|
31615
|
+
form: "single",
|
|
31616
|
+
optional: false
|
|
31617
|
+
}],
|
|
31618
|
+
"cameraStreams.getCameraStreams": [{
|
|
31619
|
+
name: "deviceId",
|
|
31620
|
+
form: "single",
|
|
31621
|
+
optional: false
|
|
31622
|
+
}],
|
|
31623
|
+
"cameraStreams.getProfileRtspEntries": [{
|
|
31624
|
+
name: "deviceId",
|
|
31625
|
+
form: "single",
|
|
31626
|
+
optional: false
|
|
31627
|
+
}],
|
|
31628
|
+
"cameraStreams.getRtspEntries": [{
|
|
31629
|
+
name: "deviceId",
|
|
31630
|
+
form: "single",
|
|
31631
|
+
optional: false
|
|
31632
|
+
}],
|
|
31633
|
+
"cameraStreams.pickStream": [{
|
|
31634
|
+
name: "deviceId",
|
|
31635
|
+
form: "single",
|
|
31636
|
+
optional: false
|
|
31637
|
+
}],
|
|
31638
|
+
"climateControl.setFanMode": [{
|
|
31639
|
+
name: "deviceId",
|
|
31640
|
+
form: "single",
|
|
31641
|
+
optional: false
|
|
31642
|
+
}],
|
|
31643
|
+
"climateControl.setMode": [{
|
|
31644
|
+
name: "deviceId",
|
|
31645
|
+
form: "single",
|
|
31646
|
+
optional: false
|
|
31647
|
+
}],
|
|
31648
|
+
"climateControl.setPreset": [{
|
|
31649
|
+
name: "deviceId",
|
|
31650
|
+
form: "single",
|
|
31651
|
+
optional: false
|
|
31652
|
+
}],
|
|
31653
|
+
"climateControl.setSwingHorizontal": [{
|
|
31654
|
+
name: "deviceId",
|
|
31655
|
+
form: "single",
|
|
31656
|
+
optional: false
|
|
31657
|
+
}],
|
|
31658
|
+
"climateControl.setSwingVertical": [{
|
|
31659
|
+
name: "deviceId",
|
|
31660
|
+
form: "single",
|
|
31661
|
+
optional: false
|
|
31662
|
+
}],
|
|
31663
|
+
"climateControl.setTarget": [{
|
|
31664
|
+
name: "deviceId",
|
|
31665
|
+
form: "single",
|
|
31666
|
+
optional: false
|
|
31667
|
+
}],
|
|
31668
|
+
"climateControl.setTargetHumidity": [{
|
|
31669
|
+
name: "deviceId",
|
|
31670
|
+
form: "single",
|
|
31671
|
+
optional: false
|
|
31672
|
+
}],
|
|
31673
|
+
"climateControl.setTargetRange": [{
|
|
31674
|
+
name: "deviceId",
|
|
31675
|
+
form: "single",
|
|
31676
|
+
optional: false
|
|
31677
|
+
}],
|
|
31678
|
+
"color.setColor": [{
|
|
31679
|
+
name: "deviceId",
|
|
31680
|
+
form: "single",
|
|
31681
|
+
optional: false
|
|
31682
|
+
}],
|
|
31683
|
+
"consumables.reset": [{
|
|
31684
|
+
name: "deviceId",
|
|
31685
|
+
form: "single",
|
|
31686
|
+
optional: false
|
|
31687
|
+
}],
|
|
31688
|
+
"control.setValue": [{
|
|
31689
|
+
name: "deviceId",
|
|
31690
|
+
form: "single",
|
|
31691
|
+
optional: false
|
|
31692
|
+
}],
|
|
31693
|
+
"cover.close": [{
|
|
31694
|
+
name: "deviceId",
|
|
31695
|
+
form: "single",
|
|
31696
|
+
optional: false
|
|
31697
|
+
}],
|
|
31698
|
+
"cover.open": [{
|
|
31699
|
+
name: "deviceId",
|
|
31700
|
+
form: "single",
|
|
31701
|
+
optional: false
|
|
31702
|
+
}],
|
|
31703
|
+
"cover.setPosition": [{
|
|
31704
|
+
name: "deviceId",
|
|
31705
|
+
form: "single",
|
|
31706
|
+
optional: false
|
|
31707
|
+
}],
|
|
31708
|
+
"cover.setTiltPosition": [{
|
|
31709
|
+
name: "deviceId",
|
|
31710
|
+
form: "single",
|
|
31711
|
+
optional: false
|
|
31712
|
+
}],
|
|
31713
|
+
"cover.stop": [{
|
|
31714
|
+
name: "deviceId",
|
|
31715
|
+
form: "single",
|
|
31716
|
+
optional: false
|
|
31717
|
+
}],
|
|
31718
|
+
"dayNight.getOptions": [{
|
|
31719
|
+
name: "deviceId",
|
|
31720
|
+
form: "single",
|
|
31721
|
+
optional: false
|
|
31722
|
+
}],
|
|
31723
|
+
"dayNight.setSettings": [{
|
|
31724
|
+
name: "deviceId",
|
|
31725
|
+
form: "single",
|
|
31726
|
+
optional: false
|
|
31727
|
+
}],
|
|
31728
|
+
"decoder.createSession": [{
|
|
31729
|
+
name: "deviceId",
|
|
31730
|
+
form: "single",
|
|
31731
|
+
optional: true
|
|
31732
|
+
}],
|
|
31733
|
+
"deviceAdoption.release": [{
|
|
31734
|
+
name: "camDeviceId",
|
|
31735
|
+
form: "single",
|
|
31736
|
+
optional: false
|
|
31737
|
+
}],
|
|
31738
|
+
"deviceAdoption.resync": [{
|
|
31739
|
+
name: "camDeviceId",
|
|
31740
|
+
form: "single",
|
|
31741
|
+
optional: false
|
|
31742
|
+
}],
|
|
31743
|
+
"deviceDiscovery.adoptDevice": [{
|
|
31744
|
+
name: "deviceId",
|
|
31745
|
+
form: "single",
|
|
31746
|
+
optional: false
|
|
31747
|
+
}],
|
|
31748
|
+
"deviceDiscovery.listDiscovered": [{
|
|
31749
|
+
name: "deviceId",
|
|
31750
|
+
form: "single",
|
|
31751
|
+
optional: false
|
|
31752
|
+
}],
|
|
31753
|
+
"deviceDiscovery.refreshDiscovery": [{
|
|
31754
|
+
name: "deviceId",
|
|
31755
|
+
form: "single",
|
|
31756
|
+
optional: false
|
|
31757
|
+
}],
|
|
31758
|
+
"deviceDiscovery.releaseDevice": [{
|
|
31759
|
+
name: "childDeviceId",
|
|
31760
|
+
form: "single",
|
|
31761
|
+
optional: false
|
|
31762
|
+
}, {
|
|
31763
|
+
name: "deviceId",
|
|
31764
|
+
form: "single",
|
|
31765
|
+
optional: false
|
|
31766
|
+
}],
|
|
31767
|
+
"deviceManager.adoptionRelease": [{
|
|
31768
|
+
name: "camDeviceId",
|
|
31769
|
+
form: "single",
|
|
31770
|
+
optional: false
|
|
31771
|
+
}],
|
|
31772
|
+
"deviceManager.adoptionResync": [{
|
|
31773
|
+
name: "camDeviceId",
|
|
31774
|
+
form: "single",
|
|
31775
|
+
optional: false
|
|
31776
|
+
}],
|
|
31777
|
+
"deviceManager.applyInitialMeta": [{
|
|
31778
|
+
name: "deviceId",
|
|
31779
|
+
form: "single",
|
|
31780
|
+
optional: false
|
|
31781
|
+
}, {
|
|
31782
|
+
name: "linkDeviceId",
|
|
31783
|
+
form: "single",
|
|
31784
|
+
optional: true
|
|
31785
|
+
}],
|
|
31786
|
+
"deviceManager.disable": [{
|
|
31787
|
+
name: "deviceId",
|
|
31788
|
+
form: "single",
|
|
31789
|
+
optional: false
|
|
31790
|
+
}],
|
|
31791
|
+
"deviceManager.enable": [{
|
|
31792
|
+
name: "deviceId",
|
|
31793
|
+
form: "single",
|
|
31794
|
+
optional: false
|
|
31795
|
+
}],
|
|
31796
|
+
"deviceManager.getBindings": [{
|
|
31797
|
+
name: "deviceId",
|
|
31798
|
+
form: "single",
|
|
31799
|
+
optional: false
|
|
31800
|
+
}],
|
|
31801
|
+
"deviceManager.getChildren": [{
|
|
31802
|
+
name: "parentDeviceId",
|
|
31803
|
+
form: "single",
|
|
31804
|
+
optional: false
|
|
31805
|
+
}],
|
|
31806
|
+
"deviceManager.getConfigSchema": [{
|
|
31807
|
+
name: "deviceId",
|
|
31808
|
+
form: "single",
|
|
31809
|
+
optional: false
|
|
31810
|
+
}],
|
|
31811
|
+
"deviceManager.getDevice": [{
|
|
31812
|
+
name: "deviceId",
|
|
31813
|
+
form: "single",
|
|
31814
|
+
optional: false
|
|
31815
|
+
}],
|
|
31816
|
+
"deviceManager.getDeviceAggregate": [{
|
|
31817
|
+
name: "deviceId",
|
|
31818
|
+
form: "single",
|
|
31819
|
+
optional: false
|
|
31820
|
+
}],
|
|
31821
|
+
"deviceManager.getDeviceLiveInfoAggregate": [{
|
|
31822
|
+
name: "deviceId",
|
|
31823
|
+
form: "single",
|
|
31824
|
+
optional: false
|
|
31825
|
+
}],
|
|
31826
|
+
"deviceManager.getDeviceSettingsAggregate": [{
|
|
31827
|
+
name: "deviceId",
|
|
31828
|
+
form: "single",
|
|
31829
|
+
optional: false
|
|
31830
|
+
}],
|
|
31831
|
+
"deviceManager.getDeviceStatusAggregate": [{
|
|
31832
|
+
name: "deviceId",
|
|
31833
|
+
form: "single",
|
|
31834
|
+
optional: false
|
|
31835
|
+
}],
|
|
31836
|
+
"deviceManager.getDeviceStatusAggregateBatch": [{
|
|
31837
|
+
name: "deviceIds",
|
|
31838
|
+
form: "array",
|
|
31839
|
+
optional: false
|
|
31840
|
+
}],
|
|
31841
|
+
"deviceManager.getLinkedDevices": [{
|
|
31842
|
+
name: "deviceId",
|
|
31843
|
+
form: "single",
|
|
31844
|
+
optional: false
|
|
31845
|
+
}],
|
|
31846
|
+
"deviceManager.getSettingsSchema": [{
|
|
31847
|
+
name: "deviceId",
|
|
31848
|
+
form: "single",
|
|
31849
|
+
optional: false
|
|
31850
|
+
}],
|
|
31851
|
+
"deviceManager.getStreamProfileMap": [{
|
|
31852
|
+
name: "deviceId",
|
|
31853
|
+
form: "single",
|
|
31854
|
+
optional: false
|
|
31855
|
+
}],
|
|
31856
|
+
"deviceManager.getStreamSources": [{
|
|
31857
|
+
name: "deviceId",
|
|
31858
|
+
form: "single",
|
|
31859
|
+
optional: false
|
|
31860
|
+
}],
|
|
31861
|
+
"deviceManager.getWireableFields": [{
|
|
31862
|
+
name: "deviceId",
|
|
31863
|
+
form: "single",
|
|
31864
|
+
optional: false
|
|
31865
|
+
}],
|
|
31866
|
+
"deviceManager.loadConfig": [{
|
|
31867
|
+
name: "deviceId",
|
|
31868
|
+
form: "single",
|
|
31869
|
+
optional: false
|
|
31870
|
+
}],
|
|
31871
|
+
"deviceManager.loadMeta": [{
|
|
31872
|
+
name: "deviceId",
|
|
31873
|
+
form: "single",
|
|
31874
|
+
optional: false
|
|
31875
|
+
}],
|
|
31876
|
+
"deviceManager.loadRuntimeState": [{
|
|
31877
|
+
name: "deviceId",
|
|
31878
|
+
form: "single",
|
|
31879
|
+
optional: false
|
|
31880
|
+
}],
|
|
31881
|
+
"deviceManager.persistConfig": [{
|
|
31882
|
+
name: "deviceId",
|
|
31883
|
+
form: "single",
|
|
31884
|
+
optional: false
|
|
31885
|
+
}],
|
|
31886
|
+
"deviceManager.probeStreams": [{
|
|
31887
|
+
name: "deviceId",
|
|
31888
|
+
form: "single",
|
|
31889
|
+
optional: false
|
|
31890
|
+
}],
|
|
31891
|
+
"deviceManager.registerDevice": [{
|
|
31892
|
+
name: "parentDeviceId",
|
|
31893
|
+
form: "single",
|
|
31894
|
+
optional: true
|
|
31895
|
+
}],
|
|
31896
|
+
"deviceManager.remove": [{
|
|
31897
|
+
name: "deviceId",
|
|
31898
|
+
form: "single",
|
|
31899
|
+
optional: false
|
|
31900
|
+
}],
|
|
31901
|
+
"deviceManager.removeDevice": [{
|
|
31902
|
+
name: "deviceId",
|
|
31903
|
+
form: "single",
|
|
31904
|
+
optional: false
|
|
31905
|
+
}],
|
|
31906
|
+
"deviceManager.runDeviceAction": [{
|
|
31907
|
+
name: "deviceId",
|
|
31908
|
+
form: "single",
|
|
31909
|
+
optional: false
|
|
31910
|
+
}],
|
|
31911
|
+
"deviceManager.setChildLayout": [{
|
|
31912
|
+
name: "deviceId",
|
|
31913
|
+
form: "single",
|
|
31914
|
+
optional: false
|
|
31915
|
+
}],
|
|
31916
|
+
"deviceManager.setDisabled": [{
|
|
31917
|
+
name: "deviceId",
|
|
31918
|
+
form: "single",
|
|
31919
|
+
optional: false
|
|
31920
|
+
}],
|
|
31921
|
+
"deviceManager.setDisplay": [{
|
|
31922
|
+
name: "deviceId",
|
|
31923
|
+
form: "single",
|
|
31924
|
+
optional: false
|
|
31925
|
+
}],
|
|
31926
|
+
"deviceManager.setIntegrationId": [{
|
|
31927
|
+
name: "deviceId",
|
|
31928
|
+
form: "single",
|
|
31929
|
+
optional: false
|
|
31930
|
+
}],
|
|
31931
|
+
"deviceManager.setLinkDeviceId": [{
|
|
31932
|
+
name: "deviceId",
|
|
31933
|
+
form: "single",
|
|
31934
|
+
optional: false
|
|
31935
|
+
}, {
|
|
31936
|
+
name: "linkDeviceId",
|
|
31937
|
+
form: "single",
|
|
31938
|
+
optional: true
|
|
31939
|
+
}],
|
|
31940
|
+
"deviceManager.setLocation": [{
|
|
31941
|
+
name: "deviceId",
|
|
31942
|
+
form: "single",
|
|
31943
|
+
optional: false
|
|
31944
|
+
}],
|
|
31945
|
+
"deviceManager.setMetadata": [{
|
|
31946
|
+
name: "deviceId",
|
|
31947
|
+
form: "single",
|
|
31948
|
+
optional: false
|
|
31949
|
+
}],
|
|
31950
|
+
"deviceManager.setName": [{
|
|
31951
|
+
name: "deviceId",
|
|
31952
|
+
form: "single",
|
|
31953
|
+
optional: false
|
|
31954
|
+
}],
|
|
31955
|
+
"deviceManager.setPrimaryChildEntityId": [{
|
|
31956
|
+
name: "deviceId",
|
|
31957
|
+
form: "single",
|
|
31958
|
+
optional: false
|
|
31959
|
+
}],
|
|
31960
|
+
"deviceManager.setRole": [{
|
|
31961
|
+
name: "deviceId",
|
|
31962
|
+
form: "single",
|
|
31963
|
+
optional: false
|
|
31964
|
+
}],
|
|
31965
|
+
"deviceManager.setStreamProfileMap": [{
|
|
31966
|
+
name: "deviceId",
|
|
31967
|
+
form: "single",
|
|
31968
|
+
optional: false
|
|
31969
|
+
}],
|
|
31970
|
+
"deviceManager.setType": [{
|
|
31971
|
+
name: "deviceId",
|
|
31972
|
+
form: "single",
|
|
31973
|
+
optional: false
|
|
31974
|
+
}],
|
|
31975
|
+
"deviceManager.setWrapperActive": [{
|
|
31976
|
+
name: "deviceId",
|
|
31977
|
+
form: "single",
|
|
31978
|
+
optional: false
|
|
31979
|
+
}],
|
|
31980
|
+
"deviceManager.testField": [{
|
|
31981
|
+
name: "deviceId",
|
|
31982
|
+
form: "single",
|
|
31983
|
+
optional: false
|
|
31984
|
+
}],
|
|
31985
|
+
"deviceManager.updateConfig": [{
|
|
31986
|
+
name: "deviceId",
|
|
31987
|
+
form: "single",
|
|
31988
|
+
optional: false
|
|
31989
|
+
}],
|
|
31990
|
+
"deviceManager.updateDeviceField": [{
|
|
31991
|
+
name: "deviceId",
|
|
31992
|
+
form: "single",
|
|
31993
|
+
optional: false
|
|
31994
|
+
}],
|
|
31995
|
+
"deviceManager.updateDeviceFieldsBatch": [{
|
|
31996
|
+
name: "deviceId",
|
|
31997
|
+
form: "single",
|
|
31998
|
+
optional: false
|
|
31999
|
+
}],
|
|
32000
|
+
"deviceOps.getConfigEntries": [{
|
|
32001
|
+
name: "deviceId",
|
|
32002
|
+
form: "single",
|
|
32003
|
+
optional: false
|
|
32004
|
+
}],
|
|
32005
|
+
"deviceOps.getRawState": [{
|
|
32006
|
+
name: "deviceId",
|
|
32007
|
+
form: "single",
|
|
32008
|
+
optional: false
|
|
32009
|
+
}],
|
|
32010
|
+
"deviceOps.getSettingsSchema": [{
|
|
32011
|
+
name: "deviceId",
|
|
32012
|
+
form: "single",
|
|
32013
|
+
optional: false
|
|
32014
|
+
}],
|
|
32015
|
+
"deviceOps.getStreamSources": [{
|
|
32016
|
+
name: "deviceId",
|
|
32017
|
+
form: "single",
|
|
32018
|
+
optional: false
|
|
32019
|
+
}],
|
|
32020
|
+
"deviceOps.removeDevice": [{
|
|
32021
|
+
name: "deviceId",
|
|
32022
|
+
form: "single",
|
|
32023
|
+
optional: false
|
|
32024
|
+
}],
|
|
32025
|
+
"deviceOps.runAction": [{
|
|
32026
|
+
name: "deviceId",
|
|
32027
|
+
form: "single",
|
|
32028
|
+
optional: false
|
|
32029
|
+
}],
|
|
32030
|
+
"deviceOps.setConfig": [{
|
|
32031
|
+
name: "deviceId",
|
|
32032
|
+
form: "single",
|
|
32033
|
+
optional: false
|
|
32034
|
+
}],
|
|
32035
|
+
"deviceState.getCapSlice": [{
|
|
32036
|
+
name: "deviceId",
|
|
32037
|
+
form: "single",
|
|
32038
|
+
optional: false
|
|
32039
|
+
}],
|
|
32040
|
+
"deviceState.getSnapshot": [{
|
|
32041
|
+
name: "deviceId",
|
|
32042
|
+
form: "single",
|
|
32043
|
+
optional: false
|
|
32044
|
+
}],
|
|
32045
|
+
"deviceState.setCapSlice": [{
|
|
32046
|
+
name: "deviceId",
|
|
32047
|
+
form: "single",
|
|
32048
|
+
optional: false
|
|
32049
|
+
}],
|
|
32050
|
+
"events.getEventClipUrl": [{
|
|
32051
|
+
name: "deviceId",
|
|
32052
|
+
form: "single",
|
|
32053
|
+
optional: false
|
|
32054
|
+
}],
|
|
32055
|
+
"events.getEvents": [{
|
|
32056
|
+
name: "deviceId",
|
|
32057
|
+
form: "single",
|
|
32058
|
+
optional: false
|
|
32059
|
+
}],
|
|
32060
|
+
"events.getEventThumbnail": [{
|
|
32061
|
+
name: "deviceId",
|
|
32062
|
+
form: "single",
|
|
32063
|
+
optional: false
|
|
32064
|
+
}],
|
|
32065
|
+
"faceGallery.getFaceByTrack": [{
|
|
32066
|
+
name: "deviceId",
|
|
32067
|
+
form: "single",
|
|
32068
|
+
optional: false
|
|
32069
|
+
}],
|
|
32070
|
+
"faceGallery.listRecentFaces": [{
|
|
32071
|
+
name: "deviceId",
|
|
32072
|
+
form: "single",
|
|
32073
|
+
optional: true
|
|
32074
|
+
}],
|
|
32075
|
+
"fanControl.setDirection": [{
|
|
32076
|
+
name: "deviceId",
|
|
32077
|
+
form: "single",
|
|
32078
|
+
optional: false
|
|
32079
|
+
}],
|
|
32080
|
+
"fanControl.setOscillating": [{
|
|
32081
|
+
name: "deviceId",
|
|
32082
|
+
form: "single",
|
|
32083
|
+
optional: false
|
|
32084
|
+
}],
|
|
32085
|
+
"fanControl.setPercentage": [{
|
|
32086
|
+
name: "deviceId",
|
|
32087
|
+
form: "single",
|
|
32088
|
+
optional: false
|
|
32089
|
+
}],
|
|
32090
|
+
"fanControl.setPreset": [{
|
|
32091
|
+
name: "deviceId",
|
|
32092
|
+
form: "single",
|
|
32093
|
+
optional: false
|
|
32094
|
+
}],
|
|
32095
|
+
"humidifier.setMode": [{
|
|
32096
|
+
name: "deviceId",
|
|
32097
|
+
form: "single",
|
|
32098
|
+
optional: false
|
|
32099
|
+
}],
|
|
32100
|
+
"humidifier.setOn": [{
|
|
32101
|
+
name: "deviceId",
|
|
32102
|
+
form: "single",
|
|
32103
|
+
optional: false
|
|
32104
|
+
}],
|
|
32105
|
+
"humidifier.setTargetHumidity": [{
|
|
32106
|
+
name: "deviceId",
|
|
32107
|
+
form: "single",
|
|
32108
|
+
optional: false
|
|
32109
|
+
}],
|
|
32110
|
+
"imageSettings.getOptions": [{
|
|
32111
|
+
name: "deviceId",
|
|
32112
|
+
form: "single",
|
|
32113
|
+
optional: false
|
|
32114
|
+
}],
|
|
32115
|
+
"imageSettings.setSettings": [{
|
|
32116
|
+
name: "deviceId",
|
|
32117
|
+
form: "single",
|
|
32118
|
+
optional: false
|
|
32119
|
+
}],
|
|
32120
|
+
"intercom.endTalkSession": [{
|
|
32121
|
+
name: "deviceId",
|
|
32122
|
+
form: "single",
|
|
32123
|
+
optional: false
|
|
32124
|
+
}],
|
|
32125
|
+
"intercom.handleAnswer": [{
|
|
32126
|
+
name: "deviceId",
|
|
32127
|
+
form: "single",
|
|
32128
|
+
optional: false
|
|
32129
|
+
}],
|
|
32130
|
+
"intercom.pushTalkAudio": [{
|
|
32131
|
+
name: "deviceId",
|
|
32132
|
+
form: "single",
|
|
32133
|
+
optional: false
|
|
32134
|
+
}],
|
|
32135
|
+
"intercom.startSession": [{
|
|
32136
|
+
name: "deviceId",
|
|
32137
|
+
form: "single",
|
|
32138
|
+
optional: false
|
|
32139
|
+
}],
|
|
32140
|
+
"intercom.startTalkSession": [{
|
|
32141
|
+
name: "deviceId",
|
|
32142
|
+
form: "single",
|
|
32143
|
+
optional: false
|
|
32144
|
+
}],
|
|
32145
|
+
"intercom.stopSession": [{
|
|
32146
|
+
name: "deviceId",
|
|
32147
|
+
form: "single",
|
|
32148
|
+
optional: false
|
|
32149
|
+
}],
|
|
32150
|
+
"lawnMowerControl.dock": [{
|
|
32151
|
+
name: "deviceId",
|
|
32152
|
+
form: "single",
|
|
32153
|
+
optional: false
|
|
32154
|
+
}],
|
|
32155
|
+
"lawnMowerControl.pause": [{
|
|
32156
|
+
name: "deviceId",
|
|
32157
|
+
form: "single",
|
|
32158
|
+
optional: false
|
|
32159
|
+
}],
|
|
32160
|
+
"lawnMowerControl.startMowing": [{
|
|
32161
|
+
name: "deviceId",
|
|
32162
|
+
form: "single",
|
|
32163
|
+
optional: false
|
|
32164
|
+
}],
|
|
32165
|
+
"lockControl.lock": [{
|
|
32166
|
+
name: "deviceId",
|
|
32167
|
+
form: "single",
|
|
32168
|
+
optional: false
|
|
32169
|
+
}],
|
|
32170
|
+
"lockControl.open": [{
|
|
32171
|
+
name: "deviceId",
|
|
32172
|
+
form: "single",
|
|
32173
|
+
optional: false
|
|
32174
|
+
}],
|
|
32175
|
+
"lockControl.unlock": [{
|
|
32176
|
+
name: "deviceId",
|
|
32177
|
+
form: "single",
|
|
32178
|
+
optional: false
|
|
32179
|
+
}],
|
|
32180
|
+
"mediaPlayer.next": [{
|
|
32181
|
+
name: "deviceId",
|
|
32182
|
+
form: "single",
|
|
32183
|
+
optional: false
|
|
32184
|
+
}],
|
|
32185
|
+
"mediaPlayer.pause": [{
|
|
32186
|
+
name: "deviceId",
|
|
32187
|
+
form: "single",
|
|
32188
|
+
optional: false
|
|
32189
|
+
}],
|
|
32190
|
+
"mediaPlayer.play": [{
|
|
32191
|
+
name: "deviceId",
|
|
32192
|
+
form: "single",
|
|
32193
|
+
optional: false
|
|
32194
|
+
}],
|
|
32195
|
+
"mediaPlayer.playMedia": [{
|
|
32196
|
+
name: "deviceId",
|
|
32197
|
+
form: "single",
|
|
32198
|
+
optional: false
|
|
32199
|
+
}],
|
|
32200
|
+
"mediaPlayer.previous": [{
|
|
32201
|
+
name: "deviceId",
|
|
32202
|
+
form: "single",
|
|
32203
|
+
optional: false
|
|
32204
|
+
}],
|
|
32205
|
+
"mediaPlayer.seek": [{
|
|
32206
|
+
name: "deviceId",
|
|
32207
|
+
form: "single",
|
|
32208
|
+
optional: false
|
|
32209
|
+
}],
|
|
32210
|
+
"mediaPlayer.selectSource": [{
|
|
32211
|
+
name: "deviceId",
|
|
32212
|
+
form: "single",
|
|
32213
|
+
optional: false
|
|
32214
|
+
}],
|
|
32215
|
+
"mediaPlayer.setMute": [{
|
|
32216
|
+
name: "deviceId",
|
|
32217
|
+
form: "single",
|
|
32218
|
+
optional: false
|
|
32219
|
+
}],
|
|
32220
|
+
"mediaPlayer.setRepeat": [{
|
|
32221
|
+
name: "deviceId",
|
|
32222
|
+
form: "single",
|
|
32223
|
+
optional: false
|
|
32224
|
+
}],
|
|
32225
|
+
"mediaPlayer.setShuffle": [{
|
|
32226
|
+
name: "deviceId",
|
|
32227
|
+
form: "single",
|
|
32228
|
+
optional: false
|
|
32229
|
+
}],
|
|
32230
|
+
"mediaPlayer.setVolume": [{
|
|
32231
|
+
name: "deviceId",
|
|
32232
|
+
form: "single",
|
|
32233
|
+
optional: false
|
|
32234
|
+
}],
|
|
32235
|
+
"mediaPlayer.stop": [{
|
|
32236
|
+
name: "deviceId",
|
|
32237
|
+
form: "single",
|
|
32238
|
+
optional: false
|
|
32239
|
+
}],
|
|
32240
|
+
"motion.isDetected": [{
|
|
32241
|
+
name: "deviceId",
|
|
32242
|
+
form: "single",
|
|
32243
|
+
optional: false
|
|
32244
|
+
}],
|
|
32245
|
+
"motionDetection.analyze": [{
|
|
32246
|
+
name: "deviceId",
|
|
32247
|
+
form: "single",
|
|
32248
|
+
optional: false
|
|
32249
|
+
}],
|
|
32250
|
+
"motionDetection.removeCamera": [{
|
|
32251
|
+
name: "deviceId",
|
|
32252
|
+
form: "single",
|
|
32253
|
+
optional: false
|
|
32254
|
+
}],
|
|
32255
|
+
"motionTrigger.setMotionTrigger": [{
|
|
32256
|
+
name: "deviceId",
|
|
32257
|
+
form: "single",
|
|
32258
|
+
optional: false
|
|
32259
|
+
}],
|
|
32260
|
+
"motionZones.getOptions": [{
|
|
32261
|
+
name: "deviceId",
|
|
32262
|
+
form: "single",
|
|
32263
|
+
optional: false
|
|
32264
|
+
}],
|
|
32265
|
+
"motionZones.setZone": [{
|
|
32266
|
+
name: "deviceId",
|
|
32267
|
+
form: "single",
|
|
32268
|
+
optional: false
|
|
32269
|
+
}],
|
|
32270
|
+
"nativeObjectDetection.setEnabled": [{
|
|
32271
|
+
name: "deviceId",
|
|
32272
|
+
form: "single",
|
|
32273
|
+
optional: false
|
|
32274
|
+
}],
|
|
32275
|
+
"networkQuality.getDeviceStats": [{
|
|
32276
|
+
name: "deviceId",
|
|
32277
|
+
form: "single",
|
|
32278
|
+
optional: false
|
|
32279
|
+
}],
|
|
32280
|
+
"networkQuality.reportClientStats": [{
|
|
32281
|
+
name: "deviceId",
|
|
32282
|
+
form: "single",
|
|
32283
|
+
optional: false
|
|
32284
|
+
}],
|
|
32285
|
+
"notificationRules.setDeviceMuted": [{
|
|
32286
|
+
name: "deviceId",
|
|
32287
|
+
form: "single",
|
|
32288
|
+
optional: false
|
|
32289
|
+
}],
|
|
32290
|
+
"notifier.cancel": [{
|
|
32291
|
+
name: "deviceId",
|
|
32292
|
+
form: "single",
|
|
32293
|
+
optional: false
|
|
32294
|
+
}],
|
|
32295
|
+
"notifier.send": [{
|
|
32296
|
+
name: "deviceId",
|
|
32297
|
+
form: "single",
|
|
32298
|
+
optional: false
|
|
32299
|
+
}],
|
|
32300
|
+
"osd.setOverlay": [{
|
|
32301
|
+
name: "deviceId",
|
|
32302
|
+
form: "single",
|
|
32303
|
+
optional: false
|
|
32304
|
+
}],
|
|
32305
|
+
"osdManager.clearSlotBinding": [{
|
|
32306
|
+
name: "deviceId",
|
|
32307
|
+
form: "single",
|
|
32308
|
+
optional: false
|
|
32309
|
+
}],
|
|
32310
|
+
"osdManager.copyDeviceConfiguration": [{
|
|
32311
|
+
name: "sourceDeviceId",
|
|
32312
|
+
form: "single",
|
|
32313
|
+
optional: false
|
|
32314
|
+
}, {
|
|
32315
|
+
name: "targetDeviceId",
|
|
32316
|
+
form: "single",
|
|
32317
|
+
optional: false
|
|
32318
|
+
}],
|
|
32319
|
+
"osdManager.getDeviceOsd": [{
|
|
32320
|
+
name: "deviceId",
|
|
32321
|
+
form: "single",
|
|
32322
|
+
optional: false
|
|
32323
|
+
}],
|
|
32324
|
+
"osdManager.getSourceCatalog": [{
|
|
32325
|
+
name: "deviceId",
|
|
32326
|
+
form: "single",
|
|
32327
|
+
optional: false
|
|
32328
|
+
}],
|
|
32329
|
+
"osdManager.previewSlot": [{
|
|
32330
|
+
name: "deviceId",
|
|
32331
|
+
form: "single",
|
|
32332
|
+
optional: false
|
|
32333
|
+
}],
|
|
32334
|
+
"osdManager.renderDevice": [{
|
|
32335
|
+
name: "deviceId",
|
|
32336
|
+
form: "single",
|
|
32337
|
+
optional: false
|
|
32338
|
+
}],
|
|
32339
|
+
"osdManager.setSlotBinding": [{
|
|
32340
|
+
name: "deviceId",
|
|
32341
|
+
form: "single",
|
|
32342
|
+
optional: false
|
|
32343
|
+
}],
|
|
32344
|
+
"petFeeder.callPet": [{
|
|
32345
|
+
name: "deviceId",
|
|
32346
|
+
form: "single",
|
|
32347
|
+
optional: false
|
|
32348
|
+
}],
|
|
32349
|
+
"petFeeder.cancelFeed": [{
|
|
32350
|
+
name: "deviceId",
|
|
32351
|
+
form: "single",
|
|
32352
|
+
optional: false
|
|
32353
|
+
}],
|
|
32354
|
+
"petFeeder.feed": [{
|
|
32355
|
+
name: "deviceId",
|
|
32356
|
+
form: "single",
|
|
32357
|
+
optional: false
|
|
32358
|
+
}],
|
|
32359
|
+
"petFeeder.markFoodReplenished": [{
|
|
32360
|
+
name: "deviceId",
|
|
32361
|
+
form: "single",
|
|
32362
|
+
optional: false
|
|
32363
|
+
}],
|
|
32364
|
+
"petFeeder.playSound": [{
|
|
32365
|
+
name: "deviceId",
|
|
32366
|
+
form: "single",
|
|
32367
|
+
optional: false
|
|
32368
|
+
}],
|
|
32369
|
+
"petFeeder.resetDesiccant": [{
|
|
32370
|
+
name: "deviceId",
|
|
32371
|
+
form: "single",
|
|
32372
|
+
optional: false
|
|
32373
|
+
}],
|
|
32374
|
+
"petFeeder.setChildLock": [{
|
|
32375
|
+
name: "deviceId",
|
|
32376
|
+
form: "single",
|
|
32377
|
+
optional: false
|
|
32378
|
+
}],
|
|
32379
|
+
"petFeeder.setFeedSound": [{
|
|
32380
|
+
name: "deviceId",
|
|
32381
|
+
form: "single",
|
|
32382
|
+
optional: false
|
|
32383
|
+
}],
|
|
32384
|
+
"petFeeder.setIndicatorLight": [{
|
|
32385
|
+
name: "deviceId",
|
|
32386
|
+
form: "single",
|
|
32387
|
+
optional: false
|
|
32388
|
+
}],
|
|
32389
|
+
"petFeeder.setVolume": [{
|
|
32390
|
+
name: "deviceId",
|
|
32391
|
+
form: "single",
|
|
32392
|
+
optional: false
|
|
32393
|
+
}],
|
|
32394
|
+
"pipelineAnalytics.clearTracks": [{
|
|
32395
|
+
name: "deviceId",
|
|
32396
|
+
form: "single",
|
|
32397
|
+
optional: false
|
|
32398
|
+
}],
|
|
32399
|
+
"pipelineAnalytics.completeRetrainTrack": [{
|
|
32400
|
+
name: "deviceId",
|
|
32401
|
+
form: "single",
|
|
32402
|
+
optional: false
|
|
32403
|
+
}],
|
|
32404
|
+
"pipelineAnalytics.deleteDeviceEvents": [{
|
|
32405
|
+
name: "deviceId",
|
|
32406
|
+
form: "single",
|
|
32407
|
+
optional: false
|
|
32408
|
+
}],
|
|
32409
|
+
"pipelineAnalytics.deleteTracks": [{
|
|
32410
|
+
name: "deviceId",
|
|
32411
|
+
form: "single",
|
|
32412
|
+
optional: false
|
|
32413
|
+
}],
|
|
32414
|
+
"pipelineAnalytics.deselectRetrainFrame": [{
|
|
32415
|
+
name: "deviceId",
|
|
32416
|
+
form: "single",
|
|
32417
|
+
optional: false
|
|
32418
|
+
}],
|
|
32419
|
+
"pipelineAnalytics.getActiveTracks": [{
|
|
32420
|
+
name: "deviceId",
|
|
32421
|
+
form: "single",
|
|
32422
|
+
optional: false
|
|
32423
|
+
}],
|
|
32424
|
+
"pipelineAnalytics.getAudioEvents": [{
|
|
32425
|
+
name: "deviceId",
|
|
32426
|
+
form: "single",
|
|
32427
|
+
optional: false
|
|
32428
|
+
}],
|
|
32429
|
+
"pipelineAnalytics.getEventDensity": [{
|
|
32430
|
+
name: "deviceId",
|
|
32431
|
+
form: "single",
|
|
32432
|
+
optional: false
|
|
32433
|
+
}],
|
|
32434
|
+
"pipelineAnalytics.getKeyEvents": [{
|
|
32435
|
+
name: "deviceId",
|
|
32436
|
+
form: "single",
|
|
32437
|
+
optional: false
|
|
32438
|
+
}],
|
|
32439
|
+
"pipelineAnalytics.getMotionEvents": [{
|
|
32440
|
+
name: "deviceId",
|
|
32441
|
+
form: "single",
|
|
32442
|
+
optional: false
|
|
32443
|
+
}],
|
|
32444
|
+
"pipelineAnalytics.getObjectEvents": [{
|
|
32445
|
+
name: "deviceId",
|
|
32446
|
+
form: "single",
|
|
32447
|
+
optional: false
|
|
32448
|
+
}],
|
|
32449
|
+
"pipelineAnalytics.getRetrainExportUrl": [{
|
|
32450
|
+
name: "deviceIds",
|
|
32451
|
+
form: "array",
|
|
32452
|
+
optional: true
|
|
32453
|
+
}],
|
|
32454
|
+
"pipelineAnalytics.getSensorEvents": [{
|
|
32455
|
+
name: "deviceId",
|
|
32456
|
+
form: "single",
|
|
32457
|
+
optional: false
|
|
32458
|
+
}],
|
|
32459
|
+
"pipelineAnalytics.getTrack": [{
|
|
32460
|
+
name: "deviceId",
|
|
32461
|
+
form: "single",
|
|
32462
|
+
optional: false
|
|
32463
|
+
}],
|
|
32464
|
+
"pipelineAnalytics.getTrainingExportSummary": [{
|
|
32465
|
+
name: "deviceIds",
|
|
32466
|
+
form: "array",
|
|
32467
|
+
optional: true
|
|
32468
|
+
}],
|
|
32469
|
+
"pipelineAnalytics.getTrainingExportUrl": [{
|
|
32470
|
+
name: "deviceIds",
|
|
32471
|
+
form: "array",
|
|
32472
|
+
optional: true
|
|
32473
|
+
}],
|
|
32474
|
+
"pipelineAnalytics.listEventKinds": [{
|
|
32475
|
+
name: "deviceId",
|
|
32476
|
+
form: "single",
|
|
32477
|
+
optional: false
|
|
32478
|
+
}],
|
|
32479
|
+
"pipelineAnalytics.listEventKindsBatch": [{
|
|
32480
|
+
name: "deviceIds",
|
|
32481
|
+
form: "array",
|
|
32482
|
+
optional: false
|
|
32483
|
+
}],
|
|
32484
|
+
"pipelineAnalytics.listOpsLog": [{
|
|
32485
|
+
name: "deviceId",
|
|
32486
|
+
form: "single",
|
|
32487
|
+
optional: true
|
|
32488
|
+
}],
|
|
32489
|
+
"pipelineAnalytics.listRecentTracks": [{
|
|
32490
|
+
name: "deviceIds",
|
|
32491
|
+
form: "array",
|
|
32492
|
+
optional: false
|
|
32493
|
+
}],
|
|
32494
|
+
"pipelineAnalytics.listRetrainStaging": [{
|
|
32495
|
+
name: "deviceIds",
|
|
32496
|
+
form: "array",
|
|
32497
|
+
optional: true
|
|
32498
|
+
}],
|
|
32499
|
+
"pipelineAnalytics.listTracks": [{
|
|
32500
|
+
name: "deviceId",
|
|
32501
|
+
form: "single",
|
|
32502
|
+
optional: false
|
|
32503
|
+
}],
|
|
32504
|
+
"pipelineAnalytics.proposeRetrainAnnotations": [{
|
|
32505
|
+
name: "deviceId",
|
|
32506
|
+
form: "single",
|
|
32507
|
+
optional: false
|
|
32508
|
+
}],
|
|
32509
|
+
"pipelineAnalytics.pruneEventsBefore": [{
|
|
32510
|
+
name: "deviceId",
|
|
32511
|
+
form: "single",
|
|
32512
|
+
optional: false
|
|
32513
|
+
}],
|
|
32514
|
+
"pipelineAnalytics.pruneTracksBefore": [{
|
|
32515
|
+
name: "deviceId",
|
|
32516
|
+
form: "single",
|
|
32517
|
+
optional: false
|
|
32518
|
+
}],
|
|
32519
|
+
"pipelineAnalytics.rebuildObjectEmbeddings": [{
|
|
32520
|
+
name: "deviceId",
|
|
32521
|
+
form: "single",
|
|
32522
|
+
optional: true
|
|
32523
|
+
}],
|
|
32524
|
+
"pipelineAnalytics.restageRetrainTrack": [{
|
|
32525
|
+
name: "deviceId",
|
|
32526
|
+
form: "single",
|
|
32527
|
+
optional: false
|
|
32528
|
+
}],
|
|
32529
|
+
"pipelineAnalytics.saveRetrainAnnotations": [{
|
|
32530
|
+
name: "deviceId",
|
|
32531
|
+
form: "single",
|
|
32532
|
+
optional: false
|
|
32533
|
+
}],
|
|
32534
|
+
"pipelineAnalytics.searchObjectEvents": [{
|
|
32535
|
+
name: "deviceId",
|
|
32536
|
+
form: "single",
|
|
32537
|
+
optional: true
|
|
32538
|
+
}],
|
|
32539
|
+
"pipelineAnalytics.selectRetrainFrames": [{
|
|
32540
|
+
name: "deviceId",
|
|
32541
|
+
form: "single",
|
|
32542
|
+
optional: false
|
|
32543
|
+
}],
|
|
32544
|
+
"pipelineAnalytics.setTrackFlags": [{
|
|
32545
|
+
name: "deviceId",
|
|
32546
|
+
form: "single",
|
|
32547
|
+
optional: false
|
|
32548
|
+
}],
|
|
32549
|
+
"pipelineAnalytics.wipeAllAnalytics": [{
|
|
32550
|
+
name: "deviceId",
|
|
32551
|
+
form: "single",
|
|
32552
|
+
optional: false
|
|
32553
|
+
}],
|
|
32554
|
+
"pipelineExecutor.runPipeline": [{
|
|
32555
|
+
name: "deviceId",
|
|
32556
|
+
form: "single",
|
|
32557
|
+
optional: true
|
|
32558
|
+
}],
|
|
32559
|
+
"pipelineExecutor.runPipelineBatch": [{
|
|
32560
|
+
name: "deviceId",
|
|
32561
|
+
form: "single",
|
|
32562
|
+
optional: true
|
|
32563
|
+
}],
|
|
32564
|
+
"pipelineOrchestrator.assignAudio": [{
|
|
32565
|
+
name: "deviceId",
|
|
32566
|
+
form: "single",
|
|
32567
|
+
optional: false
|
|
32568
|
+
}],
|
|
32569
|
+
"pipelineOrchestrator.assignPipeline": [{
|
|
32570
|
+
name: "deviceId",
|
|
32571
|
+
form: "single",
|
|
32572
|
+
optional: false
|
|
32573
|
+
}],
|
|
32574
|
+
"pipelineOrchestrator.getAudioAssignment": [{
|
|
32575
|
+
name: "deviceId",
|
|
32576
|
+
form: "single",
|
|
32577
|
+
optional: false
|
|
32578
|
+
}],
|
|
32579
|
+
"pipelineOrchestrator.getCameraMetrics": [{
|
|
32580
|
+
name: "deviceId",
|
|
32581
|
+
form: "single",
|
|
32582
|
+
optional: false
|
|
32583
|
+
}],
|
|
32584
|
+
"pipelineOrchestrator.getCameraSettings": [{
|
|
32585
|
+
name: "deviceId",
|
|
32586
|
+
form: "single",
|
|
32587
|
+
optional: false
|
|
32588
|
+
}],
|
|
32589
|
+
"pipelineOrchestrator.getCameraStatus": [{
|
|
32590
|
+
name: "deviceId",
|
|
32591
|
+
form: "single",
|
|
32592
|
+
optional: false
|
|
32593
|
+
}],
|
|
32594
|
+
"pipelineOrchestrator.getCameraStatuses": [{
|
|
32595
|
+
name: "deviceIds",
|
|
32596
|
+
form: "array",
|
|
32597
|
+
optional: true
|
|
32598
|
+
}],
|
|
32599
|
+
"pipelineOrchestrator.getCameraStepOverrides": [{
|
|
32600
|
+
name: "deviceId",
|
|
32601
|
+
form: "single",
|
|
32602
|
+
optional: false
|
|
32603
|
+
}],
|
|
32604
|
+
"pipelineOrchestrator.getCameraSwitches": [{
|
|
32605
|
+
name: "deviceId",
|
|
32606
|
+
form: "single",
|
|
32607
|
+
optional: false
|
|
32608
|
+
}],
|
|
32609
|
+
"pipelineOrchestrator.getPipelineAssignment": [{
|
|
32610
|
+
name: "deviceId",
|
|
32611
|
+
form: "single",
|
|
32612
|
+
optional: false
|
|
32613
|
+
}],
|
|
32614
|
+
"pipelineOrchestrator.getPipelineDevicePin": [{
|
|
32615
|
+
name: "deviceId",
|
|
32616
|
+
form: "single",
|
|
32617
|
+
optional: false
|
|
32618
|
+
}],
|
|
32619
|
+
"pipelineOrchestrator.resolvePipeline": [{
|
|
32620
|
+
name: "deviceId",
|
|
32621
|
+
form: "single",
|
|
32622
|
+
optional: false
|
|
32623
|
+
}],
|
|
32624
|
+
"pipelineOrchestrator.setCameraPipelineForAgent": [{
|
|
32625
|
+
name: "deviceId",
|
|
32626
|
+
form: "single",
|
|
32627
|
+
optional: false
|
|
32628
|
+
}],
|
|
32629
|
+
"pipelineOrchestrator.setCameraStepOverride": [{
|
|
32630
|
+
name: "deviceId",
|
|
32631
|
+
form: "single",
|
|
32632
|
+
optional: false
|
|
32633
|
+
}],
|
|
32634
|
+
"pipelineOrchestrator.setCameraStepToggle": [{
|
|
32635
|
+
name: "deviceId",
|
|
32636
|
+
form: "single",
|
|
32637
|
+
optional: false
|
|
32638
|
+
}],
|
|
32639
|
+
"pipelineOrchestrator.setCameraSwitch": [{
|
|
32640
|
+
name: "deviceId",
|
|
32641
|
+
form: "single",
|
|
32642
|
+
optional: false
|
|
32643
|
+
}],
|
|
32644
|
+
"pipelineOrchestrator.setPipelineDevicePin": [{
|
|
32645
|
+
name: "deviceId",
|
|
32646
|
+
form: "single",
|
|
32647
|
+
optional: false
|
|
32648
|
+
}],
|
|
32649
|
+
"pipelineOrchestrator.unassignAudio": [{
|
|
32650
|
+
name: "deviceId",
|
|
32651
|
+
form: "single",
|
|
32652
|
+
optional: false
|
|
32653
|
+
}],
|
|
32654
|
+
"pipelineOrchestrator.unassignPipeline": [{
|
|
32655
|
+
name: "deviceId",
|
|
32656
|
+
form: "single",
|
|
32657
|
+
optional: false
|
|
32658
|
+
}],
|
|
32659
|
+
"pipelineRunner.attachCamera": [{
|
|
32660
|
+
name: "deviceId",
|
|
32661
|
+
form: "single",
|
|
32662
|
+
optional: false
|
|
32663
|
+
}],
|
|
32664
|
+
"pipelineRunner.detachCamera": [{
|
|
32665
|
+
name: "deviceId",
|
|
32666
|
+
form: "single",
|
|
32667
|
+
optional: false
|
|
32668
|
+
}],
|
|
32669
|
+
"pipelineRunner.getCameraMetrics": [{
|
|
32670
|
+
name: "deviceId",
|
|
32671
|
+
form: "single",
|
|
32672
|
+
optional: false
|
|
32673
|
+
}],
|
|
32674
|
+
"pipelineRunner.reportMotion": [{
|
|
32675
|
+
name: "deviceId",
|
|
32676
|
+
form: "single",
|
|
32677
|
+
optional: false
|
|
32678
|
+
}],
|
|
32679
|
+
"pipelineRunner.runDetailSubtree": [{
|
|
32680
|
+
name: "deviceId",
|
|
32681
|
+
form: "single",
|
|
32682
|
+
optional: false
|
|
32683
|
+
}],
|
|
32684
|
+
"pipelineRunner.runStatelessStep": [{
|
|
32685
|
+
name: "sourceDeviceId",
|
|
32686
|
+
form: "single",
|
|
32687
|
+
optional: false
|
|
32688
|
+
}],
|
|
32689
|
+
"plateGallery.getPlateByTrack": [{
|
|
32690
|
+
name: "deviceId",
|
|
32691
|
+
form: "single",
|
|
32692
|
+
optional: false
|
|
32693
|
+
}],
|
|
32694
|
+
"plateGallery.listPlates": [{
|
|
32695
|
+
name: "deviceId",
|
|
32696
|
+
form: "single",
|
|
32697
|
+
optional: true
|
|
32698
|
+
}],
|
|
32699
|
+
"privacyMask.getOptions": [{
|
|
32700
|
+
name: "deviceId",
|
|
32701
|
+
form: "single",
|
|
32702
|
+
optional: false
|
|
32703
|
+
}],
|
|
32704
|
+
"privacyMask.setAudioEnabled": [{
|
|
32705
|
+
name: "deviceId",
|
|
32706
|
+
form: "single",
|
|
32707
|
+
optional: false
|
|
32708
|
+
}],
|
|
32709
|
+
"privacyMask.setMask": [{
|
|
32710
|
+
name: "deviceId",
|
|
32711
|
+
form: "single",
|
|
32712
|
+
optional: false
|
|
32713
|
+
}],
|
|
32714
|
+
"ptz.continuousMove": [{
|
|
32715
|
+
name: "deviceId",
|
|
32716
|
+
form: "single",
|
|
32717
|
+
optional: false
|
|
32718
|
+
}],
|
|
32719
|
+
"ptz.deletePreset": [{
|
|
32720
|
+
name: "deviceId",
|
|
32721
|
+
form: "single",
|
|
32722
|
+
optional: false
|
|
32723
|
+
}],
|
|
32724
|
+
"ptz.getOptions": [{
|
|
32725
|
+
name: "deviceId",
|
|
32726
|
+
form: "single",
|
|
32727
|
+
optional: false
|
|
32728
|
+
}],
|
|
32729
|
+
"ptz.getPosition": [{
|
|
32730
|
+
name: "deviceId",
|
|
32731
|
+
form: "single",
|
|
32732
|
+
optional: false
|
|
32733
|
+
}],
|
|
32734
|
+
"ptz.getPresets": [{
|
|
32735
|
+
name: "deviceId",
|
|
32736
|
+
form: "single",
|
|
32737
|
+
optional: false
|
|
32738
|
+
}],
|
|
32739
|
+
"ptz.goHome": [{
|
|
32740
|
+
name: "deviceId",
|
|
32741
|
+
form: "single",
|
|
32742
|
+
optional: false
|
|
32743
|
+
}],
|
|
32744
|
+
"ptz.goToPreset": [{
|
|
32745
|
+
name: "deviceId",
|
|
32746
|
+
form: "single",
|
|
32747
|
+
optional: false
|
|
32748
|
+
}],
|
|
32749
|
+
"ptz.move": [{
|
|
32750
|
+
name: "deviceId",
|
|
32751
|
+
form: "single",
|
|
32752
|
+
optional: false
|
|
32753
|
+
}],
|
|
32754
|
+
"ptz.savePreset": [{
|
|
32755
|
+
name: "deviceId",
|
|
32756
|
+
form: "single",
|
|
32757
|
+
optional: false
|
|
32758
|
+
}],
|
|
32759
|
+
"ptz.setAutofocus": [{
|
|
32760
|
+
name: "deviceId",
|
|
32761
|
+
form: "single",
|
|
32762
|
+
optional: false
|
|
32763
|
+
}],
|
|
32764
|
+
"ptz.stop": [{
|
|
32765
|
+
name: "deviceId",
|
|
32766
|
+
form: "single",
|
|
32767
|
+
optional: false
|
|
32768
|
+
}],
|
|
32769
|
+
"ptzAutotrack.getSettings": [{
|
|
32770
|
+
name: "deviceId",
|
|
32771
|
+
form: "single",
|
|
32772
|
+
optional: false
|
|
32773
|
+
}],
|
|
32774
|
+
"ptzAutotrack.getStatus": [{
|
|
32775
|
+
name: "deviceId",
|
|
32776
|
+
form: "single",
|
|
32777
|
+
optional: false
|
|
32778
|
+
}],
|
|
32779
|
+
"ptzAutotrack.setEnabled": [{
|
|
32780
|
+
name: "deviceId",
|
|
32781
|
+
form: "single",
|
|
32782
|
+
optional: false
|
|
32783
|
+
}],
|
|
32784
|
+
"ptzAutotrack.setSettings": [{
|
|
32785
|
+
name: "deviceId",
|
|
32786
|
+
form: "single",
|
|
32787
|
+
optional: false
|
|
32788
|
+
}],
|
|
32789
|
+
"reboot.reboot": [{
|
|
32790
|
+
name: "deviceId",
|
|
32791
|
+
form: "single",
|
|
32792
|
+
optional: false
|
|
32793
|
+
}],
|
|
32794
|
+
"recording.deleteFootprint": [{
|
|
32795
|
+
name: "deviceId",
|
|
32796
|
+
form: "single",
|
|
32797
|
+
optional: false
|
|
32798
|
+
}],
|
|
32799
|
+
"recording.getAvailability": [{
|
|
32800
|
+
name: "deviceId",
|
|
32801
|
+
form: "single",
|
|
32802
|
+
optional: false
|
|
32803
|
+
}],
|
|
32804
|
+
"recording.getDaysWithRecordings": [{
|
|
32805
|
+
name: "deviceId",
|
|
32806
|
+
form: "single",
|
|
32807
|
+
optional: false
|
|
32808
|
+
}],
|
|
32809
|
+
"recording.getDeviceConfig": [{
|
|
32810
|
+
name: "deviceId",
|
|
32811
|
+
form: "single",
|
|
32812
|
+
optional: false
|
|
32813
|
+
}],
|
|
32814
|
+
"recording.getPlaybackManifest": [{
|
|
32815
|
+
name: "deviceId",
|
|
32816
|
+
form: "single",
|
|
32817
|
+
optional: false
|
|
32818
|
+
}],
|
|
32819
|
+
"recording.listOpsLog": [{
|
|
32820
|
+
name: "deviceId",
|
|
32821
|
+
form: "single",
|
|
32822
|
+
optional: true
|
|
32823
|
+
}],
|
|
32824
|
+
"recording.locateSegment": [{
|
|
32825
|
+
name: "deviceId",
|
|
32826
|
+
form: "single",
|
|
32827
|
+
optional: false
|
|
32828
|
+
}],
|
|
32829
|
+
"recording.pruneFootage": [{
|
|
32830
|
+
name: "deviceId",
|
|
32831
|
+
form: "single",
|
|
32832
|
+
optional: false
|
|
32833
|
+
}],
|
|
32834
|
+
"recording.readGopBytes": [{
|
|
32835
|
+
name: "deviceId",
|
|
32836
|
+
form: "single",
|
|
32837
|
+
optional: false
|
|
32838
|
+
}],
|
|
32839
|
+
"recording.readSegmentBytes": [{
|
|
32840
|
+
name: "deviceId",
|
|
32841
|
+
form: "single",
|
|
32842
|
+
optional: false
|
|
32843
|
+
}],
|
|
32844
|
+
"recording.relocateFootage": [{
|
|
32845
|
+
name: "deviceId",
|
|
32846
|
+
form: "single",
|
|
32847
|
+
optional: true
|
|
32848
|
+
}],
|
|
32849
|
+
"recording.renderClip": [{
|
|
32850
|
+
name: "deviceId",
|
|
32851
|
+
form: "single",
|
|
32852
|
+
optional: false
|
|
32853
|
+
}],
|
|
32854
|
+
"recording.renderGif": [{
|
|
32855
|
+
name: "deviceId",
|
|
32856
|
+
form: "single",
|
|
32857
|
+
optional: false
|
|
32858
|
+
}],
|
|
32859
|
+
"recording.rescanStorage": [{
|
|
32860
|
+
name: "deviceId",
|
|
32861
|
+
form: "single",
|
|
32862
|
+
optional: false
|
|
32863
|
+
}],
|
|
32864
|
+
"recording.setDeviceConfig": [{
|
|
32865
|
+
name: "deviceId",
|
|
32866
|
+
form: "single",
|
|
32867
|
+
optional: false
|
|
32868
|
+
}],
|
|
32869
|
+
"recording.startStorageMigrationMove": [{
|
|
32870
|
+
name: "deviceId",
|
|
32871
|
+
form: "single",
|
|
32872
|
+
optional: true
|
|
32873
|
+
}],
|
|
32874
|
+
"recordingExport.createExport": [{
|
|
32875
|
+
name: "deviceId",
|
|
32876
|
+
form: "single",
|
|
32877
|
+
optional: false
|
|
32878
|
+
}],
|
|
32879
|
+
"recordingExport.listExports": [{
|
|
32880
|
+
name: "deviceId",
|
|
32881
|
+
form: "single",
|
|
32882
|
+
optional: true
|
|
32883
|
+
}],
|
|
32884
|
+
"sceneMonitor.captureReference": [{
|
|
32885
|
+
name: "deviceId",
|
|
32886
|
+
form: "single",
|
|
32887
|
+
optional: false
|
|
32888
|
+
}],
|
|
32889
|
+
"sceneMonitor.createScene": [{
|
|
32890
|
+
name: "deviceId",
|
|
32891
|
+
form: "single",
|
|
32892
|
+
optional: false
|
|
32893
|
+
}],
|
|
32894
|
+
"sceneMonitor.deleteReference": [{
|
|
32895
|
+
name: "deviceId",
|
|
32896
|
+
form: "single",
|
|
32897
|
+
optional: false
|
|
32898
|
+
}],
|
|
32899
|
+
"sceneMonitor.deleteScene": [{
|
|
32900
|
+
name: "deviceId",
|
|
32901
|
+
form: "single",
|
|
32902
|
+
optional: false
|
|
32903
|
+
}],
|
|
32904
|
+
"sceneMonitor.listScenes": [{
|
|
32905
|
+
name: "deviceId",
|
|
32906
|
+
form: "single",
|
|
32907
|
+
optional: false
|
|
32908
|
+
}],
|
|
32909
|
+
"sceneMonitor.recheckNow": [{
|
|
32910
|
+
name: "deviceId",
|
|
32911
|
+
form: "single",
|
|
32912
|
+
optional: false
|
|
32913
|
+
}],
|
|
32914
|
+
"sceneMonitor.updateScene": [{
|
|
32915
|
+
name: "deviceId",
|
|
32916
|
+
form: "single",
|
|
32917
|
+
optional: false
|
|
32918
|
+
}],
|
|
32919
|
+
"scriptRunner.run": [{
|
|
32920
|
+
name: "deviceId",
|
|
32921
|
+
form: "single",
|
|
32922
|
+
optional: false
|
|
32923
|
+
}],
|
|
32924
|
+
"scriptRunner.stop": [{
|
|
32925
|
+
name: "deviceId",
|
|
32926
|
+
form: "single",
|
|
32927
|
+
optional: false
|
|
32928
|
+
}],
|
|
32929
|
+
"snapshot.getSnapshot": [{
|
|
32930
|
+
name: "deviceId",
|
|
32931
|
+
form: "single",
|
|
32932
|
+
optional: false
|
|
32933
|
+
}],
|
|
32934
|
+
"snapshot.getSnapshotOverview": [{
|
|
32935
|
+
name: "deviceIds",
|
|
32936
|
+
form: "array",
|
|
32937
|
+
optional: false
|
|
32938
|
+
}],
|
|
32939
|
+
"snapshot.invalidateCache": [{
|
|
32940
|
+
name: "deviceId",
|
|
32941
|
+
form: "single",
|
|
32942
|
+
optional: false
|
|
32943
|
+
}],
|
|
32944
|
+
"streamBroker.acquireEgressTranscode": [{
|
|
32945
|
+
name: "deviceId",
|
|
32946
|
+
form: "single",
|
|
32947
|
+
optional: false
|
|
32948
|
+
}],
|
|
32949
|
+
"streamBroker.assignProfile": [{
|
|
32950
|
+
name: "deviceId",
|
|
32951
|
+
form: "single",
|
|
32952
|
+
optional: false
|
|
32953
|
+
}],
|
|
32954
|
+
"streamBroker.getDeviceAudioMute": [{
|
|
32955
|
+
name: "deviceId",
|
|
32956
|
+
form: "single",
|
|
32957
|
+
optional: false
|
|
32958
|
+
}],
|
|
32959
|
+
"streamBroker.getStreamWithCodec": [{
|
|
32960
|
+
name: "deviceId",
|
|
32961
|
+
form: "single",
|
|
32962
|
+
optional: false
|
|
32963
|
+
}],
|
|
32964
|
+
"streamBroker.produceEventMedia": [{
|
|
32965
|
+
name: "deviceId",
|
|
32966
|
+
form: "single",
|
|
32967
|
+
optional: false
|
|
32968
|
+
}],
|
|
32969
|
+
"streamBroker.publishCameraStream": [{
|
|
32970
|
+
name: "deviceId",
|
|
32971
|
+
form: "single",
|
|
32972
|
+
optional: false
|
|
32973
|
+
}],
|
|
32974
|
+
"streamBroker.renderPreBufferClip": [{
|
|
32975
|
+
name: "deviceId",
|
|
32976
|
+
form: "single",
|
|
32977
|
+
optional: false
|
|
32978
|
+
}],
|
|
32979
|
+
"streamBroker.restartProfile": [{
|
|
32980
|
+
name: "deviceId",
|
|
32981
|
+
form: "single",
|
|
32982
|
+
optional: false
|
|
32983
|
+
}],
|
|
32984
|
+
"streamBroker.retractCameraStream": [{
|
|
32985
|
+
name: "deviceId",
|
|
32986
|
+
form: "single",
|
|
32987
|
+
optional: false
|
|
32988
|
+
}],
|
|
32989
|
+
"streamBroker.setDeviceAudioMute": [{
|
|
32990
|
+
name: "deviceId",
|
|
32991
|
+
form: "single",
|
|
32992
|
+
optional: false
|
|
32993
|
+
}],
|
|
32994
|
+
"streamBroker.unassignProfile": [{
|
|
32995
|
+
name: "deviceId",
|
|
32996
|
+
form: "single",
|
|
32997
|
+
optional: false
|
|
32998
|
+
}],
|
|
32999
|
+
"streamCatalog.getCatalog": [{
|
|
33000
|
+
name: "deviceId",
|
|
33001
|
+
form: "single",
|
|
33002
|
+
optional: false
|
|
33003
|
+
}],
|
|
33004
|
+
"streamParams.getConfigSchema": [{
|
|
33005
|
+
name: "deviceId",
|
|
33006
|
+
form: "single",
|
|
33007
|
+
optional: false
|
|
33008
|
+
}],
|
|
33009
|
+
"streamParams.getOptions": [{
|
|
33010
|
+
name: "deviceId",
|
|
33011
|
+
form: "single",
|
|
33012
|
+
optional: false
|
|
33013
|
+
}],
|
|
33014
|
+
"streamParams.setProfile": [{
|
|
33015
|
+
name: "deviceId",
|
|
33016
|
+
form: "single",
|
|
33017
|
+
optional: false
|
|
33018
|
+
}],
|
|
33019
|
+
"switch.setState": [{
|
|
33020
|
+
name: "deviceId",
|
|
33021
|
+
form: "single",
|
|
33022
|
+
optional: false
|
|
33023
|
+
}],
|
|
33024
|
+
"vacuumControl.locate": [{
|
|
33025
|
+
name: "deviceId",
|
|
33026
|
+
form: "single",
|
|
33027
|
+
optional: false
|
|
33028
|
+
}],
|
|
33029
|
+
"vacuumControl.pause": [{
|
|
33030
|
+
name: "deviceId",
|
|
33031
|
+
form: "single",
|
|
33032
|
+
optional: false
|
|
33033
|
+
}],
|
|
33034
|
+
"vacuumControl.returnToBase": [{
|
|
33035
|
+
name: "deviceId",
|
|
33036
|
+
form: "single",
|
|
33037
|
+
optional: false
|
|
33038
|
+
}],
|
|
33039
|
+
"vacuumControl.setFanSpeed": [{
|
|
33040
|
+
name: "deviceId",
|
|
33041
|
+
form: "single",
|
|
33042
|
+
optional: false
|
|
33043
|
+
}],
|
|
33044
|
+
"vacuumControl.start": [{
|
|
33045
|
+
name: "deviceId",
|
|
33046
|
+
form: "single",
|
|
33047
|
+
optional: false
|
|
33048
|
+
}],
|
|
33049
|
+
"vacuumControl.stop": [{
|
|
33050
|
+
name: "deviceId",
|
|
33051
|
+
form: "single",
|
|
33052
|
+
optional: false
|
|
33053
|
+
}],
|
|
33054
|
+
"valve.close": [{
|
|
33055
|
+
name: "deviceId",
|
|
33056
|
+
form: "single",
|
|
33057
|
+
optional: false
|
|
33058
|
+
}],
|
|
33059
|
+
"valve.open": [{
|
|
33060
|
+
name: "deviceId",
|
|
33061
|
+
form: "single",
|
|
33062
|
+
optional: false
|
|
33063
|
+
}],
|
|
33064
|
+
"valve.setPosition": [{
|
|
33065
|
+
name: "deviceId",
|
|
33066
|
+
form: "single",
|
|
33067
|
+
optional: false
|
|
33068
|
+
}],
|
|
33069
|
+
"valve.stop": [{
|
|
33070
|
+
name: "deviceId",
|
|
33071
|
+
form: "single",
|
|
33072
|
+
optional: false
|
|
33073
|
+
}],
|
|
33074
|
+
"videoclips.getClipPlayback": [{
|
|
33075
|
+
name: "deviceId",
|
|
33076
|
+
form: "single",
|
|
33077
|
+
optional: false
|
|
33078
|
+
}],
|
|
33079
|
+
"videoclips.listClips": [{
|
|
33080
|
+
name: "deviceId",
|
|
33081
|
+
form: "single",
|
|
33082
|
+
optional: false
|
|
33083
|
+
}],
|
|
33084
|
+
"waterHeater.setAway": [{
|
|
33085
|
+
name: "deviceId",
|
|
33086
|
+
form: "single",
|
|
33087
|
+
optional: false
|
|
33088
|
+
}],
|
|
33089
|
+
"waterHeater.setOperationMode": [{
|
|
33090
|
+
name: "deviceId",
|
|
33091
|
+
form: "single",
|
|
33092
|
+
optional: false
|
|
33093
|
+
}],
|
|
33094
|
+
"waterHeater.setTargetTemp": [{
|
|
33095
|
+
name: "deviceId",
|
|
33096
|
+
form: "single",
|
|
33097
|
+
optional: false
|
|
33098
|
+
}],
|
|
33099
|
+
"webrtcSession.addIceCandidate": [{
|
|
33100
|
+
name: "deviceId",
|
|
33101
|
+
form: "single",
|
|
33102
|
+
optional: false
|
|
33103
|
+
}],
|
|
33104
|
+
"webrtcSession.closeSession": [{
|
|
33105
|
+
name: "deviceId",
|
|
33106
|
+
form: "single",
|
|
33107
|
+
optional: false
|
|
33108
|
+
}],
|
|
33109
|
+
"webrtcSession.createSession": [{
|
|
33110
|
+
name: "deviceId",
|
|
33111
|
+
form: "single",
|
|
33112
|
+
optional: false
|
|
33113
|
+
}],
|
|
33114
|
+
"webrtcSession.getIceCandidates": [{
|
|
33115
|
+
name: "deviceId",
|
|
33116
|
+
form: "single",
|
|
33117
|
+
optional: false
|
|
33118
|
+
}],
|
|
33119
|
+
"webrtcSession.getSessionState": [{
|
|
33120
|
+
name: "deviceId",
|
|
33121
|
+
form: "single",
|
|
33122
|
+
optional: false
|
|
33123
|
+
}],
|
|
33124
|
+
"webrtcSession.handleAnswer": [{
|
|
33125
|
+
name: "deviceId",
|
|
33126
|
+
form: "single",
|
|
33127
|
+
optional: false
|
|
33128
|
+
}],
|
|
33129
|
+
"webrtcSession.handleOffer": [{
|
|
33130
|
+
name: "deviceId",
|
|
33131
|
+
form: "single",
|
|
33132
|
+
optional: false
|
|
33133
|
+
}],
|
|
33134
|
+
"webrtcSession.hasAdaptiveBitrate": [{
|
|
33135
|
+
name: "deviceId",
|
|
33136
|
+
form: "single",
|
|
33137
|
+
optional: false
|
|
33138
|
+
}],
|
|
33139
|
+
"webrtcSession.listStreams": [{
|
|
33140
|
+
name: "deviceId",
|
|
33141
|
+
form: "single",
|
|
33142
|
+
optional: false
|
|
33143
|
+
}],
|
|
33144
|
+
"zoneAnalytics.getCameraHistory": [{
|
|
33145
|
+
name: "deviceId",
|
|
33146
|
+
form: "single",
|
|
33147
|
+
optional: false
|
|
33148
|
+
}],
|
|
33149
|
+
"zoneAnalytics.getCurrentSnapshot": [{
|
|
33150
|
+
name: "deviceId",
|
|
33151
|
+
form: "single",
|
|
33152
|
+
optional: false
|
|
33153
|
+
}],
|
|
33154
|
+
"zoneAnalytics.getUnzonedHistory": [{
|
|
33155
|
+
name: "deviceId",
|
|
33156
|
+
form: "single",
|
|
33157
|
+
optional: false
|
|
33158
|
+
}],
|
|
33159
|
+
"zoneAnalytics.getZoneHistory": [{
|
|
33160
|
+
name: "deviceId",
|
|
33161
|
+
form: "single",
|
|
33162
|
+
optional: false
|
|
33163
|
+
}],
|
|
33164
|
+
"zoneRules.listRules": [{
|
|
33165
|
+
name: "deviceId",
|
|
33166
|
+
form: "single",
|
|
33167
|
+
optional: false
|
|
33168
|
+
}],
|
|
33169
|
+
"zoneRules.setRules": [{
|
|
33170
|
+
name: "deviceId",
|
|
33171
|
+
form: "single",
|
|
33172
|
+
optional: false
|
|
33173
|
+
}],
|
|
33174
|
+
"zones.addZone": [{
|
|
33175
|
+
name: "deviceId",
|
|
33176
|
+
form: "single",
|
|
33177
|
+
optional: false
|
|
33178
|
+
}],
|
|
33179
|
+
"zones.listZones": [{
|
|
33180
|
+
name: "deviceId",
|
|
33181
|
+
form: "single",
|
|
33182
|
+
optional: false
|
|
33183
|
+
}],
|
|
33184
|
+
"zones.removeZone": [{
|
|
33185
|
+
name: "deviceId",
|
|
33186
|
+
form: "single",
|
|
33187
|
+
optional: false
|
|
33188
|
+
}],
|
|
33189
|
+
"zones.updateZone": [{
|
|
33190
|
+
name: "deviceId",
|
|
33191
|
+
form: "single",
|
|
33192
|
+
optional: false
|
|
33193
|
+
}]
|
|
33194
|
+
});
|
|
31366
33195
|
Object.freeze({
|
|
31367
33196
|
"broker": "broker",
|
|
31368
33197
|
"device-export": "device-export",
|
|
@@ -32082,7 +33911,7 @@ var Fmp4FragmentChild = class {
|
|
|
32082
33911
|
meta: {
|
|
32083
33912
|
sourceId: this.args.sourceId,
|
|
32084
33913
|
decodeHwAccel: requested,
|
|
32085
|
-
error: errMsg$
|
|
33914
|
+
error: errMsg$15(err)
|
|
32086
33915
|
}
|
|
32087
33916
|
});
|
|
32088
33917
|
this.killChild();
|
|
@@ -32261,7 +34090,7 @@ var Fmp4FragmentChild = class {
|
|
|
32261
34090
|
tags: { deviceId: this.args.deviceId },
|
|
32262
34091
|
meta: {
|
|
32263
34092
|
sourceId: this.args.sourceId,
|
|
32264
|
-
error: errMsg$
|
|
34093
|
+
error: errMsg$15(err)
|
|
32265
34094
|
}
|
|
32266
34095
|
});
|
|
32267
34096
|
}
|
|
@@ -78606,7 +80435,7 @@ function clearPairingFiles(accessoryUuid, logger) {
|
|
|
78606
80435
|
}
|
|
78607
80436
|
//#endregion
|
|
78608
80437
|
//#region src/hap-setup-uri.ts
|
|
78609
|
-
function errMsg$
|
|
80438
|
+
function errMsg$14(e) {
|
|
78610
80439
|
return e instanceof Error ? e.message : String(e);
|
|
78611
80440
|
}
|
|
78612
80441
|
/**
|
|
@@ -78633,11 +80462,79 @@ function firstExposedAccessorySetupUri(exposed, logger) {
|
|
|
78633
80462
|
try {
|
|
78634
80463
|
return first.setupURI();
|
|
78635
80464
|
} catch (err) {
|
|
78636
|
-
logger.debug("export-hap: setupURI failed on first exposed accessory", { meta: { error: errMsg$
|
|
80465
|
+
logger.debug("export-hap: setupURI failed on first exposed accessory", { meta: { error: errMsg$14(err) } });
|
|
78637
80466
|
return;
|
|
78638
80467
|
}
|
|
78639
80468
|
}
|
|
78640
80469
|
}
|
|
80470
|
+
//#endregion
|
|
80471
|
+
//#region src/mappers/builders/accessory-info.ts
|
|
80472
|
+
/**
|
|
80473
|
+
* `Service.AccessoryInformation` — the manufacturer / model / firmware /
|
|
80474
|
+
* serial block every HomeKit accessory carries.
|
|
80475
|
+
*
|
|
80476
|
+
* One implementation for both accessory shapes (camera and generic): the
|
|
80477
|
+
* fields come from the device's own metadata either way, and a second copy
|
|
80478
|
+
* would be a second answer to "what serial does this device publish".
|
|
80479
|
+
* Metadata is best-effort — a device that cannot answer still publishes.
|
|
80480
|
+
*/
|
|
80481
|
+
async function populateAccessoryInfo(accessory, proxy, displayName, modelFallback) {
|
|
80482
|
+
const info = accessory.getService(import_dist.Service.AccessoryInformation);
|
|
80483
|
+
if (!info) return;
|
|
80484
|
+
try {
|
|
80485
|
+
const device = await proxy.deviceManager?.getDevice({});
|
|
80486
|
+
const metadata = readMetadata(device);
|
|
80487
|
+
info.setCharacteristic(import_dist.Characteristic.Name, device?.name ?? displayName);
|
|
80488
|
+
info.setCharacteristic(import_dist.Characteristic.Manufacturer, stringOr(metadata?.manufacturer, "CamStack"));
|
|
80489
|
+
info.setCharacteristic(import_dist.Characteristic.Model, stringOr(metadata?.model, modelFallback));
|
|
80490
|
+
info.setCharacteristic(import_dist.Characteristic.FirmwareRevision, stringOr(metadata?.firmware, "0.0.0"));
|
|
80491
|
+
info.setCharacteristic(import_dist.Characteristic.SerialNumber, stringOr(metadata?.sn, `camstack-${proxy.deviceId}`));
|
|
80492
|
+
} catch {}
|
|
80493
|
+
}
|
|
80494
|
+
/** The four fields this module reads, or `null` — the device record's
|
|
80495
|
+
* `metadata` is an open bag and nothing else here depends on its shape. */
|
|
80496
|
+
function readMetadata(device) {
|
|
80497
|
+
const metadata = device?.metadata;
|
|
80498
|
+
if (metadata === null || typeof metadata !== "object") return {};
|
|
80499
|
+
const entries = new Map(Object.entries(metadata));
|
|
80500
|
+
return {
|
|
80501
|
+
model: stringOrNull(entries.get("model")),
|
|
80502
|
+
manufacturer: stringOrNull(entries.get("manufacturer")),
|
|
80503
|
+
firmware: stringOrNull(entries.get("firmware")),
|
|
80504
|
+
sn: stringOrNull(entries.get("sn"))
|
|
80505
|
+
};
|
|
80506
|
+
}
|
|
80507
|
+
function stringOrNull(value) {
|
|
80508
|
+
return typeof value === "string" ? value : null;
|
|
80509
|
+
}
|
|
80510
|
+
function stringOr(value, fallback) {
|
|
80511
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
80512
|
+
}
|
|
80513
|
+
//#endregion
|
|
80514
|
+
//#region src/mappers/builders/generic/characteristic-update.ts
|
|
80515
|
+
/**
|
|
80516
|
+
* Parse `status` with the capability's OWN Zod schema and turn it into
|
|
80517
|
+
* characteristic writes.
|
|
80518
|
+
*
|
|
80519
|
+
* Duck-typing the payload here would be the second source of truth about what a
|
|
80520
|
+
* cap reports; the schema is the first and only one. A payload that does not
|
|
80521
|
+
* match yields NO updates — never a partial or invented value — and the caller
|
|
80522
|
+
* logs the drop, because a sensor that silently stops moving is
|
|
80523
|
+
* indistinguishable from a sensor that never changed.
|
|
80524
|
+
*
|
|
80525
|
+
* Rows `.pick()` only the fields they read, so a provider omitting a timestamp
|
|
80526
|
+
* cannot silence a sensor.
|
|
80527
|
+
*/
|
|
80528
|
+
function reader(schema, toUpdates) {
|
|
80529
|
+
return (status) => {
|
|
80530
|
+
const parsed = schema.safeParse(status);
|
|
80531
|
+
return parsed.success ? toUpdates(parsed.data) : [];
|
|
80532
|
+
};
|
|
80533
|
+
}
|
|
80534
|
+
/** Push every update onto `service`. */
|
|
80535
|
+
function applyUpdates(service, updates) {
|
|
80536
|
+
for (const update of updates) service.updateCharacteristic(update.characteristic, update.value);
|
|
80537
|
+
}
|
|
78641
80538
|
/**
|
|
78642
80539
|
* hap-nodejs' `checkName` regex, verbatim.
|
|
78643
80540
|
*
|
|
@@ -78774,6 +80671,92 @@ function titleCase(raw) {
|
|
|
78774
80671
|
return raw.split(/[-_\s]+/u).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
78775
80672
|
}
|
|
78776
80673
|
//#endregion
|
|
80674
|
+
//#region src/mappers/builders/service-label.ts
|
|
80675
|
+
/**
|
|
80676
|
+
* The ONE place a secondary service on the camera accessory gets its label.
|
|
80677
|
+
*
|
|
80678
|
+
* A "secondary service" here is a Switch or Lightbulb published alongside the
|
|
80679
|
+
* camera on the same accessory — the privacy switch, each accessory child
|
|
80680
|
+
* (siren, floodlight), each PTZ action. iOS Home renders these as their own
|
|
80681
|
+
* controls, and the operator has seen them as "Interruttore 1", "Interruttore
|
|
80682
|
+
* 2" through three separate rounds of fixes.
|
|
80683
|
+
*
|
|
80684
|
+
* ## Why `Name` alone cannot rename anything
|
|
80685
|
+
*
|
|
80686
|
+
* Two facts about hap-nodejs 2.1.7, both measured against the installed copy
|
|
80687
|
+
* rather than reasoned about:
|
|
80688
|
+
*
|
|
80689
|
+
* 1. `accessory.addService(Type, displayName, subtype)` ALREADY writes
|
|
80690
|
+
* `displayName` to `Characteristic.Name` (`Service` constructor). So every
|
|
80691
|
+
* round of this bug — including the one that moved the label onto
|
|
80692
|
+
* `ConfiguredName` — shipped with `Name` correctly set. "iOS had no name
|
|
80693
|
+
* to render" was never true.
|
|
80694
|
+
* 2. The mDNS configuration number (`c#`) is a sha1 over
|
|
80695
|
+
* `internalHAPRepresentation(false)`, which OMITS characteristic VALUES.
|
|
80696
|
+
* Changing the string in `Name` therefore does not bump `c#`, a paired
|
|
80697
|
+
* controller gets no signal to re-read `/accessories`, and the name it
|
|
80698
|
+
* cached at first enumeration stands forever.
|
|
80699
|
+
*
|
|
80700
|
+
* `Name` is also declared `pr` only — paired read, no write, no notify. It is
|
|
80701
|
+
* the seed a controller seeds its database from once; it is not a channel.
|
|
80702
|
+
*
|
|
80703
|
+
* ## Why `ConfiguredName`
|
|
80704
|
+
*
|
|
80705
|
+
* `ConfiguredName` (`000000E3`) is declared `pr | pw | ev` — the only name
|
|
80706
|
+
* characteristic a controller may write and may subscribe to. It is what iOS
|
|
80707
|
+
* 16+ reads for a service the user can rename, and adding it CHANGES the
|
|
80708
|
+
* accessory structure, so `c#` does bump and the controller re-reads.
|
|
80709
|
+
*
|
|
80710
|
+
* It was removed once because hap-nodejs logged
|
|
80711
|
+
*
|
|
80712
|
+
* ```
|
|
80713
|
+
* Characteristic not in required or optional characteristic section for
|
|
80714
|
+
* service Switch. Adding anyway.
|
|
80715
|
+
* ```
|
|
80716
|
+
*
|
|
80717
|
+
* That line is a WARNING, not a rejection: `Service.getCharacteristic` calls
|
|
80718
|
+
* `addCharacteristic` unconditionally and only then emits the warning. The
|
|
80719
|
+
* characteristic was always present and always published. hap-nodejs'
|
|
80720
|
+
* per-service optional lists simply predate `ConfiguredName` being valid on
|
|
80721
|
+
* any service.
|
|
80722
|
+
*
|
|
80723
|
+
* Registering it with {@link Service.addOptionalCharacteristic} first takes
|
|
80724
|
+
* the branch above the warning, so the accessory still builds with ZERO
|
|
80725
|
+
* characteristic warnings — which is what `service-naming.spec.ts` asserts.
|
|
80726
|
+
*
|
|
80727
|
+
* ## Scope: EVERY service, including the sensors
|
|
80728
|
+
*
|
|
80729
|
+
* An earlier round applied this to Switch- and Lightbulb-shaped services only,
|
|
80730
|
+
* on the theory that `Service.MotionSensor` and `Service.Battery` are not
|
|
80731
|
+
* separately named tiles in iOS Home and that naming them would be a guess.
|
|
80732
|
+
*
|
|
80733
|
+
* That theory was never measured, and it had a cost: it left services on the
|
|
80734
|
+
* accessory whose name a paired controller could never be told about, and it
|
|
80735
|
+
* made "did `ConfiguredName` fix the operator's 'Interruttore N'?" unanswerable
|
|
80736
|
+
* — a negative result on a partial application proves nothing about the
|
|
80737
|
+
* mechanism. Every service this addon publishes now carries both
|
|
80738
|
+
* characteristics, on the camera accessory and on the generic one.
|
|
80739
|
+
*
|
|
80740
|
+
* The reasoning above is mechanism, not measurement: it says why `Name` alone
|
|
80741
|
+
* CANNOT work and why `ConfiguredName` is the only characteristic that can. It
|
|
80742
|
+
* does not prove iOS renders it on every service shape. That is an observation
|
|
80743
|
+
* only a re-paired controller can make — and after a change like this one, the
|
|
80744
|
+
* controller must be re-paired, because a cached accessory database is exactly
|
|
80745
|
+
* what the whole mechanism is about.
|
|
80746
|
+
*/
|
|
80747
|
+
/**
|
|
80748
|
+
* Publish `name` as both the immutable `Name` and the controller-visible
|
|
80749
|
+
* `ConfiguredName` of `service`.
|
|
80750
|
+
*
|
|
80751
|
+
* `name` must already be HAP-valid — build it with `service-names.ts`, which
|
|
80752
|
+
* cannot return a string hap-nodejs' `checkName` would warn about.
|
|
80753
|
+
*/
|
|
80754
|
+
function applyServiceLabel(service, name) {
|
|
80755
|
+
service.setCharacteristic(import_dist.Characteristic.Name, name);
|
|
80756
|
+
if (!service.optionalCharacteristics.some((characteristic) => characteristic.UUID === import_dist.Characteristic.ConfiguredName.UUID)) service.addOptionalCharacteristic(import_dist.Characteristic.ConfiguredName);
|
|
80757
|
+
service.setCharacteristic(import_dist.Characteristic.ConfiguredName, name);
|
|
80758
|
+
}
|
|
80759
|
+
//#endregion
|
|
78777
80760
|
//#region src/mappers/builders/battery.ts
|
|
78778
80761
|
/**
|
|
78779
80762
|
* Battery builder — surfaces a battery-operated camera's power state
|
|
@@ -78783,34 +80766,56 @@ function titleCase(raw) {
|
|
|
78783
80766
|
* subscribes to the runtime-state slice so iOS Home reflects level /
|
|
78784
80767
|
* charging changes pushed by the firmware without a poll loop.
|
|
78785
80768
|
*
|
|
80769
|
+
* The status→characteristic mapping is {@link batteryCharacteristicUpdates},
|
|
80770
|
+
* exported because the generic (non-camera) export path publishes the same
|
|
80771
|
+
* `Service.Battery` from the same cap — one derivation, two accessory shapes.
|
|
80772
|
+
*
|
|
80773
|
+
* Skipped silently when the `battery` cap is not bound — caller checks
|
|
80774
|
+
* cap presence before invoking this builder (see `camera-accessory.ts`).
|
|
80775
|
+
*/
|
|
80776
|
+
var LOW_BATTERY_THRESHOLD_PCT = 20;
|
|
80777
|
+
/**
|
|
78786
80778
|
* Mapping:
|
|
78787
80779
|
* - `BatteryStatus.percentage` (0..100) → `Characteristic.BatteryLevel`
|
|
78788
80780
|
* - `BatteryStatus.charging`:
|
|
78789
80781
|
* `'none'` → `ChargingState.NOT_CHARGING`
|
|
78790
80782
|
* `'dc' | 'solar'` → `ChargingState.CHARGING`
|
|
78791
80783
|
* - `percentage <= LOW_BATTERY_THRESHOLD_PCT` → `StatusLowBattery.LOW`
|
|
78792
|
-
*
|
|
78793
|
-
* Skipped silently when the `battery` cap is not bound — caller checks
|
|
78794
|
-
* cap presence before invoking this builder (see `camera-accessory.ts`).
|
|
78795
80784
|
*/
|
|
78796
|
-
var
|
|
80785
|
+
var batteryCharacteristicUpdates = reader(BatteryStatusSchema.pick({ percentage: true }).extend({ charging: BatteryStatusSchema.shape.charging.optional() }), (status) => {
|
|
80786
|
+
const pct = Math.max(0, Math.min(100, Math.round(status.percentage)));
|
|
80787
|
+
return [
|
|
80788
|
+
{
|
|
80789
|
+
characteristic: import_dist.Characteristic.BatteryLevel,
|
|
80790
|
+
value: pct
|
|
80791
|
+
},
|
|
80792
|
+
...status.charging === void 0 ? [] : [{
|
|
80793
|
+
characteristic: import_dist.Characteristic.ChargingState,
|
|
80794
|
+
value: status.charging === "none" ? import_dist.Characteristic.ChargingState.NOT_CHARGING : import_dist.Characteristic.ChargingState.CHARGING
|
|
80795
|
+
}],
|
|
80796
|
+
{
|
|
80797
|
+
characteristic: import_dist.Characteristic.StatusLowBattery,
|
|
80798
|
+
value: pct <= LOW_BATTERY_THRESHOLD_PCT ? import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL
|
|
80799
|
+
}
|
|
80800
|
+
];
|
|
80801
|
+
});
|
|
78797
80802
|
async function buildBattery(bctx) {
|
|
78798
80803
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
78799
80804
|
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
78800
|
-
const
|
|
80805
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
80806
|
+
const service = accessory.addService(import_dist.Service.Battery, label);
|
|
80807
|
+
applyServiceLabel(service, label);
|
|
78801
80808
|
try {
|
|
78802
80809
|
const status = await proxy.battery?.getStatus({});
|
|
78803
|
-
if (status) applyToService(service, status);
|
|
80810
|
+
if (status !== void 0 && status !== null) applyToService(service, status);
|
|
78804
80811
|
} catch (err) {
|
|
78805
|
-
log.debug("export-hap: battery getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
80812
|
+
log.debug("export-hap: battery getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$13(err) } });
|
|
78806
80813
|
}
|
|
78807
80814
|
const unsubscribes = [];
|
|
78808
80815
|
if (proxy.state.battery) {
|
|
78809
80816
|
const unsub = proxy.state.battery.subscribe((value) => {
|
|
78810
80817
|
if (!value) return;
|
|
78811
|
-
|
|
78812
|
-
if (typeof status.percentage !== "number") return;
|
|
78813
|
-
applyToService(service, status);
|
|
80818
|
+
applyToService(service, value);
|
|
78814
80819
|
});
|
|
78815
80820
|
unsubscribes.push(unsub);
|
|
78816
80821
|
}
|
|
@@ -78821,14 +80826,9 @@ async function buildBattery(bctx) {
|
|
|
78821
80826
|
} };
|
|
78822
80827
|
}
|
|
78823
80828
|
function applyToService(service, status) {
|
|
78824
|
-
|
|
78825
|
-
service.updateCharacteristic(import_dist.Characteristic.BatteryLevel, pct);
|
|
78826
|
-
const chargingState = status.charging === "none" ? import_dist.Characteristic.ChargingState.NOT_CHARGING : import_dist.Characteristic.ChargingState.CHARGING;
|
|
78827
|
-
service.updateCharacteristic(import_dist.Characteristic.ChargingState, chargingState);
|
|
78828
|
-
const lowBattery = pct <= LOW_BATTERY_THRESHOLD_PCT ? import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
78829
|
-
service.updateCharacteristic(import_dist.Characteristic.StatusLowBattery, lowBattery);
|
|
80829
|
+
applyUpdates(service, batteryCharacteristicUpdates(status));
|
|
78830
80830
|
}
|
|
78831
|
-
function errMsg$
|
|
80831
|
+
function errMsg$13(err) {
|
|
78832
80832
|
return err instanceof Error ? err.message : String(err);
|
|
78833
80833
|
}
|
|
78834
80834
|
//#endregion
|
|
@@ -89580,7 +91580,7 @@ var VIDEO_LOOPBACK_RCVBUF_BYTES = 8 * 1024 * 1024;
|
|
|
89580
91580
|
* not burst size.
|
|
89581
91581
|
*/
|
|
89582
91582
|
var AUDIO_LOOPBACK_RCVBUF_BYTES = 1024 * 1024;
|
|
89583
|
-
function errMsg$
|
|
91583
|
+
function errMsg$12(err) {
|
|
89584
91584
|
return err instanceof Error ? err.message : String(err);
|
|
89585
91585
|
}
|
|
89586
91586
|
/**
|
|
@@ -89595,13 +91595,13 @@ function applyReceiveBuffer(socket, requestedBytes) {
|
|
|
89595
91595
|
try {
|
|
89596
91596
|
socket.setRecvBufferSize(requestedBytes);
|
|
89597
91597
|
} catch (err) {
|
|
89598
|
-
error = errMsg$
|
|
91598
|
+
error = errMsg$12(err);
|
|
89599
91599
|
}
|
|
89600
91600
|
let effectiveBytes = null;
|
|
89601
91601
|
try {
|
|
89602
91602
|
effectiveBytes = socket.getRecvBufferSize();
|
|
89603
91603
|
} catch (err) {
|
|
89604
|
-
if (error === null) error = errMsg$
|
|
91604
|
+
if (error === null) error = errMsg$12(err);
|
|
89605
91605
|
}
|
|
89606
91606
|
return {
|
|
89607
91607
|
requestedBytes,
|
|
@@ -89861,20 +91861,20 @@ function buildCameraStreamingDelegate(bctx, advertised) {
|
|
|
89861
91861
|
delegate: {
|
|
89862
91862
|
handleSnapshotRequest(request, callback) {
|
|
89863
91863
|
handleSnapshot(bctx, request).then((buf) => callback(void 0, buf)).catch((err) => {
|
|
89864
|
-
log.warn("export-hap: snapshot failed", { meta: { error: errMsg$
|
|
89865
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91864
|
+
log.warn("export-hap: snapshot failed", { meta: { error: errMsg$11(err) } });
|
|
91865
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89866
91866
|
});
|
|
89867
91867
|
},
|
|
89868
91868
|
prepareStream(request, callback) {
|
|
89869
91869
|
prepareStream(request, sessions, bctx).then((resp) => callback(void 0, resp)).catch((err) => {
|
|
89870
|
-
log.warn("export-hap: prepareStream failed", { meta: { error: errMsg$
|
|
89871
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91870
|
+
log.warn("export-hap: prepareStream failed", { meta: { error: errMsg$11(err) } });
|
|
91871
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89872
91872
|
});
|
|
89873
91873
|
},
|
|
89874
91874
|
handleStreamRequest(request, callback) {
|
|
89875
91875
|
handleStreamRequest(request, sessions, bctx, advertised).then(() => callback()).catch((err) => {
|
|
89876
|
-
log.warn("export-hap: handleStreamRequest failed", { meta: { error: errMsg$
|
|
89877
|
-
callback(err instanceof Error ? err : new Error(errMsg$
|
|
91876
|
+
log.warn("export-hap: handleStreamRequest failed", { meta: { error: errMsg$11(err) } });
|
|
91877
|
+
callback(err instanceof Error ? err : new Error(errMsg$11(err)));
|
|
89878
91878
|
});
|
|
89879
91879
|
}
|
|
89880
91880
|
},
|
|
@@ -89990,7 +91990,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
89990
91990
|
closeSocket(audioUdp);
|
|
89991
91991
|
closeSocket(videoLoopUdp);
|
|
89992
91992
|
closeSocket(audioLoopUdp);
|
|
89993
|
-
throw new Error(`export-hap: outbound SrtpSession init failed: ${errMsg$
|
|
91993
|
+
throw new Error(`export-hap: outbound SrtpSession init failed: ${errMsg$11(err)}`, { cause: err });
|
|
89994
91994
|
}
|
|
89995
91995
|
let upstreamAudioSrtp = null;
|
|
89996
91996
|
try {
|
|
@@ -90004,7 +92004,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90004
92004
|
profile: import_src.ProtectionProfileAes128CmHmacSha1_80
|
|
90005
92005
|
});
|
|
90006
92006
|
} catch (err) {
|
|
90007
|
-
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).warn("export-hap: SrtpSession init failed (upstream audio decrypt disabled)", { meta: { error: errMsg$
|
|
92007
|
+
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).warn("export-hap: SrtpSession init failed (upstream audio decrypt disabled)", { meta: { error: errMsg$11(err) } });
|
|
90008
92008
|
}
|
|
90009
92009
|
const videoSsrc = randomSsrc();
|
|
90010
92010
|
const audioSsrc = randomSsrc();
|
|
@@ -90121,7 +92121,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90121
92121
|
});
|
|
90122
92122
|
audioUdp.on("message", (packet, rinfo) => {
|
|
90123
92123
|
handleIncomingAudioRtp(session, packet, rinfo.address, bctx).catch((err) => {
|
|
90124
|
-
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).debug("export-hap: incoming-audio handler error (dropped)", { meta: { error: errMsg$
|
|
92124
|
+
bctx.ctx.logger.withTags({ deviceId: bctx.numericDeviceId }).debug("export-hap: incoming-audio handler error (dropped)", { meta: { error: errMsg$11(err) } });
|
|
90125
92125
|
});
|
|
90126
92126
|
});
|
|
90127
92127
|
logLoopbackBuffer(tagLog, request.sessionID, "video", videoLoop.buffer);
|
|
@@ -90317,7 +92317,7 @@ function readControllerRtcp(session, leg, packet, log) {
|
|
|
90317
92317
|
} catch (err) {
|
|
90318
92318
|
drop(session, "inbound-rtcp-decrypt-failed");
|
|
90319
92319
|
storeReceiverReports(session, leg, recordUnreadableRtcp(tally));
|
|
90320
|
-
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$
|
|
92320
|
+
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$11(err)}`, log);
|
|
90321
92321
|
return;
|
|
90322
92322
|
}
|
|
90323
92323
|
const outcome = ingestDecryptedRtcp(plaintext, tally);
|
|
@@ -91083,7 +93083,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91083
93083
|
session.upstreamRtpDecryptFailures += 1;
|
|
91084
93084
|
if (session.upstreamRtpDecryptFailures % 100 === 1) log.debug("export-hap: SRTP decrypt failed (rate-limited)", { meta: {
|
|
91085
93085
|
failures: session.upstreamRtpDecryptFailures,
|
|
91086
|
-
error: errMsg$
|
|
93086
|
+
error: errMsg$11(err)
|
|
91087
93087
|
} });
|
|
91088
93088
|
return;
|
|
91089
93089
|
}
|
|
@@ -91097,7 +93097,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91097
93097
|
rtpPayloadType = parsedRtp.header.payloadType;
|
|
91098
93098
|
} catch (err) {
|
|
91099
93099
|
drop(session, "upstream-parse-failed");
|
|
91100
|
-
log.debug("export-hap: RTP parse failed after decrypt", { meta: { error: errMsg$
|
|
93100
|
+
log.debug("export-hap: RTP parse failed after decrypt", { meta: { error: errMsg$11(err) } });
|
|
91101
93101
|
return;
|
|
91102
93102
|
}
|
|
91103
93103
|
const negotiatedAudioPt = session.lastStartParams?.audioPt;
|
|
@@ -91121,7 +93121,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91121
93121
|
if (session.intercomTalkSessionId === null) {
|
|
91122
93122
|
session.intercomTalkSessionId = "";
|
|
91123
93123
|
const opened = await openIntercomTalkSession(bctx).catch((err) => {
|
|
91124
|
-
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$
|
|
93124
|
+
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$11(err) } });
|
|
91125
93125
|
return null;
|
|
91126
93126
|
});
|
|
91127
93127
|
if (opened) {
|
|
@@ -91148,7 +93148,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91148
93148
|
drop(session, "upstream-push-failed");
|
|
91149
93149
|
log.debug("export-hap: intercom.pushTalkAudio failed (will re-open on next frame)", { meta: {
|
|
91150
93150
|
sequenceNumber: session.intercomPcmSequence,
|
|
91151
|
-
error: errMsg$
|
|
93151
|
+
error: errMsg$11(err)
|
|
91152
93152
|
} });
|
|
91153
93153
|
session.intercomTalkSessionId = null;
|
|
91154
93154
|
}
|
|
@@ -91180,7 +93180,7 @@ async function closeIntercomTalkSession(session, bctx) {
|
|
|
91180
93180
|
} catch (err) {
|
|
91181
93181
|
log.debug("export-hap: intercom.endTalkSession failed (continuing)", { meta: {
|
|
91182
93182
|
sessionId,
|
|
91183
|
-
error: errMsg$
|
|
93183
|
+
error: errMsg$11(err)
|
|
91184
93184
|
} });
|
|
91185
93185
|
}
|
|
91186
93186
|
}
|
|
@@ -91205,7 +93205,7 @@ function closeSocket(udp) {
|
|
|
91205
93205
|
function randomSsrc() {
|
|
91206
93206
|
return Math.floor(Math.random() * 2147483646) + 1 | 0;
|
|
91207
93207
|
}
|
|
91208
|
-
function errMsg$
|
|
93208
|
+
function errMsg$11(err) {
|
|
91209
93209
|
return err instanceof Error ? err.message : String(err);
|
|
91210
93210
|
}
|
|
91211
93211
|
//#endregion
|
|
@@ -91289,14 +93289,14 @@ async function buildDoorbell(input) {
|
|
|
91289
93289
|
}
|
|
91290
93290
|
log.info("export-hap: doorbell SINGLE_PRESS pushed to HomeKit", { meta: { ...delivery } });
|
|
91291
93291
|
} catch (err) {
|
|
91292
|
-
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$
|
|
93292
|
+
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$10(err) } });
|
|
91293
93293
|
}
|
|
91294
93294
|
});
|
|
91295
93295
|
return { async dispose() {
|
|
91296
93296
|
unsubscribe();
|
|
91297
93297
|
} };
|
|
91298
93298
|
}
|
|
91299
|
-
function errMsg$
|
|
93299
|
+
function errMsg$10(err) {
|
|
91300
93300
|
return err instanceof Error ? err.message : String(err);
|
|
91301
93301
|
}
|
|
91302
93302
|
//#endregion
|
|
@@ -91336,13 +93336,15 @@ var RESET_DEBOUNCE_MS = 5e3;
|
|
|
91336
93336
|
*/
|
|
91337
93337
|
async function buildMotionSensor(bctx, existing = null) {
|
|
91338
93338
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
91339
|
-
const
|
|
93339
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
93340
|
+
const motionService = existing ?? accessory.addService(import_dist.Service.MotionSensor, label);
|
|
93341
|
+
applyServiceLabel(motionService, label);
|
|
91340
93342
|
motionService.setCharacteristic(import_dist.Characteristic.MotionDetected, false);
|
|
91341
93343
|
try {
|
|
91342
93344
|
const detected = await proxy.motion?.isDetected({});
|
|
91343
93345
|
if (typeof detected === "boolean") motionService.updateCharacteristic(import_dist.Characteristic.MotionDetected, detected);
|
|
91344
93346
|
} catch (err) {
|
|
91345
|
-
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: initial motion hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
93347
|
+
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: initial motion hydrate failed (non-fatal)", { meta: { error: errMsg$9(err) } });
|
|
91346
93348
|
}
|
|
91347
93349
|
let resetTimer = null;
|
|
91348
93350
|
const armReset = () => {
|
|
@@ -91376,82 +93378,10 @@ async function buildMotionSensor(bctx, existing = null) {
|
|
|
91376
93378
|
}
|
|
91377
93379
|
} };
|
|
91378
93380
|
}
|
|
91379
|
-
function errMsg$
|
|
93381
|
+
function errMsg$9(err) {
|
|
91380
93382
|
return err instanceof Error ? err.message : String(err);
|
|
91381
93383
|
}
|
|
91382
93384
|
//#endregion
|
|
91383
|
-
//#region src/mappers/builders/service-label.ts
|
|
91384
|
-
/**
|
|
91385
|
-
* The ONE place a secondary service on the camera accessory gets its label.
|
|
91386
|
-
*
|
|
91387
|
-
* A "secondary service" here is a Switch or Lightbulb published alongside the
|
|
91388
|
-
* camera on the same accessory — the privacy switch, each accessory child
|
|
91389
|
-
* (siren, floodlight), each PTZ action. iOS Home renders these as their own
|
|
91390
|
-
* controls, and the operator has seen them as "Interruttore 1", "Interruttore
|
|
91391
|
-
* 2" through three separate rounds of fixes.
|
|
91392
|
-
*
|
|
91393
|
-
* ## Why `Name` alone cannot rename anything
|
|
91394
|
-
*
|
|
91395
|
-
* Two facts about hap-nodejs 2.1.7, both measured against the installed copy
|
|
91396
|
-
* rather than reasoned about:
|
|
91397
|
-
*
|
|
91398
|
-
* 1. `accessory.addService(Type, displayName, subtype)` ALREADY writes
|
|
91399
|
-
* `displayName` to `Characteristic.Name` (`Service` constructor). So every
|
|
91400
|
-
* round of this bug — including the one that moved the label onto
|
|
91401
|
-
* `ConfiguredName` — shipped with `Name` correctly set. "iOS had no name
|
|
91402
|
-
* to render" was never true.
|
|
91403
|
-
* 2. The mDNS configuration number (`c#`) is a sha1 over
|
|
91404
|
-
* `internalHAPRepresentation(false)`, which OMITS characteristic VALUES.
|
|
91405
|
-
* Changing the string in `Name` therefore does not bump `c#`, a paired
|
|
91406
|
-
* controller gets no signal to re-read `/accessories`, and the name it
|
|
91407
|
-
* cached at first enumeration stands forever.
|
|
91408
|
-
*
|
|
91409
|
-
* `Name` is also declared `pr` only — paired read, no write, no notify. It is
|
|
91410
|
-
* the seed a controller seeds its database from once; it is not a channel.
|
|
91411
|
-
*
|
|
91412
|
-
* ## Why `ConfiguredName`
|
|
91413
|
-
*
|
|
91414
|
-
* `ConfiguredName` (`000000E3`) is declared `pr | pw | ev` — the only name
|
|
91415
|
-
* characteristic a controller may write and may subscribe to. It is what iOS
|
|
91416
|
-
* 16+ reads for a service the user can rename, and adding it CHANGES the
|
|
91417
|
-
* accessory structure, so `c#` does bump and the controller re-reads.
|
|
91418
|
-
*
|
|
91419
|
-
* It was removed once because hap-nodejs logged
|
|
91420
|
-
*
|
|
91421
|
-
* ```
|
|
91422
|
-
* Characteristic not in required or optional characteristic section for
|
|
91423
|
-
* service Switch. Adding anyway.
|
|
91424
|
-
* ```
|
|
91425
|
-
*
|
|
91426
|
-
* That line is a WARNING, not a rejection: `Service.getCharacteristic` calls
|
|
91427
|
-
* `addCharacteristic` unconditionally and only then emits the warning. The
|
|
91428
|
-
* characteristic was always present and always published. hap-nodejs'
|
|
91429
|
-
* per-service optional lists simply predate `ConfiguredName` being valid on
|
|
91430
|
-
* any service.
|
|
91431
|
-
*
|
|
91432
|
-
* Registering it with {@link Service.addOptionalCharacteristic} first takes
|
|
91433
|
-
* the branch above the warning, so the accessory still builds with ZERO
|
|
91434
|
-
* characteristic warnings — which is what `service-naming.spec.ts` asserts.
|
|
91435
|
-
*
|
|
91436
|
-
* ## Scope
|
|
91437
|
-
*
|
|
91438
|
-
* Switch- and Lightbulb-shaped services only. `Service.MotionSensor` on a
|
|
91439
|
-
* camera accessory is not a separately named tile in iOS Home, so giving it a
|
|
91440
|
-
* writable name would be a guess, and this module does not guess.
|
|
91441
|
-
*/
|
|
91442
|
-
/**
|
|
91443
|
-
* Publish `name` as both the immutable `Name` and the controller-visible
|
|
91444
|
-
* `ConfiguredName` of `service`.
|
|
91445
|
-
*
|
|
91446
|
-
* `name` must already be HAP-valid — build it with `service-names.ts`, which
|
|
91447
|
-
* cannot return a string hap-nodejs' `checkName` would warn about.
|
|
91448
|
-
*/
|
|
91449
|
-
function applyServiceLabel(service, name) {
|
|
91450
|
-
service.setCharacteristic(import_dist.Characteristic.Name, name);
|
|
91451
|
-
if (!service.optionalCharacteristics.some((characteristic) => characteristic.UUID === import_dist.Characteristic.ConfiguredName.UUID)) service.addOptionalCharacteristic(import_dist.Characteristic.ConfiguredName);
|
|
91452
|
-
service.setCharacteristic(import_dist.Characteristic.ConfiguredName, name);
|
|
91453
|
-
}
|
|
91454
|
-
//#endregion
|
|
91455
93385
|
//#region src/mappers/builders/privacy-switch.ts
|
|
91456
93386
|
/**
|
|
91457
93387
|
* Privacy-mask switch builder — turns the camstack `privacy-mask` cap's
|
|
@@ -91479,7 +93409,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91479
93409
|
const status = await proxy.privacyMask?.getStatus({});
|
|
91480
93410
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91481
93411
|
} catch (err) {
|
|
91482
|
-
log.debug("export-hap: privacy-mask getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$
|
|
93412
|
+
log.debug("export-hap: privacy-mask getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$8(err) } });
|
|
91483
93413
|
}
|
|
91484
93414
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91485
93415
|
const enabled = value === true;
|
|
@@ -91488,7 +93418,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91488
93418
|
} catch (err) {
|
|
91489
93419
|
log.warn("export-hap: privacy-mask setMask failed", { meta: {
|
|
91490
93420
|
enabled,
|
|
91491
|
-
error: errMsg$
|
|
93421
|
+
error: errMsg$8(err)
|
|
91492
93422
|
} });
|
|
91493
93423
|
}
|
|
91494
93424
|
});
|
|
@@ -91506,7 +93436,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91506
93436
|
} catch {}
|
|
91507
93437
|
} };
|
|
91508
93438
|
}
|
|
91509
|
-
function errMsg$
|
|
93439
|
+
function errMsg$8(err) {
|
|
91510
93440
|
return err instanceof Error ? err.message : String(err);
|
|
91511
93441
|
}
|
|
91512
93442
|
//#endregion
|
|
@@ -91597,7 +93527,7 @@ async function buildPtz(bctx) {
|
|
|
91597
93527
|
} catch (err) {
|
|
91598
93528
|
log.warn("export-hap: ptz.goToPreset failed", { meta: {
|
|
91599
93529
|
presetId: preset.id,
|
|
91600
|
-
error: errMsg$
|
|
93530
|
+
error: errMsg$7(err)
|
|
91601
93531
|
} });
|
|
91602
93532
|
}
|
|
91603
93533
|
armReset(() => service.updateCharacteristic(import_dist.Characteristic.On, false), MOMENTARY_RESET_MS);
|
|
@@ -91615,14 +93545,14 @@ async function buildPtz(bctx) {
|
|
|
91615
93545
|
try {
|
|
91616
93546
|
await proxy.ptz?.stop({});
|
|
91617
93547
|
} catch (err) {
|
|
91618
|
-
log.debug("export-hap: ptz.stop failed (non-fatal)", { meta: { error: errMsg$
|
|
93548
|
+
log.debug("export-hap: ptz.stop failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91619
93549
|
}
|
|
91620
93550
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91621
93551
|
}, options.ptzPulseMs);
|
|
91622
93552
|
} catch (err) {
|
|
91623
93553
|
log.warn("export-hap: ptz.continuousMove failed", { meta: {
|
|
91624
93554
|
dir: dir.label,
|
|
91625
|
-
error: errMsg$
|
|
93555
|
+
error: errMsg$7(err)
|
|
91626
93556
|
} });
|
|
91627
93557
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91628
93558
|
}
|
|
@@ -91645,7 +93575,7 @@ async function readPresets(bctx) {
|
|
|
91645
93575
|
name: p.name
|
|
91646
93576
|
}));
|
|
91647
93577
|
} catch (err) {
|
|
91648
|
-
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$
|
|
93578
|
+
ctx.logger.withTags({ deviceId: numericDeviceId }).debug("export-hap: ptz.getPresets failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91649
93579
|
return [];
|
|
91650
93580
|
}
|
|
91651
93581
|
}
|
|
@@ -91660,7 +93590,7 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91660
93590
|
const status = await proxy.ptzAutotrack.getStatus({});
|
|
91661
93591
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91662
93592
|
} catch (err) {
|
|
91663
|
-
log.debug("export-hap: ptzAutotrack.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
93593
|
+
log.debug("export-hap: ptzAutotrack.getStatus failed (non-fatal)", { meta: { error: errMsg$7(err) } });
|
|
91664
93594
|
}
|
|
91665
93595
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91666
93596
|
const enabled = value === true;
|
|
@@ -91669,13 +93599,13 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91669
93599
|
} catch (err) {
|
|
91670
93600
|
log.warn("export-hap: ptzAutotrack.setEnabled failed", { meta: {
|
|
91671
93601
|
enabled,
|
|
91672
|
-
error: errMsg$
|
|
93602
|
+
error: errMsg$7(err)
|
|
91673
93603
|
} });
|
|
91674
93604
|
}
|
|
91675
93605
|
});
|
|
91676
93606
|
return { async dispose() {} };
|
|
91677
93607
|
}
|
|
91678
|
-
function errMsg$
|
|
93608
|
+
function errMsg$7(err) {
|
|
91679
93609
|
return err instanceof Error ? err.message : String(err);
|
|
91680
93610
|
}
|
|
91681
93611
|
//#endregion
|
|
@@ -92706,13 +94636,13 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92706
94636
|
const switchStatus = await proxy.switch?.getStatus({});
|
|
92707
94637
|
if (switchStatus && typeof switchStatus.on === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, switchStatus.on);
|
|
92708
94638
|
} catch (err) {
|
|
92709
|
-
log.debug("export-hap: child switch.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
94639
|
+
log.debug("export-hap: child switch.getStatus failed (non-fatal)", { meta: { error: errMsg$6(err) } });
|
|
92710
94640
|
}
|
|
92711
94641
|
if (useLightbulb) try {
|
|
92712
94642
|
const status = await proxy.brightness?.getStatus({});
|
|
92713
94643
|
if (status && typeof status.percentage === "number") service.updateCharacteristic(import_dist.Characteristic.Brightness, status.percentage);
|
|
92714
94644
|
} catch (err) {
|
|
92715
|
-
log.debug("export-hap: child brightness.getStatus failed (non-fatal)", { meta: { error: errMsg$
|
|
94645
|
+
log.debug("export-hap: child brightness.getStatus failed (non-fatal)", { meta: { error: errMsg$6(err) } });
|
|
92716
94646
|
}
|
|
92717
94647
|
if (hasSwitch) service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
92718
94648
|
const on = value === true;
|
|
@@ -92721,7 +94651,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92721
94651
|
} catch (err) {
|
|
92722
94652
|
log.warn("export-hap: child switch.setState failed", { meta: {
|
|
92723
94653
|
on,
|
|
92724
|
-
error: errMsg$
|
|
94654
|
+
error: errMsg$6(err)
|
|
92725
94655
|
} });
|
|
92726
94656
|
}
|
|
92727
94657
|
});
|
|
@@ -92733,7 +94663,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92733
94663
|
} catch (err) {
|
|
92734
94664
|
log.warn("export-hap: child brightness.setBrightness failed", { meta: {
|
|
92735
94665
|
percentage,
|
|
92736
|
-
error: errMsg$
|
|
94666
|
+
error: errMsg$6(err)
|
|
92737
94667
|
} });
|
|
92738
94668
|
}
|
|
92739
94669
|
});
|
|
@@ -92756,7 +94686,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92756
94686
|
} catch {}
|
|
92757
94687
|
} };
|
|
92758
94688
|
}
|
|
92759
|
-
function errMsg$
|
|
94689
|
+
function errMsg$6(err) {
|
|
92760
94690
|
return err instanceof Error ? err.message : String(err);
|
|
92761
94691
|
}
|
|
92762
94692
|
//#endregion
|
|
@@ -92783,7 +94713,7 @@ async function buildChildServicesFor(input) {
|
|
|
92783
94713
|
for (const h of handles) try {
|
|
92784
94714
|
await h.dispose();
|
|
92785
94715
|
} catch (err) {
|
|
92786
|
-
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: child service dispose failed (continuing)", { meta: { error: errMsg$
|
|
94716
|
+
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: child service dispose failed (continuing)", { meta: { error: errMsg$5(err) } });
|
|
92787
94717
|
}
|
|
92788
94718
|
} };
|
|
92789
94719
|
}
|
|
@@ -92799,7 +94729,7 @@ async function listChildren(ctx, parentNumericId) {
|
|
|
92799
94729
|
features: Array.isArray(c.features) ? c.features.filter((f) => typeof f === "string") : []
|
|
92800
94730
|
}));
|
|
92801
94731
|
} catch (err) {
|
|
92802
|
-
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: deviceManager.getChildren failed (non-fatal)", { meta: { error: errMsg$
|
|
94732
|
+
ctx.logger.withTags({ deviceId: parentNumericId }).debug("export-hap: deviceManager.getChildren failed (non-fatal)", { meta: { error: errMsg$5(err) } });
|
|
92803
94733
|
return [];
|
|
92804
94734
|
}
|
|
92805
94735
|
}
|
|
@@ -92815,10 +94745,76 @@ function asDeviceType(raw) {
|
|
|
92815
94745
|
for (const value of Object.values(DeviceType)) if (value === lower) return value;
|
|
92816
94746
|
return DeviceType.Generic;
|
|
92817
94747
|
}
|
|
92818
|
-
function errMsg$
|
|
94748
|
+
function errMsg$5(err) {
|
|
92819
94749
|
return err instanceof Error ? err.message : String(err);
|
|
92820
94750
|
}
|
|
92821
94751
|
//#endregion
|
|
94752
|
+
//#region src/mappers/kind.ts
|
|
94753
|
+
/**
|
|
94754
|
+
* Which orchestrator a device gets, which device types the picker offers, and
|
|
94755
|
+
* what a device's HomeKit accessory is called on the wire.
|
|
94756
|
+
*
|
|
94757
|
+
* Separate from `index.ts` (the factory registry) only so the orchestrators can
|
|
94758
|
+
* import the uuid convention without importing the registry that imports them.
|
|
94759
|
+
*/
|
|
94760
|
+
var SUPPORTED_MAPPER_KINDS = ["camera", "generic"];
|
|
94761
|
+
/**
|
|
94762
|
+
* The device types the Export picker offers, and the answer to the cap's
|
|
94763
|
+
* `listSupportedDeviceKinds`.
|
|
94764
|
+
*
|
|
94765
|
+
* A UI FILTER, nothing more. Whether a device actually exports anything is
|
|
94766
|
+
* decided by its capabilities (`rowsForCaps` in the capability table) — the
|
|
94767
|
+
* doctrine the HA exporter writes down, and the thing that confined the
|
|
94768
|
+
* previous exporter to cameras when it was ignored.
|
|
94769
|
+
*
|
|
94770
|
+
* Tier A: the types whose HomeKit services exist today and need no
|
|
94771
|
+
* feature-dependent shape. `cover`, `climate`, `fan`, `valve`, `humidifier`,
|
|
94772
|
+
* `water-heater`, `alarm-panel`, `button` and `media-player` are deliberately
|
|
94773
|
+
* absent — each needs a service whose semantics the table cannot yet honour,
|
|
94774
|
+
* and offering the tab before the mapping exists is how an operator gets an
|
|
94775
|
+
* accessory that pairs and does nothing.
|
|
94776
|
+
*/
|
|
94777
|
+
var HAP_EXPORTABLE_DEVICE_TYPES = [
|
|
94778
|
+
DeviceType.Camera,
|
|
94779
|
+
DeviceType.Light,
|
|
94780
|
+
DeviceType.Switch,
|
|
94781
|
+
DeviceType.Siren,
|
|
94782
|
+
DeviceType.Sensor,
|
|
94783
|
+
DeviceType.Lock,
|
|
94784
|
+
DeviceType.Presence,
|
|
94785
|
+
DeviceType.Generic
|
|
94786
|
+
];
|
|
94787
|
+
/**
|
|
94788
|
+
* Resolve the orchestrator for a device from its TYPE.
|
|
94789
|
+
*
|
|
94790
|
+
* `null` means "no Export tab": the picker must not offer a type no
|
|
94791
|
+
* orchestrator can build. An UNKNOWN type (the device-manager read failed) maps
|
|
94792
|
+
* to `camera` — that is what every entry persisted before this function existed
|
|
94793
|
+
* carries, and a transient API failure must never re-shape an exposed camera.
|
|
94794
|
+
*/
|
|
94795
|
+
function pickMapperKind(deviceType) {
|
|
94796
|
+
if (deviceType === null || deviceType === void 0 || deviceType.length === 0) return "camera";
|
|
94797
|
+
if (deviceType === DeviceType.Camera) return "camera";
|
|
94798
|
+
return HAP_EXPORTABLE_DEVICE_TYPES.find((type) => type === deviceType) === void 0 ? null : "generic";
|
|
94799
|
+
}
|
|
94800
|
+
/**
|
|
94801
|
+
* The deterministic HAP accessory UUID for a device.
|
|
94802
|
+
*
|
|
94803
|
+
* One function, because three call sites depend on the answer agreeing: the
|
|
94804
|
+
* orchestrator that builds the accessory, `unexposeDevice` (which wipes
|
|
94805
|
+
* hap-nodejs' pairing blobs by uuid, on a device whose mapper is already gone)
|
|
94806
|
+
* and the export sync state that records it.
|
|
94807
|
+
*
|
|
94808
|
+
* The camera namespace is FROZEN — every camera paired to date derives its MAC
|
|
94809
|
+
* from `sha256("camstack:hap:" + uuid)` of this exact string, and changing it
|
|
94810
|
+
* would make every paired camera a stranger. Generic devices get their own
|
|
94811
|
+
* namespace so a switch and a camera that happen to share a device id are not
|
|
94812
|
+
* the same HomeKit accessory.
|
|
94813
|
+
*/
|
|
94814
|
+
function accessoryUuidFor(kind, deviceId) {
|
|
94815
|
+
return import_dist.uuid.generate(kind === "camera" ? `camstack:camera:${deviceId}` : `camstack:device:${deviceId}`);
|
|
94816
|
+
}
|
|
94817
|
+
//#endregion
|
|
92822
94818
|
//#region src/mappers/camera-accessory.ts
|
|
92823
94819
|
/**
|
|
92824
94820
|
* Camera accessory orchestrator — given a camstack deviceId, builds one
|
|
@@ -92851,9 +94847,9 @@ async function buildCameraAccessory(input) {
|
|
|
92851
94847
|
const capNames = new Set(proxy.binding?.entries.map((e) => e.capName) ?? []);
|
|
92852
94848
|
const isDoorbell = capNames.has("doorbell");
|
|
92853
94849
|
const category = isDoorbell ? import_dist.Categories.VIDEO_DOORBELL : import_dist.Categories.IP_CAMERA;
|
|
92854
|
-
const accessory = new import_dist.Accessory(displayName,
|
|
94850
|
+
const accessory = new import_dist.Accessory(displayName, accessoryUuidFor("camera", numericId));
|
|
92855
94851
|
accessory.category = category;
|
|
92856
|
-
await populateAccessoryInfo(accessory, proxy, displayName);
|
|
94852
|
+
await populateAccessoryInfo(accessory, proxy, displayName, "Camera");
|
|
92857
94853
|
const bctx = {
|
|
92858
94854
|
ctx,
|
|
92859
94855
|
accessory,
|
|
@@ -92911,55 +94907,462 @@ async function buildCameraAccessory(input) {
|
|
|
92911
94907
|
for (const h of handles) try {
|
|
92912
94908
|
await h.dispose();
|
|
92913
94909
|
} catch (err) {
|
|
92914
|
-
log.debug("export-hap: builder dispose failed (continuing)", { meta: { error: errMsg$
|
|
94910
|
+
log.debug("export-hap: builder dispose failed (continuing)", { meta: { error: errMsg$4(err) } });
|
|
92915
94911
|
}
|
|
92916
94912
|
try {
|
|
92917
94913
|
await streams.dispose();
|
|
92918
94914
|
} catch (err) {
|
|
92919
|
-
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$
|
|
94915
|
+
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$4(err) } });
|
|
92920
94916
|
}
|
|
92921
94917
|
}
|
|
92922
94918
|
};
|
|
92923
94919
|
}
|
|
92924
|
-
|
|
92925
|
-
|
|
92926
|
-
|
|
94920
|
+
function errMsg$4(err) {
|
|
94921
|
+
return err instanceof Error ? err.message : String(err);
|
|
94922
|
+
}
|
|
94923
|
+
//#endregion
|
|
94924
|
+
//#region src/mappers/builders/generic/lock.ts
|
|
94925
|
+
/**
|
|
94926
|
+
* `lock-control` → `Service.LockMechanism`.
|
|
94927
|
+
*
|
|
94928
|
+
* Not a sensor row: HomeKit models a lock as two characteristics, a CURRENT
|
|
94929
|
+
* state the accessory owns and a TARGET state the controller writes, and the
|
|
94930
|
+
* cap's five states do not map onto either one alone.
|
|
94931
|
+
*
|
|
94932
|
+
* ```
|
|
94933
|
+
* cap state LockCurrentState LockTargetState
|
|
94934
|
+
* locked SECURED SECURED
|
|
94935
|
+
* unlocked UNSECURED UNSECURED
|
|
94936
|
+
* locking UNKNOWN SECURED (in flight — do not claim SECURED)
|
|
94937
|
+
* unlocking UNKNOWN UNSECURED
|
|
94938
|
+
* jammed JAMMED unchanged (the controller's intent stands)
|
|
94939
|
+
* ```
|
|
94940
|
+
*
|
|
94941
|
+
* Reporting SECURED while the bolt is still moving is the failure worth naming:
|
|
94942
|
+
* iOS renders the lock as closed, the operator walks away, and the motor stalls
|
|
94943
|
+
* behind them. `UNKNOWN` is the honest answer for an in-flight transition.
|
|
94944
|
+
*
|
|
94945
|
+
* `open` (the cap's third method, for locks with a latch/buzzer) has no HomeKit
|
|
94946
|
+
* counterpart on this service and is deliberately not wired — a Switch that
|
|
94947
|
+
* silently buzzes a door open would be a second knob nobody asked for.
|
|
94948
|
+
*/
|
|
94949
|
+
var SUBTYPE = "lock-control";
|
|
94950
|
+
var readLock = reader(LockControlStatusSchema.pick({ state: true }), (status) => {
|
|
94951
|
+
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;
|
|
94952
|
+
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;
|
|
94953
|
+
return [{
|
|
94954
|
+
characteristic: import_dist.Characteristic.LockCurrentState,
|
|
94955
|
+
value: current
|
|
94956
|
+
}, ...target === null ? [] : [{
|
|
94957
|
+
characteristic: import_dist.Characteristic.LockTargetState,
|
|
94958
|
+
value: target
|
|
94959
|
+
}]];
|
|
94960
|
+
});
|
|
94961
|
+
async function buildLockMechanism(bctx) {
|
|
94962
|
+
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
94963
|
+
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
94964
|
+
const label = hapServiceName([displayName], `Lock ${numericDeviceId}`);
|
|
94965
|
+
const service = accessory.addService(import_dist.Service.LockMechanism, label, SUBTYPE);
|
|
94966
|
+
applyServiceLabel(service, label);
|
|
94967
|
+
const apply = (status, source) => {
|
|
94968
|
+
const updates = readLock(status);
|
|
94969
|
+
if (updates.length === 0) {
|
|
94970
|
+
log.debug("export-hap: lock status did not match the cap schema — no update", { meta: { source } });
|
|
94971
|
+
return;
|
|
94972
|
+
}
|
|
94973
|
+
applyUpdates(service, updates);
|
|
94974
|
+
};
|
|
92927
94975
|
try {
|
|
92928
|
-
const
|
|
92929
|
-
|
|
92930
|
-
|
|
92931
|
-
|
|
92932
|
-
|
|
92933
|
-
|
|
92934
|
-
|
|
92935
|
-
|
|
94976
|
+
const status = await proxy.lockControl?.getStatus({});
|
|
94977
|
+
if (status !== void 0 && status !== null) apply(status, "getStatus");
|
|
94978
|
+
} catch (err) {
|
|
94979
|
+
log.debug("export-hap: lock getStatus hydrate failed (non-fatal)", { meta: { error: errMsg$3(err) } });
|
|
94980
|
+
}
|
|
94981
|
+
service.getCharacteristic(import_dist.Characteristic.LockTargetState).onSet(async (value) => {
|
|
94982
|
+
const secure = value === import_dist.Characteristic.LockTargetState.SECURED;
|
|
94983
|
+
try {
|
|
94984
|
+
if (secure) await proxy.lockControl?.lock({});
|
|
94985
|
+
else await proxy.lockControl?.unlock({});
|
|
94986
|
+
} catch (err) {
|
|
94987
|
+
log.warn("export-hap: lock command failed", { meta: {
|
|
94988
|
+
secure,
|
|
94989
|
+
error: errMsg$3(err)
|
|
94990
|
+
} });
|
|
94991
|
+
}
|
|
94992
|
+
});
|
|
94993
|
+
const unsubscribe = proxy.state.lockControl?.subscribe((value) => {
|
|
94994
|
+
if (value === void 0 || value === null) return;
|
|
94995
|
+
apply(value, "slice");
|
|
94996
|
+
}) ?? null;
|
|
94997
|
+
return { async dispose() {
|
|
94998
|
+
try {
|
|
94999
|
+
unsubscribe?.();
|
|
95000
|
+
} catch {}
|
|
95001
|
+
} };
|
|
92936
95002
|
}
|
|
92937
|
-
function
|
|
92938
|
-
return
|
|
95003
|
+
function errMsg$3(err) {
|
|
95004
|
+
return err instanceof Error ? err.message : String(err);
|
|
95005
|
+
}
|
|
95006
|
+
//#endregion
|
|
95007
|
+
//#region src/mappers/builders/generic/sensor-service.ts
|
|
95008
|
+
async function buildSensorService(input) {
|
|
95009
|
+
const { bctx, spec, name, subtype } = input;
|
|
95010
|
+
const { ctx, accessory, proxy, numericDeviceId } = bctx;
|
|
95011
|
+
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
95012
|
+
const label = hapServiceName([name], spec.label);
|
|
95013
|
+
const service = spec.addService(accessory, label, subtype);
|
|
95014
|
+
applyServiceLabel(service, label);
|
|
95015
|
+
const apply = (status, source) => {
|
|
95016
|
+
const updates = spec.read(status);
|
|
95017
|
+
if (updates.length === 0) {
|
|
95018
|
+
log.debug("export-hap: sensor status did not match the cap schema — no update", { meta: {
|
|
95019
|
+
subtype,
|
|
95020
|
+
source
|
|
95021
|
+
} });
|
|
95022
|
+
return;
|
|
95023
|
+
}
|
|
95024
|
+
applyUpdates(service, updates);
|
|
95025
|
+
};
|
|
95026
|
+
try {
|
|
95027
|
+
const status = await spec.getStatus(proxy);
|
|
95028
|
+
if (status !== void 0 && status !== null) apply(status, "getStatus");
|
|
95029
|
+
} catch (err) {
|
|
95030
|
+
log.debug("export-hap: sensor getStatus hydrate failed (non-fatal)", { meta: {
|
|
95031
|
+
subtype,
|
|
95032
|
+
error: errMsg$2(err)
|
|
95033
|
+
} });
|
|
95034
|
+
}
|
|
95035
|
+
const unsubscribe = spec.subscribe(proxy, (value) => {
|
|
95036
|
+
if (value === void 0 || value === null) return;
|
|
95037
|
+
apply(value, "slice");
|
|
95038
|
+
});
|
|
95039
|
+
return { async dispose() {
|
|
95040
|
+
try {
|
|
95041
|
+
unsubscribe?.();
|
|
95042
|
+
} catch {}
|
|
95043
|
+
} };
|
|
95044
|
+
}
|
|
95045
|
+
function errMsg$2(err) {
|
|
95046
|
+
return err instanceof Error ? err.message : String(err);
|
|
95047
|
+
}
|
|
95048
|
+
//#endregion
|
|
95049
|
+
//#region src/mappers/builders/generic/cap-service-table.ts
|
|
95050
|
+
/**
|
|
95051
|
+
* The capability→HomeKit-service TABLE for non-camera devices.
|
|
95052
|
+
*
|
|
95053
|
+
* This is the whole coverage decision for the generic export path, and it is
|
|
95054
|
+
* deliberately a table rather than a `switch` on `DeviceType`. The doctrine is
|
|
95055
|
+
* the one the Home Assistant exporter already writes down: *coverage is decided
|
|
95056
|
+
* by a device's capabilities, not its type — restricting the picker by type is
|
|
95057
|
+
* what confined the previous exporter to cameras.* A `DeviceType` here is only
|
|
95058
|
+
* ever a UI filter (`HAP_EXPORTABLE_DEVICE_TYPES`) or an icon
|
|
95059
|
+
* (`ACCESSORY_CATEGORY_BY_TYPE`); it never decides whether something exports.
|
|
95060
|
+
*
|
|
95061
|
+
* The shape mirrors `CAP_ENTITY_MAP` in
|
|
95062
|
+
* `packages/addon-provider-homeassistant/src/ha-export/entity-catalog.ts` so
|
|
95063
|
+
* the two can be unified later — one row per capability, keyed by the cap name
|
|
95064
|
+
* exactly as it appears in `proxy.binding.entries[].capName`.
|
|
95065
|
+
*
|
|
95066
|
+
* ## Reading a status
|
|
95067
|
+
*
|
|
95068
|
+
* Every row parses the cap's OWN Zod status schema rather than duck-typing the
|
|
95069
|
+
* payload, and it `.pick()`s only the fields it reads: a provider that omits a
|
|
95070
|
+
* timestamp must not silence a sensor, and a provider that sends the wrong
|
|
95071
|
+
* shape must not write a garbage characteristic. A parse that fails yields no
|
|
95072
|
+
* updates, and the builder that owns the service logs the drop — silence reads
|
|
95073
|
+
* as "the sensor never changed".
|
|
95074
|
+
*
|
|
95075
|
+
* ## Tier
|
|
95076
|
+
*
|
|
95077
|
+
* Tier A only: the caps whose HomeKit service exists today and needs no
|
|
95078
|
+
* feature-dependent shape (`cover` needs `cover-positionable`, `climate-control`
|
|
95079
|
+
* needs the dual-setpoint split, `alarm-panel` must survive a refused `arm`).
|
|
95080
|
+
* Those are a separate task; adding a row here must never mean adding a service
|
|
95081
|
+
* whose semantics this file cannot fully honour.
|
|
95082
|
+
*/
|
|
95083
|
+
/** HomeKit's floor for `CurrentAmbientLightLevel`; 0 lux is out of range. */
|
|
95084
|
+
var MIN_LUX = 1e-4;
|
|
95085
|
+
var SENSOR_SPECS = {
|
|
95086
|
+
contact: {
|
|
95087
|
+
label: "Contact",
|
|
95088
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.ContactSensor, name, subtype),
|
|
95089
|
+
getStatus: (proxy) => proxy.contact?.getStatus({}),
|
|
95090
|
+
subscribe: (proxy, onValue) => proxy.state.contact?.subscribe(onValue) ?? null,
|
|
95091
|
+
read: reader(ContactStatusSchema.pick({ entryOpen: true }), (status) => [{
|
|
95092
|
+
characteristic: import_dist.Characteristic.ContactSensorState,
|
|
95093
|
+
value: status.entryOpen ? import_dist.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : import_dist.Characteristic.ContactSensorState.CONTACT_DETECTED
|
|
95094
|
+
}])
|
|
95095
|
+
},
|
|
95096
|
+
motion: {
|
|
95097
|
+
label: "Motion",
|
|
95098
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.MotionSensor, name, subtype),
|
|
95099
|
+
getStatus: (proxy) => proxy.motion?.getStatus({}),
|
|
95100
|
+
subscribe: (proxy, onValue) => proxy.state.motion?.subscribe(onValue) ?? null,
|
|
95101
|
+
read: reader(MotionStatusSchema.pick({ detected: true }), (status) => [{
|
|
95102
|
+
characteristic: import_dist.Characteristic.MotionDetected,
|
|
95103
|
+
value: status.detected
|
|
95104
|
+
}])
|
|
95105
|
+
},
|
|
95106
|
+
presence: {
|
|
95107
|
+
label: "Presence",
|
|
95108
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.OccupancySensor, name, subtype),
|
|
95109
|
+
getStatus: (proxy) => proxy.presence?.getStatus({}),
|
|
95110
|
+
subscribe: (proxy, onValue) => proxy.state.presence?.subscribe(onValue) ?? null,
|
|
95111
|
+
read: reader(PresenceStatusSchema.pick({ state: true }), (status) => [{
|
|
95112
|
+
characteristic: import_dist.Characteristic.OccupancyDetected,
|
|
95113
|
+
value: status.state === "home" ? import_dist.Characteristic.OccupancyDetected.OCCUPANCY_DETECTED : import_dist.Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED
|
|
95114
|
+
}])
|
|
95115
|
+
},
|
|
95116
|
+
smoke: {
|
|
95117
|
+
label: "Smoke",
|
|
95118
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.SmokeSensor, name, subtype),
|
|
95119
|
+
getStatus: (proxy) => proxy.smoke?.getStatus({}),
|
|
95120
|
+
subscribe: (proxy, onValue) => proxy.state.smoke?.subscribe(onValue) ?? null,
|
|
95121
|
+
read: reader(SmokeStatusSchema.pick({ detected: true }), (status) => [{
|
|
95122
|
+
characteristic: import_dist.Characteristic.SmokeDetected,
|
|
95123
|
+
value: status.detected ? import_dist.Characteristic.SmokeDetected.SMOKE_DETECTED : import_dist.Characteristic.SmokeDetected.SMOKE_NOT_DETECTED
|
|
95124
|
+
}])
|
|
95125
|
+
},
|
|
95126
|
+
"carbon-monoxide": {
|
|
95127
|
+
label: "Carbon monoxide",
|
|
95128
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.CarbonMonoxideSensor, name, subtype),
|
|
95129
|
+
getStatus: (proxy) => proxy.carbonMonoxide?.getStatus({}),
|
|
95130
|
+
subscribe: (proxy, onValue) => proxy.state.carbonMonoxide?.subscribe(onValue) ?? null,
|
|
95131
|
+
read: reader(CarbonMonoxideStatusSchema.pick({ detected: true }), (status) => [{
|
|
95132
|
+
characteristic: import_dist.Characteristic.CarbonMonoxideDetected,
|
|
95133
|
+
value: status.detected ? import_dist.Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL : import_dist.Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL
|
|
95134
|
+
}])
|
|
95135
|
+
},
|
|
95136
|
+
flood: {
|
|
95137
|
+
label: "Leak",
|
|
95138
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.LeakSensor, name, subtype),
|
|
95139
|
+
getStatus: (proxy) => proxy.flood?.getStatus({}),
|
|
95140
|
+
subscribe: (proxy, onValue) => proxy.state.flood?.subscribe(onValue) ?? null,
|
|
95141
|
+
read: reader(FloodStatusSchema.pick({ flooded: true }), (status) => [{
|
|
95142
|
+
characteristic: import_dist.Characteristic.LeakDetected,
|
|
95143
|
+
value: status.flooded ? import_dist.Characteristic.LeakDetected.LEAK_DETECTED : import_dist.Characteristic.LeakDetected.LEAK_NOT_DETECTED
|
|
95144
|
+
}])
|
|
95145
|
+
},
|
|
95146
|
+
"temperature-sensor": {
|
|
95147
|
+
label: "Temperature",
|
|
95148
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.TemperatureSensor, name, subtype),
|
|
95149
|
+
getStatus: (proxy) => proxy.temperatureSensor?.getStatus({}),
|
|
95150
|
+
subscribe: (proxy, onValue) => proxy.state.temperatureSensor?.subscribe(onValue) ?? null,
|
|
95151
|
+
read: reader(TemperatureSensorStatusSchema.pick({ celsius: true }), (status) => [{
|
|
95152
|
+
characteristic: import_dist.Characteristic.CurrentTemperature,
|
|
95153
|
+
value: status.celsius
|
|
95154
|
+
}])
|
|
95155
|
+
},
|
|
95156
|
+
"humidity-sensor": {
|
|
95157
|
+
label: "Humidity",
|
|
95158
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.HumiditySensor, name, subtype),
|
|
95159
|
+
getStatus: (proxy) => proxy.humiditySensor?.getStatus({}),
|
|
95160
|
+
subscribe: (proxy, onValue) => proxy.state.humiditySensor?.subscribe(onValue) ?? null,
|
|
95161
|
+
read: reader(HumiditySensorStatusSchema.pick({ percent: true }), (status) => [{
|
|
95162
|
+
characteristic: import_dist.Characteristic.CurrentRelativeHumidity,
|
|
95163
|
+
value: status.percent
|
|
95164
|
+
}])
|
|
95165
|
+
},
|
|
95166
|
+
"ambient-light-sensor": {
|
|
95167
|
+
label: "Light level",
|
|
95168
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.LightSensor, name, subtype),
|
|
95169
|
+
getStatus: (proxy) => proxy.ambientLightSensor?.getStatus({}),
|
|
95170
|
+
subscribe: (proxy, onValue) => proxy.state.ambientLightSensor?.subscribe(onValue) ?? null,
|
|
95171
|
+
read: reader(AmbientLightSensorStatusSchema.pick({ lux: true }), (status) => [{
|
|
95172
|
+
characteristic: import_dist.Characteristic.CurrentAmbientLightLevel,
|
|
95173
|
+
value: Math.max(MIN_LUX, status.lux)
|
|
95174
|
+
}])
|
|
95175
|
+
}
|
|
95176
|
+
};
|
|
95177
|
+
/**
|
|
95178
|
+
* The `battery` row reads like a sensor but writes three characteristics from
|
|
95179
|
+
* one status, and the camera path publishes the SAME service from the same cap.
|
|
95180
|
+
* The derivation therefore lives once, in `battery.ts`, and both callers read
|
|
95181
|
+
* it from there.
|
|
95182
|
+
*/
|
|
95183
|
+
var BATTERY_SPEC = {
|
|
95184
|
+
label: "Battery",
|
|
95185
|
+
addService: (accessory, name, subtype) => accessory.addService(import_dist.Service.Battery, name, subtype),
|
|
95186
|
+
getStatus: (proxy) => proxy.battery?.getStatus({}),
|
|
95187
|
+
subscribe: (proxy, onValue) => proxy.state.battery?.subscribe(onValue) ?? null,
|
|
95188
|
+
read: batteryCharacteristicUpdates
|
|
95189
|
+
};
|
|
95190
|
+
function sensorRow(capName, spec) {
|
|
95191
|
+
return {
|
|
95192
|
+
caps: [capName],
|
|
95193
|
+
label: spec.label,
|
|
95194
|
+
build: ({ bctx, name }) => buildSensorService({
|
|
95195
|
+
bctx,
|
|
95196
|
+
spec,
|
|
95197
|
+
name,
|
|
95198
|
+
subtype: capName
|
|
95199
|
+
})
|
|
95200
|
+
};
|
|
95201
|
+
}
|
|
95202
|
+
/**
|
|
95203
|
+
* THE table. Order matters only for one thing: the first row that matches a
|
|
95204
|
+
* device decides the accessory's category when the device's own type does not.
|
|
95205
|
+
*/
|
|
95206
|
+
var HAP_CAP_SERVICES = [
|
|
95207
|
+
{
|
|
95208
|
+
caps: ["switch", "brightness"],
|
|
95209
|
+
label: "Power",
|
|
95210
|
+
build: ({ bctx, name, deviceType }) => buildChildSwitch({
|
|
95211
|
+
...bctx,
|
|
95212
|
+
displayName: name
|
|
95213
|
+
}, "switch", deviceType)
|
|
95214
|
+
},
|
|
95215
|
+
{
|
|
95216
|
+
caps: ["lock-control"],
|
|
95217
|
+
label: "Lock",
|
|
95218
|
+
build: ({ bctx, name }) => buildLockMechanism({
|
|
95219
|
+
...bctx,
|
|
95220
|
+
displayName: name
|
|
95221
|
+
})
|
|
95222
|
+
},
|
|
95223
|
+
{
|
|
95224
|
+
caps: ["battery"],
|
|
95225
|
+
label: BATTERY_SPEC.label,
|
|
95226
|
+
build: ({ bctx, name }) => buildSensorService({
|
|
95227
|
+
bctx,
|
|
95228
|
+
spec: BATTERY_SPEC,
|
|
95229
|
+
name,
|
|
95230
|
+
subtype: "battery"
|
|
95231
|
+
})
|
|
95232
|
+
},
|
|
95233
|
+
...Object.entries(SENSOR_SPECS).map(([capName, spec]) => sensorRow(capName, spec))
|
|
95234
|
+
];
|
|
95235
|
+
/**
|
|
95236
|
+
* The rows a device's bound capabilities select, in table order.
|
|
95237
|
+
*
|
|
95238
|
+
* A row matches when ANY of its caps is bound: a lamp with `switch` but no
|
|
95239
|
+
* `brightness` is still a Lightbulb-shaped row, and the builder degrades on its
|
|
95240
|
+
* own.
|
|
95241
|
+
*/
|
|
95242
|
+
function rowsForCaps(capNames) {
|
|
95243
|
+
return HAP_CAP_SERVICES.filter((row) => row.caps.some((cap) => capNames.has(cap)));
|
|
95244
|
+
}
|
|
95245
|
+
/**
|
|
95246
|
+
* The HomeKit accessory category per camstack device type — the icon iOS Home
|
|
95247
|
+
* draws and nothing else. Absent = `OTHER`, which is a valid accessory that
|
|
95248
|
+
* simply gets the generic tile.
|
|
95249
|
+
*
|
|
95250
|
+
* This is NOT a coverage decision. `HAP_EXPORTABLE_DEVICE_TYPES` (in
|
|
95251
|
+
* `mappers/index.ts`) filters the picker; whether a device exports anything is
|
|
95252
|
+
* decided by `rowsForCaps`.
|
|
95253
|
+
*/
|
|
95254
|
+
var ACCESSORY_CATEGORY_BY_TYPE = {
|
|
95255
|
+
[DeviceType.Switch]: import_dist.Categories.SWITCH,
|
|
95256
|
+
[DeviceType.Light]: import_dist.Categories.LIGHTBULB,
|
|
95257
|
+
[DeviceType.Siren]: import_dist.Categories.SWITCH,
|
|
95258
|
+
[DeviceType.Lock]: import_dist.Categories.DOOR_LOCK,
|
|
95259
|
+
[DeviceType.Sensor]: import_dist.Categories.SENSOR,
|
|
95260
|
+
[DeviceType.Presence]: import_dist.Categories.SENSOR
|
|
95261
|
+
};
|
|
95262
|
+
function accessoryCategoryFor(deviceType) {
|
|
95263
|
+
return ACCESSORY_CATEGORY_BY_TYPE[deviceType] ?? import_dist.Categories.OTHER;
|
|
95264
|
+
}
|
|
95265
|
+
//#endregion
|
|
95266
|
+
//#region src/mappers/generic-accessory.ts
|
|
95267
|
+
/**
|
|
95268
|
+
* Generic accessory orchestrator — every camstack device that is NOT a camera.
|
|
95269
|
+
*
|
|
95270
|
+
* Same three steps as the camera orchestrator, and deliberately nothing more:
|
|
95271
|
+
* 1. resolve the typed `DeviceProxy`,
|
|
95272
|
+
* 2. select rows from the capability→service table with the device's BOUND
|
|
95273
|
+
* capabilities (never its type — see `builders/generic/cap-service-table.ts`),
|
|
95274
|
+
* 3. let each row add its service to one `Accessory`.
|
|
95275
|
+
*
|
|
95276
|
+
* It publishes standalone, like the camera path: one accessory, one mDNS
|
|
95277
|
+
* advertisement, one pairing, the shared setup code. This addon is not a
|
|
95278
|
+
* bridge — that decision predates this file and is not re-litigated here.
|
|
95279
|
+
*
|
|
95280
|
+
* ## It REFUSES rather than publishing an empty tile
|
|
95281
|
+
*
|
|
95282
|
+
* A device whose capabilities select no row throws. The alternative is an
|
|
95283
|
+
* accessory carrying nothing but `AccessoryInformation`: it pairs, it appears
|
|
95284
|
+
* in the Home app, it does nothing, and the operator has no way to tell that
|
|
95285
|
+
* from a broken integration. The Export tab is gated on the same question
|
|
95286
|
+
* (`hap-export.addon.ts`), so reaching this throw means the device changed
|
|
95287
|
+
* shape between the tab rendering and the toggle landing.
|
|
95288
|
+
*/
|
|
95289
|
+
async function buildGenericAccessory(input) {
|
|
95290
|
+
const { ctx, deviceId, displayName, options } = input;
|
|
95291
|
+
const numericId = Number.parseInt(deviceId, 10);
|
|
95292
|
+
if (!Number.isFinite(numericId)) throw new Error(`export-hap: cannot map device '${deviceId}' — id is not numeric`);
|
|
95293
|
+
const log = ctx.logger.withTags({ deviceId: numericId });
|
|
95294
|
+
const proxy = await ctx.fetchDevice(numericId);
|
|
95295
|
+
const capNames = new Set(proxy.binding?.entries.map((e) => e.capName) ?? []);
|
|
95296
|
+
const rows = rowsForCaps(capNames);
|
|
95297
|
+
if (rows.length === 0) throw new Error(`export-hap: device ${numericId} carries no capability HomeKit can export (bound: ${[...capNames].toSorted().join(", ") || "none"})`);
|
|
95298
|
+
const deviceType = await resolveDeviceType(proxy);
|
|
95299
|
+
const accessory = new import_dist.Accessory(displayName, accessoryUuidFor("generic", numericId));
|
|
95300
|
+
accessory.category = accessoryCategoryFor(deviceType);
|
|
95301
|
+
await populateAccessoryInfo(accessory, proxy, displayName, "Accessory");
|
|
95302
|
+
const bctx = {
|
|
95303
|
+
ctx,
|
|
95304
|
+
accessory,
|
|
95305
|
+
proxy,
|
|
95306
|
+
numericDeviceId: numericId,
|
|
95307
|
+
displayName,
|
|
95308
|
+
options
|
|
95309
|
+
};
|
|
95310
|
+
const handles = [];
|
|
95311
|
+
for (const row of rows) {
|
|
95312
|
+
const name = rows.length === 1 ? displayName : row.label;
|
|
95313
|
+
handles.push(await row.build({
|
|
95314
|
+
bctx,
|
|
95315
|
+
name,
|
|
95316
|
+
deviceType
|
|
95317
|
+
}));
|
|
95318
|
+
}
|
|
95319
|
+
log.info("export-hap: built generic accessory", { meta: {
|
|
95320
|
+
deviceType,
|
|
95321
|
+
services: rows.map((row) => row.caps[0]).join(",")
|
|
95322
|
+
} });
|
|
95323
|
+
return {
|
|
95324
|
+
accessory,
|
|
95325
|
+
accessories: [accessory],
|
|
95326
|
+
async dispose() {
|
|
95327
|
+
for (const handle of handles) try {
|
|
95328
|
+
await handle.dispose();
|
|
95329
|
+
} catch (err) {
|
|
95330
|
+
log.debug("export-hap: generic builder dispose failed (continuing)", { meta: { error: errMsg$1(err) } });
|
|
95331
|
+
}
|
|
95332
|
+
}
|
|
95333
|
+
};
|
|
95334
|
+
}
|
|
95335
|
+
/**
|
|
95336
|
+
* The device's own type, or `Generic`.
|
|
95337
|
+
*
|
|
95338
|
+
* Used ONLY where one capability has two HomeKit shapes — a siren's
|
|
95339
|
+
* `brightness` is alarm volume, not luminosity — and for the accessory's icon.
|
|
95340
|
+
* `Generic` is the safe reading: it is the shape `child-switch.ts` already
|
|
95341
|
+
* treats as "a lamp if it dims, a switch otherwise".
|
|
95342
|
+
*/
|
|
95343
|
+
async function resolveDeviceType(proxy) {
|
|
95344
|
+
try {
|
|
95345
|
+
const device = await proxy.deviceManager?.getDevice({});
|
|
95346
|
+
const raw = typeof device?.type === "string" ? device.type.toLowerCase() : "";
|
|
95347
|
+
return Object.values(DeviceType).find((value) => value === raw) ?? DeviceType.Generic;
|
|
95348
|
+
} catch {
|
|
95349
|
+
return DeviceType.Generic;
|
|
95350
|
+
}
|
|
92939
95351
|
}
|
|
92940
95352
|
function errMsg$1(err) {
|
|
92941
95353
|
return err instanceof Error ? err.message : String(err);
|
|
92942
95354
|
}
|
|
92943
95355
|
//#endregion
|
|
92944
95356
|
//#region src/mappers/index.ts
|
|
92945
|
-
var
|
|
92946
|
-
|
|
95357
|
+
var REGISTRY = {
|
|
95358
|
+
camera: buildCameraAccessory,
|
|
95359
|
+
generic: buildGenericAccessory
|
|
95360
|
+
};
|
|
92947
95361
|
function getMapperFactory(kind) {
|
|
92948
95362
|
const factory = REGISTRY[kind];
|
|
92949
95363
|
if (!factory) throw new Error(`export-hap: no mapper registered for kind '${kind}'`);
|
|
92950
95364
|
return factory;
|
|
92951
95365
|
}
|
|
92952
|
-
/**
|
|
92953
|
-
* Resolve the best-fit mapper kind. With Round 2, the operator just
|
|
92954
|
-
* picks a camera and the orchestrator does the rest — the single
|
|
92955
|
-
* `camera` kind is returned unconditionally. We keep the function
|
|
92956
|
-
* signature for backwards-compat with the addon's existing
|
|
92957
|
-
* `exposeDevice` flow and to leave room for future device types (NVR,
|
|
92958
|
-
* climate sensor, ...).
|
|
92959
|
-
*/
|
|
92960
|
-
function pickMapperKind(_capabilities) {
|
|
92961
|
-
return "camera";
|
|
92962
|
-
}
|
|
92963
95366
|
//#endregion
|
|
92964
95367
|
//#region src/mappers/builders/stream-hwaccel-memo.ts
|
|
92965
95368
|
/**
|
|
@@ -93097,8 +95500,8 @@ function syncStateToJson(map) {
|
|
|
93097
95500
|
//#endregion
|
|
93098
95501
|
//#region src/hap-export.addon.ts
|
|
93099
95502
|
/**
|
|
93100
|
-
* HomeKit (HAP) export addon — publishes
|
|
93101
|
-
*
|
|
95503
|
+
* HomeKit (HAP) export addon — publishes selected camstack devices as
|
|
95504
|
+
* standalone HomeKit accessories.
|
|
93102
95505
|
*
|
|
93103
95506
|
* Operator flow:
|
|
93104
95507
|
* 1. Install + enable the addon (hub-only, group `export-hap`).
|
|
@@ -93109,10 +95512,10 @@ function syncStateToJson(map) {
|
|
|
93109
95512
|
* 3. The setup URI (`X-HM://…`) is logged AND surfaced via the
|
|
93110
95513
|
* `getStatus` cap method so the wizard UI can render the QR.
|
|
93111
95514
|
* 4. Operator opens iOS Home → + → scan QR → enter pincode.
|
|
93112
|
-
* 5. Operator hits "Expose to HomeKit" on individual camstack
|
|
93113
|
-
*
|
|
93114
|
-
*
|
|
93115
|
-
*
|
|
95515
|
+
* 5. Operator hits "Expose to HomeKit" on individual camstack devices.
|
|
95516
|
+
* Each exposed device is published as its OWN accessory with its own
|
|
95517
|
+
* mDNS advertisement, sharing one setup code — cameras cannot be
|
|
95518
|
+
* bridged, so nothing is.
|
|
93116
95519
|
*
|
|
93117
95520
|
* Exception — the hap-nodejs library's own `HAPStorage` lives under
|
|
93118
95521
|
* `ctx.dataDir/hap-store/`. That directory is library-internal: the
|
|
@@ -93121,23 +95524,20 @@ function syncStateToJson(map) {
|
|
|
93121
95524
|
* I/O for addon-owned data") explicitly scopes itself to OUR own data;
|
|
93122
95525
|
* library-managed blobs are out of scope.
|
|
93123
95526
|
*
|
|
93124
|
-
*
|
|
93125
|
-
*
|
|
93126
|
-
*
|
|
93127
|
-
*
|
|
93128
|
-
*
|
|
93129
|
-
*
|
|
93130
|
-
*
|
|
93131
|
-
*
|
|
93132
|
-
*
|
|
93133
|
-
*
|
|
93134
|
-
*
|
|
93135
|
-
*
|
|
93136
|
-
*
|
|
93137
|
-
*
|
|
93138
|
-
* What's deferred to Round 3+: cam→browser audio Opus transcode,
|
|
93139
|
-
* intercom upload bridge, HomeKit Secure Video, recording, native
|
|
93140
|
-
* H.264 stream tap (currently uses RTSP + ffmpeg copy).
|
|
95527
|
+
* ## Two accessory shapes, one rule about coverage
|
|
95528
|
+
*
|
|
95529
|
+
* `exposeDevice({deviceId})` resolves the device's TYPE to a mapper kind
|
|
95530
|
+
* (`mappers/kind.ts`) and hands off:
|
|
95531
|
+
*
|
|
95532
|
+
* - a camera → `mappers/camera-accessory.ts`: streams, HKSV recording,
|
|
95533
|
+
* doorbell, motion, intercom, PTZ, battery, privacy and every accessory
|
|
95534
|
+
* child, all as services on one accessory;
|
|
95535
|
+
* - anything else → `mappers/generic-accessory.ts`, driven by the
|
|
95536
|
+
* capability→service table in `mappers/builders/generic/`.
|
|
95537
|
+
*
|
|
95538
|
+
* The type only picks the SHAPE. What a device publishes is decided by the
|
|
95539
|
+
* capabilities bound to it — restricting coverage by type is precisely what
|
|
95540
|
+
* confined this exporter to cameras for its first three rounds.
|
|
93141
95541
|
*/
|
|
93142
95542
|
var DEFAULT_DEVICE_SETTINGS = {
|
|
93143
95543
|
streamPreference: "auto",
|
|
@@ -93180,7 +95580,6 @@ var DEFAULT_CONFIG = {
|
|
|
93180
95580
|
fixedPin: "",
|
|
93181
95581
|
interfaceName: "",
|
|
93182
95582
|
ptzPulseMs: 400,
|
|
93183
|
-
hksvPreview: false,
|
|
93184
95583
|
identity: {
|
|
93185
95584
|
username: "",
|
|
93186
95585
|
pincode: "",
|
|
@@ -93296,7 +95695,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93296
95695
|
...setup ? { setup } : {}
|
|
93297
95696
|
};
|
|
93298
95697
|
},
|
|
93299
|
-
listSupportedDeviceKinds: async () => [...
|
|
95698
|
+
listSupportedDeviceKinds: async () => [...HAP_EXPORTABLE_DEVICE_TYPES],
|
|
93300
95699
|
listExposedDevices: async () => Array.from(this.exposed.entries()).map(([deviceId, m]) => {
|
|
93301
95700
|
const entry = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93302
95701
|
return {
|
|
@@ -93381,9 +95780,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93381
95780
|
log.debug("export-hap: device already exposed — refreshing capabilities");
|
|
93382
95781
|
await this.detachMapper(deviceId);
|
|
93383
95782
|
}
|
|
93384
|
-
const
|
|
93385
|
-
|
|
93386
|
-
|
|
95783
|
+
const summary = await this.fetchDeviceSummary(numericId);
|
|
95784
|
+
const mapperKind = pickMapperKind(summary?.type);
|
|
95785
|
+
if (!mapperKind) throw new Error(`export-hap: device ${numericId} has type '${summary?.type ?? "unknown"}', which HomeKit export does not support`);
|
|
95786
|
+
const displayName = summary?.name ?? `Device ${deviceId}`;
|
|
93387
95787
|
const previous = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93388
95788
|
const baseEntry = carryForward({
|
|
93389
95789
|
deviceId,
|
|
@@ -93413,10 +95813,11 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93413
95813
|
async unexposeDevice(deviceId, options = {}) {
|
|
93414
95814
|
const numericId = Number.parseInt(deviceId, 10);
|
|
93415
95815
|
const log = this.ctx.logger.withTags({ deviceId: numericId });
|
|
95816
|
+
const mapperKind = this.findEntry(numericId)?.mapperKind ?? "camera";
|
|
93416
95817
|
await this.detachMapper(deviceId);
|
|
93417
95818
|
const next = this.config.exposed.filter((e) => e.deviceId !== deviceId);
|
|
93418
95819
|
if (next.length !== this.config.exposed.length) await this.updateGlobalSettings({ exposed: next });
|
|
93419
|
-
if (options.clearPairing !== false) clearPairingFiles(
|
|
95820
|
+
if (options.clearPairing !== false) clearPairingFiles(accessoryUuidFor(mapperKind, numericId), this.ctx.logger);
|
|
93420
95821
|
await this.forgetFingerprint(numericId);
|
|
93421
95822
|
log.info("export-hap: unexposed device");
|
|
93422
95823
|
}
|
|
@@ -93466,10 +95867,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93466
95867
|
/** Pending rebuild timers per deviceId — used to debounce. Cleared
|
|
93467
95868
|
* on detach so stale timers can't republish a removed accessory. */
|
|
93468
95869
|
pendingRebuildTimers = /* @__PURE__ */ new Map();
|
|
93469
|
-
/** Deterministic HAP accessory UUID for a device
|
|
93470
|
-
*
|
|
95870
|
+
/** Deterministic HAP accessory UUID for a device — the same one the
|
|
95871
|
+
* orchestrator built it with (`mappers/kind.ts`). */
|
|
93471
95872
|
hapAccessoryUuid(deviceId) {
|
|
93472
|
-
return
|
|
95873
|
+
return accessoryUuidFor(this.findEntry(deviceId)?.mapperKind ?? "camera", deviceId);
|
|
93473
95874
|
}
|
|
93474
95875
|
/**
|
|
93475
95876
|
* Export fingerprint of a device from its PERSISTED features + type
|
|
@@ -93628,31 +96029,79 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93628
96029
|
log.warn("export-hap: reconcile rebuild failed", { meta: { error: errMsg(err) } });
|
|
93629
96030
|
}
|
|
93630
96031
|
}
|
|
93631
|
-
|
|
93632
|
-
|
|
93633
|
-
|
|
96032
|
+
/**
|
|
96033
|
+
* The device's name and type, or `null` when the registry cannot answer.
|
|
96034
|
+
*
|
|
96035
|
+
* `null` is NOT "no such device" — it also covers a transient API failure,
|
|
96036
|
+
* and every caller treats it as "keep doing what was already being done"
|
|
96037
|
+
* rather than re-shaping an exposed accessory on incomplete information.
|
|
96038
|
+
*/
|
|
96039
|
+
async fetchDeviceSummary(deviceId) {
|
|
96040
|
+
if (!Number.isFinite(deviceId)) return null;
|
|
96041
|
+
try {
|
|
96042
|
+
const device = await this.ctx.api.deviceManager?.getDevice.query({ deviceId });
|
|
96043
|
+
if (!device) return null;
|
|
96044
|
+
return {
|
|
96045
|
+
name: device.name,
|
|
96046
|
+
type: device.type
|
|
96047
|
+
};
|
|
96048
|
+
} catch (err) {
|
|
96049
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: deviceManager.getDevice failed", { meta: { error: errMsg(err) } });
|
|
96050
|
+
return null;
|
|
96051
|
+
}
|
|
96052
|
+
}
|
|
96053
|
+
/**
|
|
96054
|
+
* Which accessory shape this device would get, or `null` for "no Export tab".
|
|
96055
|
+
*
|
|
96056
|
+
* Two questions, in this order, because they fail differently:
|
|
96057
|
+
* 1. the TYPE — a type with no orchestrator is refused outright;
|
|
96058
|
+
* 2. for a non-camera, the CAPABILITIES — a device carrying nothing the
|
|
96059
|
+
* table maps would publish an accessory that pairs and does nothing, and
|
|
96060
|
+
* the operator cannot tell that from a broken integration.
|
|
96061
|
+
*
|
|
96062
|
+
* A camera skips (2): the camera orchestrator has always published on type
|
|
96063
|
+
* alone, and adding a cap gate here would be a new way for an existing camera
|
|
96064
|
+
* to lose its Export tab. A registry that cannot answer at all also resolves
|
|
96065
|
+
* to `camera`, which is what every persisted entry predating this method
|
|
96066
|
+
* carries.
|
|
96067
|
+
*/
|
|
96068
|
+
async resolveExportKind(deviceId) {
|
|
96069
|
+
const summary = await this.fetchDeviceSummary(deviceId);
|
|
96070
|
+
const kind = pickMapperKind(summary?.type);
|
|
96071
|
+
if (kind === null) return null;
|
|
96072
|
+
if (kind === "camera") return "camera";
|
|
93634
96073
|
try {
|
|
93635
|
-
const
|
|
93636
|
-
|
|
96074
|
+
const proxy = await this.ctx.fetchDevice(deviceId);
|
|
96075
|
+
const capNames = new Set(proxy.binding?.entries.map((entry) => entry.capName) ?? []);
|
|
96076
|
+
if (rowsForCaps(capNames).length > 0) return "generic";
|
|
96077
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: no Export tab — no mapped caps", { meta: {
|
|
96078
|
+
type: summary?.type,
|
|
96079
|
+
caps: [...capNames].toSorted().join(",")
|
|
96080
|
+
} });
|
|
96081
|
+
return null;
|
|
93637
96082
|
} catch (err) {
|
|
93638
|
-
this.ctx.logger.withTags({ deviceId
|
|
96083
|
+
this.ctx.logger.withTags({ deviceId }).debug("export-hap: no Export tab — capability read failed", { meta: {
|
|
96084
|
+
type: summary?.type,
|
|
96085
|
+
error: errMsg(err)
|
|
96086
|
+
} });
|
|
96087
|
+
return null;
|
|
93639
96088
|
}
|
|
93640
|
-
return `Device ${deviceId}`;
|
|
93641
96089
|
}
|
|
93642
96090
|
globalSettingsSchema() {
|
|
93643
96091
|
return this.schema({ sections: [{
|
|
93644
96092
|
id: "export-hap",
|
|
93645
96093
|
title: "HomeKit Export",
|
|
93646
|
-
description: "Publishes
|
|
96094
|
+
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.",
|
|
93647
96095
|
columns: 1,
|
|
93648
96096
|
fields: [
|
|
93649
96097
|
this.field({
|
|
93650
96098
|
type: "text",
|
|
93651
96099
|
key: "bridgeName",
|
|
93652
|
-
label: "
|
|
93653
|
-
description: "
|
|
96100
|
+
label: "Installation name",
|
|
96101
|
+
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.",
|
|
93654
96102
|
default: DEFAULT_CONFIG.bridgeName,
|
|
93655
|
-
requiresRestart: true
|
|
96103
|
+
requiresRestart: true,
|
|
96104
|
+
placement: { tab: "advanced" }
|
|
93656
96105
|
}),
|
|
93657
96106
|
this.field({
|
|
93658
96107
|
type: "number",
|
|
@@ -93688,21 +96137,13 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93688
96137
|
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.",
|
|
93689
96138
|
default: DEFAULT_CONFIG.ptzPulseMs,
|
|
93690
96139
|
placement: { tab: "advanced" }
|
|
93691
|
-
}),
|
|
93692
|
-
this.field({
|
|
93693
|
-
type: "boolean",
|
|
93694
|
-
key: "hksvPreview",
|
|
93695
|
-
label: "HKSV Developer Preview (experimental)",
|
|
93696
|
-
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.",
|
|
93697
|
-
default: DEFAULT_CONFIG.hksvPreview,
|
|
93698
|
-
requiresRestart: true,
|
|
93699
|
-
placement: { tab: "advanced" }
|
|
93700
96140
|
})
|
|
93701
96141
|
]
|
|
93702
96142
|
}] });
|
|
93703
96143
|
}
|
|
93704
96144
|
async buildDeviceSettingsContribution(deviceId) {
|
|
93705
|
-
|
|
96145
|
+
const kind = await this.resolveExportKind(deviceId);
|
|
96146
|
+
if (kind === null) return null;
|
|
93706
96147
|
const entry = this.findEntry(deviceId);
|
|
93707
96148
|
const settings = entry?.settings ?? DEFAULT_DEVICE_SETTINGS;
|
|
93708
96149
|
const enabled = entry !== null;
|
|
@@ -93719,6 +96160,32 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93719
96160
|
} catch (err) {
|
|
93720
96161
|
this.ctx.logger.withTags({ deviceId }).debug("export-hap: setupURI failed for per-device contribution", { meta: { error: errMsg(err) } });
|
|
93721
96162
|
}
|
|
96163
|
+
const cameraFields = kind !== "camera" ? [] : [{
|
|
96164
|
+
type: "select",
|
|
96165
|
+
key: streamPreferenceKey,
|
|
96166
|
+
label: "Source stream (HomeKit)",
|
|
96167
|
+
description: "Which camstack profile slot HomeKit pulls. Auto = the broker picks the slot closest to 1080p at session start.",
|
|
96168
|
+
options: HAP_STREAM_PREFERENCE_OPTIONS,
|
|
96169
|
+
required: true,
|
|
96170
|
+
value: settings.streamPreference,
|
|
96171
|
+
showWhen: {
|
|
96172
|
+
field: enabledKey,
|
|
96173
|
+
equals: true
|
|
96174
|
+
},
|
|
96175
|
+
immediate: true
|
|
96176
|
+
}, {
|
|
96177
|
+
type: "boolean",
|
|
96178
|
+
key: hksvKey,
|
|
96179
|
+
label: "HomeKit recording (Secure Video)",
|
|
96180
|
+
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).",
|
|
96181
|
+
style: "switch",
|
|
96182
|
+
value: resolveHksvRecording(settings),
|
|
96183
|
+
showWhen: {
|
|
96184
|
+
field: enabledKey,
|
|
96185
|
+
equals: true
|
|
96186
|
+
},
|
|
96187
|
+
immediate: true
|
|
96188
|
+
}];
|
|
93722
96189
|
return {
|
|
93723
96190
|
tabs: [{
|
|
93724
96191
|
id: "export",
|
|
@@ -93729,7 +96196,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93729
96196
|
sections: [{
|
|
93730
96197
|
id: "export-hap",
|
|
93731
96198
|
title: "HomeKit Export",
|
|
93732
|
-
description: "
|
|
96199
|
+
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.",
|
|
93733
96200
|
tab: "export",
|
|
93734
96201
|
columns: 1,
|
|
93735
96202
|
order: 10,
|
|
@@ -93745,7 +96212,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93745
96212
|
type: "qr-code",
|
|
93746
96213
|
key: "__hap-pair-qr",
|
|
93747
96214
|
label: paired ? "Pairing QR (already paired)" : "Pairing QR",
|
|
93748
|
-
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
|
|
96215
|
+
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}.`,
|
|
93749
96216
|
value: qrValue,
|
|
93750
96217
|
size: 192,
|
|
93751
96218
|
alt: `HomeKit pairing QR for ${name}`,
|
|
@@ -93758,38 +96225,12 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93758
96225
|
type: "boolean",
|
|
93759
96226
|
key: enabledKey,
|
|
93760
96227
|
label: "Expose to HomeKit",
|
|
93761
|
-
description: "Toggle to publish this
|
|
96228
|
+
description: "Toggle to publish this device as a HomeKit accessory.",
|
|
93762
96229
|
style: "switch",
|
|
93763
96230
|
value: enabled,
|
|
93764
96231
|
immediate: true
|
|
93765
96232
|
},
|
|
93766
|
-
|
|
93767
|
-
type: "select",
|
|
93768
|
-
key: streamPreferenceKey,
|
|
93769
|
-
label: "Source stream (HomeKit)",
|
|
93770
|
-
description: "Which camstack profile slot HomeKit pulls. Auto = the broker picks the slot closest to 1080p at session start.",
|
|
93771
|
-
options: HAP_STREAM_PREFERENCE_OPTIONS,
|
|
93772
|
-
required: true,
|
|
93773
|
-
value: settings.streamPreference,
|
|
93774
|
-
showWhen: {
|
|
93775
|
-
field: enabledKey,
|
|
93776
|
-
equals: true
|
|
93777
|
-
},
|
|
93778
|
-
immediate: true
|
|
93779
|
-
},
|
|
93780
|
-
{
|
|
93781
|
-
type: "boolean",
|
|
93782
|
-
key: hksvKey,
|
|
93783
|
-
label: "HomeKit recording (Secure Video)",
|
|
93784
|
-
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).",
|
|
93785
|
-
style: "switch",
|
|
93786
|
-
value: resolveHksvRecording(settings),
|
|
93787
|
-
showWhen: {
|
|
93788
|
-
field: enabledKey,
|
|
93789
|
-
equals: true
|
|
93790
|
-
},
|
|
93791
|
-
immediate: true
|
|
93792
|
-
}
|
|
96233
|
+
...cameraFields
|
|
93793
96234
|
]
|
|
93794
96235
|
}]
|
|
93795
96236
|
};
|
|
@@ -93855,23 +96296,6 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93855
96296
|
}
|
|
93856
96297
|
return { success: true };
|
|
93857
96298
|
}
|
|
93858
|
-
/**
|
|
93859
|
-
* Camera-only gate — HomeKit export only knows how to mirror cameras
|
|
93860
|
-
* today. Mirrors the snapshot addon's source-side filter so the
|
|
93861
|
-
* device-details page doesn't render an "Export" tab on lights /
|
|
93862
|
-
* switches / sensors.
|
|
93863
|
-
*/
|
|
93864
|
-
async isCameraDevice(deviceId) {
|
|
93865
|
-
const api = this.ctx.api;
|
|
93866
|
-
if (!api.deviceManager) return true;
|
|
93867
|
-
try {
|
|
93868
|
-
const dev = await api.deviceManager.getDevice.query({ deviceId });
|
|
93869
|
-
if (!dev) return true;
|
|
93870
|
-
return dev.type === DeviceType.Camera;
|
|
93871
|
-
} catch {
|
|
93872
|
-
return true;
|
|
93873
|
-
}
|
|
93874
|
-
}
|
|
93875
96299
|
findEntry(deviceId) {
|
|
93876
96300
|
const id = String(deviceId);
|
|
93877
96301
|
return this.config.exposed.find((e) => e.deviceId === id) ?? null;
|