@camstack/addon-export-hap 1.2.26 → 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 +2793 -301
- package/dist/hap-export.addon.mjs +2793 -301
- 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);
|
|
@@ -13726,11 +13740,33 @@ var NotificationFormatSchema = _enum([
|
|
|
13726
13740
|
* Named by INTENT, never by glyph. "check" would tie the vocabulary to one
|
|
13727
13741
|
* renderer's icon set; "acknowledge" survives an adapter that draws it
|
|
13728
13742
|
* differently.
|
|
13743
|
+
*
|
|
13744
|
+
* ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
|
|
13745
|
+
*
|
|
13746
|
+
* These names are for US. **No adapter may forward one verbatim.** Each maps
|
|
13747
|
+
* the whole set onto its own renderer's vocabulary through a
|
|
13748
|
+
* `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
|
|
13749
|
+
* fallback, so adding a member here fails every adapter's build until someone
|
|
13750
|
+
* decides its glyph, which is the only place that decision can be made
|
|
13751
|
+
* honestly.
|
|
13752
|
+
*
|
|
13753
|
+
* This paragraph is the bug. Zentik declared `actionIcons: true` and passed
|
|
13754
|
+
* `disarm` straight through; iOS feeds that string to
|
|
13755
|
+
* `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
|
|
13756
|
+
* and every snooze and alarm button arrived BLANK. A pass-through is not a
|
|
13757
|
+
* mapping, and "the field is documented" is not "the value renders".
|
|
13758
|
+
*
|
|
13759
|
+
* Adding a member is TRAIN-BOUND. The enum lives in the published
|
|
13760
|
+
* `@camstack/server` closure and the cap seam validates against the HUB's copy,
|
|
13761
|
+
* so an addon that emits a token the running hub does not know does not lose an
|
|
13762
|
+
* icon — its whole `send` fails Zod validation and the notification never
|
|
13763
|
+
* arrives. Never emit a new token from an addon before the train carrying it.
|
|
13729
13764
|
*/
|
|
13730
13765
|
var NotificationActionIconSchema = _enum([
|
|
13731
13766
|
"acknowledge",
|
|
13732
13767
|
"dismiss",
|
|
13733
13768
|
"silence",
|
|
13769
|
+
"snooze",
|
|
13734
13770
|
"view",
|
|
13735
13771
|
"play",
|
|
13736
13772
|
"open",
|
|
@@ -13738,9 +13774,13 @@ var NotificationActionIconSchema = _enum([
|
|
|
13738
13774
|
"lock",
|
|
13739
13775
|
"unlock",
|
|
13740
13776
|
"arm",
|
|
13777
|
+
"arm-home",
|
|
13778
|
+
"arm-away",
|
|
13779
|
+
"arm-night",
|
|
13741
13780
|
"disarm",
|
|
13742
13781
|
"light",
|
|
13743
|
-
"alert"
|
|
13782
|
+
"alert",
|
|
13783
|
+
"camera"
|
|
13744
13784
|
]);
|
|
13745
13785
|
/** A single tap-through action button. */
|
|
13746
13786
|
var NotificationActionSchema = object({
|
|
@@ -13940,6 +13980,24 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
13940
13980
|
targetId: string(),
|
|
13941
13981
|
enabled: boolean()
|
|
13942
13982
|
}), _void(), { kind: "mutation" });
|
|
13983
|
+
new Set([
|
|
13984
|
+
{
|
|
13985
|
+
id: "person",
|
|
13986
|
+
name: "Person"
|
|
13987
|
+
},
|
|
13988
|
+
{
|
|
13989
|
+
id: "vehicle",
|
|
13990
|
+
name: "Vehicle"
|
|
13991
|
+
},
|
|
13992
|
+
{
|
|
13993
|
+
id: "animal",
|
|
13994
|
+
name: "Animal"
|
|
13995
|
+
},
|
|
13996
|
+
{
|
|
13997
|
+
id: "package",
|
|
13998
|
+
name: "Package"
|
|
13999
|
+
}
|
|
14000
|
+
].map((l) => l.id));
|
|
13943
14001
|
var COCO_TO_MACRO = {
|
|
13944
14002
|
mapping: {
|
|
13945
14003
|
person: "person",
|
|
@@ -14737,6 +14795,8 @@ var NcSystemEventKindSchema = _enum([
|
|
|
14737
14795
|
"alarm-triggered",
|
|
14738
14796
|
"alarm-armed",
|
|
14739
14797
|
"alarm-disarmed",
|
|
14798
|
+
"alarm-arming",
|
|
14799
|
+
"alarm-arm-refused",
|
|
14740
14800
|
"camera-online",
|
|
14741
14801
|
"camera-offline",
|
|
14742
14802
|
"camera-disabled",
|
|
@@ -15945,7 +16005,87 @@ var MethodAccessSchema = _enum([
|
|
|
15945
16005
|
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
15946
16006
|
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
15947
16007
|
var CapScopeSchema = _enum(["device", "system"]);
|
|
15948
|
-
|
|
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", [
|
|
15949
16089
|
object({
|
|
15950
16090
|
type: literal("category"),
|
|
15951
16091
|
target: CapScopeSchema,
|
|
@@ -15961,18 +16101,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
|
|
|
15961
16101
|
target: string(),
|
|
15962
16102
|
access: array(MethodAccessSchema).min(1)
|
|
15963
16103
|
}),
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
/**
|
|
15967
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
15968
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
15969
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
15970
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
15971
|
-
*/
|
|
15972
|
-
targets: array(string()).min(1),
|
|
15973
|
-
access: array(MethodAccessSchema).min(1)
|
|
15974
|
-
})
|
|
15975
|
-
]);
|
|
16104
|
+
DeviceTokenScopeSchema
|
|
16105
|
+
]));
|
|
15976
16106
|
object({
|
|
15977
16107
|
id: string(),
|
|
15978
16108
|
username: string(),
|
|
@@ -17969,10 +18099,26 @@ var detectionFpsField = {
|
|
|
17969
18099
|
default: 10,
|
|
17970
18100
|
step: 1
|
|
17971
18101
|
};
|
|
18102
|
+
/**
|
|
18103
|
+
* The occupancy re-check interval. DEFAULT 300 s (2026-08-13 — was 30 s).
|
|
18104
|
+
*
|
|
18105
|
+
* The recheck is now on by default (a parked car is invisible to occupancy
|
|
18106
|
+
* rules until the stationary registry has been rebuilt by motion, which after a
|
|
18107
|
+
* restart may be never on a quiet camera). Each cycle re-subscribes a detection
|
|
18108
|
+
* session — an RTSP re-dial — so the switch is only affordable at a WIDE
|
|
18109
|
+
* interval: 300 s is ~12 re-dials an hour per camera, against 120 at the old
|
|
18110
|
+
* 30 s. A parked car is therefore counted within 5 minutes of a restart.
|
|
18111
|
+
*
|
|
18112
|
+
* Why not wider: `max` is 300 and raising it is TRAIN-BOUND, not addon-bound —
|
|
18113
|
+
* the host validates `attachCamera` against ITS copy of this schema, so a
|
|
18114
|
+
* runner asked for 600 would be rejected by the hub until a `@camstack/server`
|
|
18115
|
+
* carrying the wider bound is installed everywhere. 300 is the widest value
|
|
18116
|
+
* that ships with an addon deploy.
|
|
18117
|
+
*/
|
|
17972
18118
|
var occupancyRecheckSecField = {
|
|
17973
18119
|
min: 0,
|
|
17974
18120
|
max: 300,
|
|
17975
|
-
default:
|
|
18121
|
+
default: 300,
|
|
17976
18122
|
step: 5
|
|
17977
18123
|
};
|
|
17978
18124
|
var occupancyRecheckFramesField = {
|
|
@@ -18170,15 +18316,21 @@ var RunnerCameraConfigSchema = object({
|
|
|
18170
18316
|
*/
|
|
18171
18317
|
onboardMotionDrivesAnalyzer: boolean().default(true),
|
|
18172
18318
|
/**
|
|
18173
|
-
* Master toggle for the occupancy re-check. When `false`
|
|
18174
|
-
*
|
|
18175
|
-
* this is off by default because the recheck re-subscribes a detection session
|
|
18176
|
-
* every N seconds while `watching`, a major source of pull-decoder re-dial
|
|
18177
|
-
* churn (each cycle creates+tears a session → RTSP re-dial → latency). The
|
|
18319
|
+
* Master toggle for the occupancy re-check. When `false` the runner never arms
|
|
18320
|
+
* the periodic recheck timer, regardless of `occupancyRecheckSec`; the
|
|
18178
18321
|
* `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
|
|
18179
18322
|
* (and only render) when this is enabled.
|
|
18180
|
-
|
|
18181
|
-
|
|
18323
|
+
*
|
|
18324
|
+
* DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
|
|
18325
|
+
* recheck re-subscribes a detection session every N seconds while `watching`
|
|
18326
|
+
* — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
|
|
18327
|
+
* pull-decoder churn source. What that bought was a blind spot: a STATIONARY
|
|
18328
|
+
* object is counted only while the stationary registry holds it, and the
|
|
18329
|
+
* registry rebuilds from motion, so after a restart a parked car was invisible
|
|
18330
|
+
* to every occupancy rule until something moved in front of it. The churn is
|
|
18331
|
+
* now paid on the interval instead — see `occupancyRecheckSecField`.
|
|
18332
|
+
*/
|
|
18333
|
+
occupancyRecheckEnabled: boolean().default(true),
|
|
18182
18334
|
occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
|
|
18183
18335
|
occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
|
|
18184
18336
|
/**
|
|
@@ -20579,7 +20731,11 @@ object({
|
|
|
20579
20731
|
precision: number().int().min(0).max(10).optional()
|
|
20580
20732
|
});
|
|
20581
20733
|
DeviceType.Sensor;
|
|
20582
|
-
|
|
20734
|
+
/**
|
|
20735
|
+
* Ambient illuminance reading in lux. Drives Home Assistant `sensor`
|
|
20736
|
+
* entries with `device_class: illuminance`.
|
|
20737
|
+
*/
|
|
20738
|
+
var AmbientLightSensorStatusSchema = object({
|
|
20583
20739
|
/** Current illuminance in lux (lx). */
|
|
20584
20740
|
lux: number().min(0),
|
|
20585
20741
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -20852,7 +21008,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), CameraCredentialsSchem
|
|
|
20852
21008
|
kind: "query",
|
|
20853
21009
|
auth: "admin"
|
|
20854
21010
|
});
|
|
20855
|
-
|
|
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({
|
|
20856
21016
|
detected: boolean(),
|
|
20857
21017
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
20858
21018
|
lastChangedAt: number()
|
|
@@ -21140,7 +21300,19 @@ Object.values(DeviceType), method(object({
|
|
|
21140
21300
|
kind: "mutation",
|
|
21141
21301
|
auth: "admin"
|
|
21142
21302
|
}), ConsumablesStatusSchema.extend({ lastFetchedAt: number() });
|
|
21143
|
-
|
|
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({
|
|
21144
21316
|
/** True when the entry is open; false when closed. */
|
|
21145
21317
|
entryOpen: boolean(),
|
|
21146
21318
|
/** Ms epoch of the last open↔closed transition. 0 if never observed. */
|
|
@@ -21739,7 +21911,15 @@ object({
|
|
|
21739
21911
|
deviceId: number(),
|
|
21740
21912
|
status: FeatureProbeStatusSchema
|
|
21741
21913
|
});
|
|
21742
|
-
|
|
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({
|
|
21743
21923
|
/** True when leak is currently detected. */
|
|
21744
21924
|
flooded: boolean(),
|
|
21745
21925
|
/** Ms epoch of the last flooded↔dry transition. 0 if never observed. */
|
|
@@ -21793,7 +21973,15 @@ DeviceType.Humidifier, method(object({
|
|
|
21793
21973
|
kind: "mutation",
|
|
21794
21974
|
auth: "admin"
|
|
21795
21975
|
});
|
|
21796
|
-
|
|
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({
|
|
21797
21985
|
/** Current relative humidity, 0..100. */
|
|
21798
21986
|
percent: number().min(0).max(100),
|
|
21799
21987
|
/** Ms epoch when the slice was last updated. */
|
|
@@ -22415,7 +22603,7 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
22415
22603
|
* tunnel always emits `https://` regardless. */
|
|
22416
22604
|
scheme: _enum(["http", "https"]).optional()
|
|
22417
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" });
|
|
22418
|
-
object({
|
|
22606
|
+
var LockControlStatusSchema = object({
|
|
22419
22607
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
22420
22608
|
* failure to reach the target — operator intervention required. */
|
|
22421
22609
|
state: _enum([
|
|
@@ -22753,7 +22941,19 @@ authKey: string().optional() }), object({
|
|
|
22753
22941
|
/** Human-readable error when `ok: false`. */
|
|
22754
22942
|
error: string().optional()
|
|
22755
22943
|
}), { kind: "mutation" });
|
|
22756
|
-
|
|
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({
|
|
22757
22957
|
detected: boolean(),
|
|
22758
22958
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
22759
22959
|
lastDetectedAt: number().nullable(),
|
|
@@ -23865,7 +24065,7 @@ var GpsLocationSchema = object({
|
|
|
23865
24065
|
/** Reported accuracy in meters (lower = better). */
|
|
23866
24066
|
accuracyMeters: number().nonnegative()
|
|
23867
24067
|
});
|
|
23868
|
-
object({
|
|
24068
|
+
var PresenceStatusSchema = object({
|
|
23869
24069
|
/** `home` / `not_home` / any user-defined zone name. */
|
|
23870
24070
|
state: string(),
|
|
23871
24071
|
/** Optional textual location label (zone name, city, address). Null
|
|
@@ -24281,7 +24481,7 @@ method(object({
|
|
|
24281
24481
|
toMs: number()
|
|
24282
24482
|
}), RecordingAvailabilitySchema, {
|
|
24283
24483
|
kind: "query",
|
|
24284
|
-
auth: "
|
|
24484
|
+
auth: "protected"
|
|
24285
24485
|
}), method(object({
|
|
24286
24486
|
deviceId: number(),
|
|
24287
24487
|
fromMs: number(),
|
|
@@ -24289,14 +24489,14 @@ method(object({
|
|
|
24289
24489
|
tzOffsetMinutes: number()
|
|
24290
24490
|
}), RecordingDaysSchema, {
|
|
24291
24491
|
kind: "query",
|
|
24292
|
-
auth: "
|
|
24492
|
+
auth: "protected"
|
|
24293
24493
|
}), method(object({
|
|
24294
24494
|
deviceId: number(),
|
|
24295
24495
|
fromMs: number(),
|
|
24296
24496
|
toMs: number()
|
|
24297
24497
|
}), RecordingManifestSchema, {
|
|
24298
24498
|
kind: "query",
|
|
24299
|
-
auth: "
|
|
24499
|
+
auth: "protected"
|
|
24300
24500
|
}), method(object({}), RecordingStorageUsageSchema, {
|
|
24301
24501
|
kind: "query",
|
|
24302
24502
|
auth: "admin"
|
|
@@ -24825,7 +25025,16 @@ DeviceType.Script, method(object({
|
|
|
24825
25025
|
kind: "mutation",
|
|
24826
25026
|
auth: "admin"
|
|
24827
25027
|
});
|
|
24828
|
-
|
|
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({
|
|
24829
25038
|
detected: boolean(),
|
|
24830
25039
|
/** Ms epoch of the last transition. 0 if never observed. */
|
|
24831
25040
|
lastChangedAt: number()
|
|
@@ -25034,7 +25243,23 @@ object({
|
|
|
25034
25243
|
lastChangedAt: number()
|
|
25035
25244
|
});
|
|
25036
25245
|
DeviceType.Sensor;
|
|
25037
|
-
|
|
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({
|
|
25038
25263
|
/** Current temperature in Celsius. */
|
|
25039
25264
|
celsius: number(),
|
|
25040
25265
|
/** Ms epoch when the slice was last updated (push or poll). */
|
|
@@ -31295,6 +31520,1678 @@ Object.freeze({
|
|
|
31295
31520
|
access: "create"
|
|
31296
31521
|
}
|
|
31297
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
|
+
});
|
|
31298
33195
|
Object.freeze({
|
|
31299
33196
|
"broker": "broker",
|
|
31300
33197
|
"device-export": "device-export",
|
|
@@ -32014,7 +33911,7 @@ var Fmp4FragmentChild = class {
|
|
|
32014
33911
|
meta: {
|
|
32015
33912
|
sourceId: this.args.sourceId,
|
|
32016
33913
|
decodeHwAccel: requested,
|
|
32017
|
-
error: errMsg$
|
|
33914
|
+
error: errMsg$15(err)
|
|
32018
33915
|
}
|
|
32019
33916
|
});
|
|
32020
33917
|
this.killChild();
|
|
@@ -32193,7 +34090,7 @@ var Fmp4FragmentChild = class {
|
|
|
32193
34090
|
tags: { deviceId: this.args.deviceId },
|
|
32194
34091
|
meta: {
|
|
32195
34092
|
sourceId: this.args.sourceId,
|
|
32196
|
-
error: errMsg$
|
|
34093
|
+
error: errMsg$15(err)
|
|
32197
34094
|
}
|
|
32198
34095
|
});
|
|
32199
34096
|
}
|
|
@@ -78538,7 +80435,7 @@ function clearPairingFiles(accessoryUuid, logger) {
|
|
|
78538
80435
|
}
|
|
78539
80436
|
//#endregion
|
|
78540
80437
|
//#region src/hap-setup-uri.ts
|
|
78541
|
-
function errMsg$
|
|
80438
|
+
function errMsg$14(e) {
|
|
78542
80439
|
return e instanceof Error ? e.message : String(e);
|
|
78543
80440
|
}
|
|
78544
80441
|
/**
|
|
@@ -78565,11 +80462,79 @@ function firstExposedAccessorySetupUri(exposed, logger) {
|
|
|
78565
80462
|
try {
|
|
78566
80463
|
return first.setupURI();
|
|
78567
80464
|
} catch (err) {
|
|
78568
|
-
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) } });
|
|
78569
80466
|
return;
|
|
78570
80467
|
}
|
|
78571
80468
|
}
|
|
78572
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
|
+
}
|
|
78573
80538
|
/**
|
|
78574
80539
|
* hap-nodejs' `checkName` regex, verbatim.
|
|
78575
80540
|
*
|
|
@@ -78706,6 +80671,92 @@ function titleCase(raw) {
|
|
|
78706
80671
|
return raw.split(/[-_\s]+/u).filter((part) => part.length > 0).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
78707
80672
|
}
|
|
78708
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
|
|
78709
80760
|
//#region src/mappers/builders/battery.ts
|
|
78710
80761
|
/**
|
|
78711
80762
|
* Battery builder — surfaces a battery-operated camera's power state
|
|
@@ -78715,34 +80766,56 @@ function titleCase(raw) {
|
|
|
78715
80766
|
* subscribes to the runtime-state slice so iOS Home reflects level /
|
|
78716
80767
|
* charging changes pushed by the firmware without a poll loop.
|
|
78717
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
|
+
/**
|
|
78718
80778
|
* Mapping:
|
|
78719
80779
|
* - `BatteryStatus.percentage` (0..100) → `Characteristic.BatteryLevel`
|
|
78720
80780
|
* - `BatteryStatus.charging`:
|
|
78721
80781
|
* `'none'` → `ChargingState.NOT_CHARGING`
|
|
78722
80782
|
* `'dc' | 'solar'` → `ChargingState.CHARGING`
|
|
78723
80783
|
* - `percentage <= LOW_BATTERY_THRESHOLD_PCT` → `StatusLowBattery.LOW`
|
|
78724
|
-
*
|
|
78725
|
-
* Skipped silently when the `battery` cap is not bound — caller checks
|
|
78726
|
-
* cap presence before invoking this builder (see `camera-accessory.ts`).
|
|
78727
80784
|
*/
|
|
78728
|
-
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
|
+
});
|
|
78729
80802
|
async function buildBattery(bctx) {
|
|
78730
80803
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
78731
80804
|
const log = ctx.logger.withTags({ deviceId: numericDeviceId });
|
|
78732
|
-
const
|
|
80805
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
80806
|
+
const service = accessory.addService(import_dist.Service.Battery, label);
|
|
80807
|
+
applyServiceLabel(service, label);
|
|
78733
80808
|
try {
|
|
78734
80809
|
const status = await proxy.battery?.getStatus({});
|
|
78735
|
-
if (status) applyToService(service, status);
|
|
80810
|
+
if (status !== void 0 && status !== null) applyToService(service, status);
|
|
78736
80811
|
} catch (err) {
|
|
78737
|
-
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) } });
|
|
78738
80813
|
}
|
|
78739
80814
|
const unsubscribes = [];
|
|
78740
80815
|
if (proxy.state.battery) {
|
|
78741
80816
|
const unsub = proxy.state.battery.subscribe((value) => {
|
|
78742
80817
|
if (!value) return;
|
|
78743
|
-
|
|
78744
|
-
if (typeof status.percentage !== "number") return;
|
|
78745
|
-
applyToService(service, status);
|
|
80818
|
+
applyToService(service, value);
|
|
78746
80819
|
});
|
|
78747
80820
|
unsubscribes.push(unsub);
|
|
78748
80821
|
}
|
|
@@ -78753,14 +80826,9 @@ async function buildBattery(bctx) {
|
|
|
78753
80826
|
} };
|
|
78754
80827
|
}
|
|
78755
80828
|
function applyToService(service, status) {
|
|
78756
|
-
|
|
78757
|
-
service.updateCharacteristic(import_dist.Characteristic.BatteryLevel, pct);
|
|
78758
|
-
const chargingState = status.charging === "none" ? import_dist.Characteristic.ChargingState.NOT_CHARGING : import_dist.Characteristic.ChargingState.CHARGING;
|
|
78759
|
-
service.updateCharacteristic(import_dist.Characteristic.ChargingState, chargingState);
|
|
78760
|
-
const lowBattery = pct <= LOW_BATTERY_THRESHOLD_PCT ? import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : import_dist.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
78761
|
-
service.updateCharacteristic(import_dist.Characteristic.StatusLowBattery, lowBattery);
|
|
80829
|
+
applyUpdates(service, batteryCharacteristicUpdates(status));
|
|
78762
80830
|
}
|
|
78763
|
-
function errMsg$
|
|
80831
|
+
function errMsg$13(err) {
|
|
78764
80832
|
return err instanceof Error ? err.message : String(err);
|
|
78765
80833
|
}
|
|
78766
80834
|
//#endregion
|
|
@@ -89512,7 +91580,7 @@ var VIDEO_LOOPBACK_RCVBUF_BYTES = 8 * 1024 * 1024;
|
|
|
89512
91580
|
* not burst size.
|
|
89513
91581
|
*/
|
|
89514
91582
|
var AUDIO_LOOPBACK_RCVBUF_BYTES = 1024 * 1024;
|
|
89515
|
-
function errMsg$
|
|
91583
|
+
function errMsg$12(err) {
|
|
89516
91584
|
return err instanceof Error ? err.message : String(err);
|
|
89517
91585
|
}
|
|
89518
91586
|
/**
|
|
@@ -89527,13 +91595,13 @@ function applyReceiveBuffer(socket, requestedBytes) {
|
|
|
89527
91595
|
try {
|
|
89528
91596
|
socket.setRecvBufferSize(requestedBytes);
|
|
89529
91597
|
} catch (err) {
|
|
89530
|
-
error = errMsg$
|
|
91598
|
+
error = errMsg$12(err);
|
|
89531
91599
|
}
|
|
89532
91600
|
let effectiveBytes = null;
|
|
89533
91601
|
try {
|
|
89534
91602
|
effectiveBytes = socket.getRecvBufferSize();
|
|
89535
91603
|
} catch (err) {
|
|
89536
|
-
if (error === null) error = errMsg$
|
|
91604
|
+
if (error === null) error = errMsg$12(err);
|
|
89537
91605
|
}
|
|
89538
91606
|
return {
|
|
89539
91607
|
requestedBytes,
|
|
@@ -89793,20 +91861,20 @@ function buildCameraStreamingDelegate(bctx, advertised) {
|
|
|
89793
91861
|
delegate: {
|
|
89794
91862
|
handleSnapshotRequest(request, callback) {
|
|
89795
91863
|
handleSnapshot(bctx, request).then((buf) => callback(void 0, buf)).catch((err) => {
|
|
89796
|
-
log.warn("export-hap: snapshot failed", { meta: { error: errMsg$
|
|
89797
|
-
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)));
|
|
89798
91866
|
});
|
|
89799
91867
|
},
|
|
89800
91868
|
prepareStream(request, callback) {
|
|
89801
91869
|
prepareStream(request, sessions, bctx).then((resp) => callback(void 0, resp)).catch((err) => {
|
|
89802
|
-
log.warn("export-hap: prepareStream failed", { meta: { error: errMsg$
|
|
89803
|
-
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)));
|
|
89804
91872
|
});
|
|
89805
91873
|
},
|
|
89806
91874
|
handleStreamRequest(request, callback) {
|
|
89807
91875
|
handleStreamRequest(request, sessions, bctx, advertised).then(() => callback()).catch((err) => {
|
|
89808
|
-
log.warn("export-hap: handleStreamRequest failed", { meta: { error: errMsg$
|
|
89809
|
-
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)));
|
|
89810
91878
|
});
|
|
89811
91879
|
}
|
|
89812
91880
|
},
|
|
@@ -89922,7 +91990,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
89922
91990
|
closeSocket(audioUdp);
|
|
89923
91991
|
closeSocket(videoLoopUdp);
|
|
89924
91992
|
closeSocket(audioLoopUdp);
|
|
89925
|
-
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 });
|
|
89926
91994
|
}
|
|
89927
91995
|
let upstreamAudioSrtp = null;
|
|
89928
91996
|
try {
|
|
@@ -89936,7 +92004,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
89936
92004
|
profile: import_src.ProtectionProfileAes128CmHmacSha1_80
|
|
89937
92005
|
});
|
|
89938
92006
|
} catch (err) {
|
|
89939
|
-
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) } });
|
|
89940
92008
|
}
|
|
89941
92009
|
const videoSsrc = randomSsrc();
|
|
89942
92010
|
const audioSsrc = randomSsrc();
|
|
@@ -90053,7 +92121,7 @@ async function prepareStream(request, sessions, bctx) {
|
|
|
90053
92121
|
});
|
|
90054
92122
|
audioUdp.on("message", (packet, rinfo) => {
|
|
90055
92123
|
handleIncomingAudioRtp(session, packet, rinfo.address, bctx).catch((err) => {
|
|
90056
|
-
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) } });
|
|
90057
92125
|
});
|
|
90058
92126
|
});
|
|
90059
92127
|
logLoopbackBuffer(tagLog, request.sessionID, "video", videoLoop.buffer);
|
|
@@ -90249,7 +92317,7 @@ function readControllerRtcp(session, leg, packet, log) {
|
|
|
90249
92317
|
} catch (err) {
|
|
90250
92318
|
drop(session, "inbound-rtcp-decrypt-failed");
|
|
90251
92319
|
storeReceiverReports(session, leg, recordUnreadableRtcp(tally));
|
|
90252
|
-
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$
|
|
92320
|
+
logUnreadableRtcp(session, leg, `decrypt: ${errMsg$11(err)}`, log);
|
|
90253
92321
|
return;
|
|
90254
92322
|
}
|
|
90255
92323
|
const outcome = ingestDecryptedRtcp(plaintext, tally);
|
|
@@ -91015,7 +93083,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91015
93083
|
session.upstreamRtpDecryptFailures += 1;
|
|
91016
93084
|
if (session.upstreamRtpDecryptFailures % 100 === 1) log.debug("export-hap: SRTP decrypt failed (rate-limited)", { meta: {
|
|
91017
93085
|
failures: session.upstreamRtpDecryptFailures,
|
|
91018
|
-
error: errMsg$
|
|
93086
|
+
error: errMsg$11(err)
|
|
91019
93087
|
} });
|
|
91020
93088
|
return;
|
|
91021
93089
|
}
|
|
@@ -91029,7 +93097,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91029
93097
|
rtpPayloadType = parsedRtp.header.payloadType;
|
|
91030
93098
|
} catch (err) {
|
|
91031
93099
|
drop(session, "upstream-parse-failed");
|
|
91032
|
-
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) } });
|
|
91033
93101
|
return;
|
|
91034
93102
|
}
|
|
91035
93103
|
const negotiatedAudioPt = session.lastStartParams?.audioPt;
|
|
@@ -91053,7 +93121,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91053
93121
|
if (session.intercomTalkSessionId === null) {
|
|
91054
93122
|
session.intercomTalkSessionId = "";
|
|
91055
93123
|
const opened = await openIntercomTalkSession(bctx).catch((err) => {
|
|
91056
|
-
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$
|
|
93124
|
+
log.warn("export-hap: intercom.startTalkSession failed", { meta: { error: errMsg$11(err) } });
|
|
91057
93125
|
return null;
|
|
91058
93126
|
});
|
|
91059
93127
|
if (opened) {
|
|
@@ -91080,7 +93148,7 @@ async function handleIncomingAudioRtp(session, packet, sourceAddress, bctx) {
|
|
|
91080
93148
|
drop(session, "upstream-push-failed");
|
|
91081
93149
|
log.debug("export-hap: intercom.pushTalkAudio failed (will re-open on next frame)", { meta: {
|
|
91082
93150
|
sequenceNumber: session.intercomPcmSequence,
|
|
91083
|
-
error: errMsg$
|
|
93151
|
+
error: errMsg$11(err)
|
|
91084
93152
|
} });
|
|
91085
93153
|
session.intercomTalkSessionId = null;
|
|
91086
93154
|
}
|
|
@@ -91112,7 +93180,7 @@ async function closeIntercomTalkSession(session, bctx) {
|
|
|
91112
93180
|
} catch (err) {
|
|
91113
93181
|
log.debug("export-hap: intercom.endTalkSession failed (continuing)", { meta: {
|
|
91114
93182
|
sessionId,
|
|
91115
|
-
error: errMsg$
|
|
93183
|
+
error: errMsg$11(err)
|
|
91116
93184
|
} });
|
|
91117
93185
|
}
|
|
91118
93186
|
}
|
|
@@ -91137,7 +93205,7 @@ function closeSocket(udp) {
|
|
|
91137
93205
|
function randomSsrc() {
|
|
91138
93206
|
return Math.floor(Math.random() * 2147483646) + 1 | 0;
|
|
91139
93207
|
}
|
|
91140
|
-
function errMsg$
|
|
93208
|
+
function errMsg$11(err) {
|
|
91141
93209
|
return err instanceof Error ? err.message : String(err);
|
|
91142
93210
|
}
|
|
91143
93211
|
//#endregion
|
|
@@ -91221,14 +93289,14 @@ async function buildDoorbell(input) {
|
|
|
91221
93289
|
}
|
|
91222
93290
|
log.info("export-hap: doorbell SINGLE_PRESS pushed to HomeKit", { meta: { ...delivery } });
|
|
91223
93291
|
} catch (err) {
|
|
91224
|
-
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$
|
|
93292
|
+
log.warn("export-hap: ringDoorbell() failed", { meta: { error: errMsg$10(err) } });
|
|
91225
93293
|
}
|
|
91226
93294
|
});
|
|
91227
93295
|
return { async dispose() {
|
|
91228
93296
|
unsubscribe();
|
|
91229
93297
|
} };
|
|
91230
93298
|
}
|
|
91231
|
-
function errMsg$
|
|
93299
|
+
function errMsg$10(err) {
|
|
91232
93300
|
return err instanceof Error ? err.message : String(err);
|
|
91233
93301
|
}
|
|
91234
93302
|
//#endregion
|
|
@@ -91268,13 +93336,15 @@ var RESET_DEBOUNCE_MS = 5e3;
|
|
|
91268
93336
|
*/
|
|
91269
93337
|
async function buildMotionSensor(bctx, existing = null) {
|
|
91270
93338
|
const { ctx, accessory, proxy, numericDeviceId, displayName } = bctx;
|
|
91271
|
-
const
|
|
93339
|
+
const label = hapServiceName([displayName], `Camera ${numericDeviceId}`);
|
|
93340
|
+
const motionService = existing ?? accessory.addService(import_dist.Service.MotionSensor, label);
|
|
93341
|
+
applyServiceLabel(motionService, label);
|
|
91272
93342
|
motionService.setCharacteristic(import_dist.Characteristic.MotionDetected, false);
|
|
91273
93343
|
try {
|
|
91274
93344
|
const detected = await proxy.motion?.isDetected({});
|
|
91275
93345
|
if (typeof detected === "boolean") motionService.updateCharacteristic(import_dist.Characteristic.MotionDetected, detected);
|
|
91276
93346
|
} catch (err) {
|
|
91277
|
-
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) } });
|
|
91278
93348
|
}
|
|
91279
93349
|
let resetTimer = null;
|
|
91280
93350
|
const armReset = () => {
|
|
@@ -91308,82 +93378,10 @@ async function buildMotionSensor(bctx, existing = null) {
|
|
|
91308
93378
|
}
|
|
91309
93379
|
} };
|
|
91310
93380
|
}
|
|
91311
|
-
function errMsg$
|
|
93381
|
+
function errMsg$9(err) {
|
|
91312
93382
|
return err instanceof Error ? err.message : String(err);
|
|
91313
93383
|
}
|
|
91314
93384
|
//#endregion
|
|
91315
|
-
//#region src/mappers/builders/service-label.ts
|
|
91316
|
-
/**
|
|
91317
|
-
* The ONE place a secondary service on the camera accessory gets its label.
|
|
91318
|
-
*
|
|
91319
|
-
* A "secondary service" here is a Switch or Lightbulb published alongside the
|
|
91320
|
-
* camera on the same accessory — the privacy switch, each accessory child
|
|
91321
|
-
* (siren, floodlight), each PTZ action. iOS Home renders these as their own
|
|
91322
|
-
* controls, and the operator has seen them as "Interruttore 1", "Interruttore
|
|
91323
|
-
* 2" through three separate rounds of fixes.
|
|
91324
|
-
*
|
|
91325
|
-
* ## Why `Name` alone cannot rename anything
|
|
91326
|
-
*
|
|
91327
|
-
* Two facts about hap-nodejs 2.1.7, both measured against the installed copy
|
|
91328
|
-
* rather than reasoned about:
|
|
91329
|
-
*
|
|
91330
|
-
* 1. `accessory.addService(Type, displayName, subtype)` ALREADY writes
|
|
91331
|
-
* `displayName` to `Characteristic.Name` (`Service` constructor). So every
|
|
91332
|
-
* round of this bug — including the one that moved the label onto
|
|
91333
|
-
* `ConfiguredName` — shipped with `Name` correctly set. "iOS had no name
|
|
91334
|
-
* to render" was never true.
|
|
91335
|
-
* 2. The mDNS configuration number (`c#`) is a sha1 over
|
|
91336
|
-
* `internalHAPRepresentation(false)`, which OMITS characteristic VALUES.
|
|
91337
|
-
* Changing the string in `Name` therefore does not bump `c#`, a paired
|
|
91338
|
-
* controller gets no signal to re-read `/accessories`, and the name it
|
|
91339
|
-
* cached at first enumeration stands forever.
|
|
91340
|
-
*
|
|
91341
|
-
* `Name` is also declared `pr` only — paired read, no write, no notify. It is
|
|
91342
|
-
* the seed a controller seeds its database from once; it is not a channel.
|
|
91343
|
-
*
|
|
91344
|
-
* ## Why `ConfiguredName`
|
|
91345
|
-
*
|
|
91346
|
-
* `ConfiguredName` (`000000E3`) is declared `pr | pw | ev` — the only name
|
|
91347
|
-
* characteristic a controller may write and may subscribe to. It is what iOS
|
|
91348
|
-
* 16+ reads for a service the user can rename, and adding it CHANGES the
|
|
91349
|
-
* accessory structure, so `c#` does bump and the controller re-reads.
|
|
91350
|
-
*
|
|
91351
|
-
* It was removed once because hap-nodejs logged
|
|
91352
|
-
*
|
|
91353
|
-
* ```
|
|
91354
|
-
* Characteristic not in required or optional characteristic section for
|
|
91355
|
-
* service Switch. Adding anyway.
|
|
91356
|
-
* ```
|
|
91357
|
-
*
|
|
91358
|
-
* That line is a WARNING, not a rejection: `Service.getCharacteristic` calls
|
|
91359
|
-
* `addCharacteristic` unconditionally and only then emits the warning. The
|
|
91360
|
-
* characteristic was always present and always published. hap-nodejs'
|
|
91361
|
-
* per-service optional lists simply predate `ConfiguredName` being valid on
|
|
91362
|
-
* any service.
|
|
91363
|
-
*
|
|
91364
|
-
* Registering it with {@link Service.addOptionalCharacteristic} first takes
|
|
91365
|
-
* the branch above the warning, so the accessory still builds with ZERO
|
|
91366
|
-
* characteristic warnings — which is what `service-naming.spec.ts` asserts.
|
|
91367
|
-
*
|
|
91368
|
-
* ## Scope
|
|
91369
|
-
*
|
|
91370
|
-
* Switch- and Lightbulb-shaped services only. `Service.MotionSensor` on a
|
|
91371
|
-
* camera accessory is not a separately named tile in iOS Home, so giving it a
|
|
91372
|
-
* writable name would be a guess, and this module does not guess.
|
|
91373
|
-
*/
|
|
91374
|
-
/**
|
|
91375
|
-
* Publish `name` as both the immutable `Name` and the controller-visible
|
|
91376
|
-
* `ConfiguredName` of `service`.
|
|
91377
|
-
*
|
|
91378
|
-
* `name` must already be HAP-valid — build it with `service-names.ts`, which
|
|
91379
|
-
* cannot return a string hap-nodejs' `checkName` would warn about.
|
|
91380
|
-
*/
|
|
91381
|
-
function applyServiceLabel(service, name) {
|
|
91382
|
-
service.setCharacteristic(import_dist.Characteristic.Name, name);
|
|
91383
|
-
if (!service.optionalCharacteristics.some((characteristic) => characteristic.UUID === import_dist.Characteristic.ConfiguredName.UUID)) service.addOptionalCharacteristic(import_dist.Characteristic.ConfiguredName);
|
|
91384
|
-
service.setCharacteristic(import_dist.Characteristic.ConfiguredName, name);
|
|
91385
|
-
}
|
|
91386
|
-
//#endregion
|
|
91387
93385
|
//#region src/mappers/builders/privacy-switch.ts
|
|
91388
93386
|
/**
|
|
91389
93387
|
* Privacy-mask switch builder — turns the camstack `privacy-mask` cap's
|
|
@@ -91411,7 +93409,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91411
93409
|
const status = await proxy.privacyMask?.getStatus({});
|
|
91412
93410
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91413
93411
|
} catch (err) {
|
|
91414
|
-
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) } });
|
|
91415
93413
|
}
|
|
91416
93414
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91417
93415
|
const enabled = value === true;
|
|
@@ -91420,7 +93418,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91420
93418
|
} catch (err) {
|
|
91421
93419
|
log.warn("export-hap: privacy-mask setMask failed", { meta: {
|
|
91422
93420
|
enabled,
|
|
91423
|
-
error: errMsg$
|
|
93421
|
+
error: errMsg$8(err)
|
|
91424
93422
|
} });
|
|
91425
93423
|
}
|
|
91426
93424
|
});
|
|
@@ -91438,7 +93436,7 @@ async function buildPrivacySwitch(bctx) {
|
|
|
91438
93436
|
} catch {}
|
|
91439
93437
|
} };
|
|
91440
93438
|
}
|
|
91441
|
-
function errMsg$
|
|
93439
|
+
function errMsg$8(err) {
|
|
91442
93440
|
return err instanceof Error ? err.message : String(err);
|
|
91443
93441
|
}
|
|
91444
93442
|
//#endregion
|
|
@@ -91529,7 +93527,7 @@ async function buildPtz(bctx) {
|
|
|
91529
93527
|
} catch (err) {
|
|
91530
93528
|
log.warn("export-hap: ptz.goToPreset failed", { meta: {
|
|
91531
93529
|
presetId: preset.id,
|
|
91532
|
-
error: errMsg$
|
|
93530
|
+
error: errMsg$7(err)
|
|
91533
93531
|
} });
|
|
91534
93532
|
}
|
|
91535
93533
|
armReset(() => service.updateCharacteristic(import_dist.Characteristic.On, false), MOMENTARY_RESET_MS);
|
|
@@ -91547,14 +93545,14 @@ async function buildPtz(bctx) {
|
|
|
91547
93545
|
try {
|
|
91548
93546
|
await proxy.ptz?.stop({});
|
|
91549
93547
|
} catch (err) {
|
|
91550
|
-
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) } });
|
|
91551
93549
|
}
|
|
91552
93550
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91553
93551
|
}, options.ptzPulseMs);
|
|
91554
93552
|
} catch (err) {
|
|
91555
93553
|
log.warn("export-hap: ptz.continuousMove failed", { meta: {
|
|
91556
93554
|
dir: dir.label,
|
|
91557
|
-
error: errMsg$
|
|
93555
|
+
error: errMsg$7(err)
|
|
91558
93556
|
} });
|
|
91559
93557
|
service.updateCharacteristic(import_dist.Characteristic.On, false);
|
|
91560
93558
|
}
|
|
@@ -91577,7 +93575,7 @@ async function readPresets(bctx) {
|
|
|
91577
93575
|
name: p.name
|
|
91578
93576
|
}));
|
|
91579
93577
|
} catch (err) {
|
|
91580
|
-
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) } });
|
|
91581
93579
|
return [];
|
|
91582
93580
|
}
|
|
91583
93581
|
}
|
|
@@ -91592,7 +93590,7 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91592
93590
|
const status = await proxy.ptzAutotrack.getStatus({});
|
|
91593
93591
|
if (status && typeof status.enabled === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, status.enabled);
|
|
91594
93592
|
} catch (err) {
|
|
91595
|
-
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) } });
|
|
91596
93594
|
}
|
|
91597
93595
|
service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
91598
93596
|
const enabled = value === true;
|
|
@@ -91601,13 +93599,13 @@ async function tryBuildAutotrack(bctx) {
|
|
|
91601
93599
|
} catch (err) {
|
|
91602
93600
|
log.warn("export-hap: ptzAutotrack.setEnabled failed", { meta: {
|
|
91603
93601
|
enabled,
|
|
91604
|
-
error: errMsg$
|
|
93602
|
+
error: errMsg$7(err)
|
|
91605
93603
|
} });
|
|
91606
93604
|
}
|
|
91607
93605
|
});
|
|
91608
93606
|
return { async dispose() {} };
|
|
91609
93607
|
}
|
|
91610
|
-
function errMsg$
|
|
93608
|
+
function errMsg$7(err) {
|
|
91611
93609
|
return err instanceof Error ? err.message : String(err);
|
|
91612
93610
|
}
|
|
91613
93611
|
//#endregion
|
|
@@ -92638,13 +94636,13 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92638
94636
|
const switchStatus = await proxy.switch?.getStatus({});
|
|
92639
94637
|
if (switchStatus && typeof switchStatus.on === "boolean") service.updateCharacteristic(import_dist.Characteristic.On, switchStatus.on);
|
|
92640
94638
|
} catch (err) {
|
|
92641
|
-
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) } });
|
|
92642
94640
|
}
|
|
92643
94641
|
if (useLightbulb) try {
|
|
92644
94642
|
const status = await proxy.brightness?.getStatus({});
|
|
92645
94643
|
if (status && typeof status.percentage === "number") service.updateCharacteristic(import_dist.Characteristic.Brightness, status.percentage);
|
|
92646
94644
|
} catch (err) {
|
|
92647
|
-
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) } });
|
|
92648
94646
|
}
|
|
92649
94647
|
if (hasSwitch) service.getCharacteristic(import_dist.Characteristic.On).onSet(async (value) => {
|
|
92650
94648
|
const on = value === true;
|
|
@@ -92653,7 +94651,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92653
94651
|
} catch (err) {
|
|
92654
94652
|
log.warn("export-hap: child switch.setState failed", { meta: {
|
|
92655
94653
|
on,
|
|
92656
|
-
error: errMsg$
|
|
94654
|
+
error: errMsg$6(err)
|
|
92657
94655
|
} });
|
|
92658
94656
|
}
|
|
92659
94657
|
});
|
|
@@ -92665,7 +94663,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92665
94663
|
} catch (err) {
|
|
92666
94664
|
log.warn("export-hap: child brightness.setBrightness failed", { meta: {
|
|
92667
94665
|
percentage,
|
|
92668
|
-
error: errMsg$
|
|
94666
|
+
error: errMsg$6(err)
|
|
92669
94667
|
} });
|
|
92670
94668
|
}
|
|
92671
94669
|
});
|
|
@@ -92688,7 +94686,7 @@ async function buildChildSwitch(bctx, subtype, deviceType) {
|
|
|
92688
94686
|
} catch {}
|
|
92689
94687
|
} };
|
|
92690
94688
|
}
|
|
92691
|
-
function errMsg$
|
|
94689
|
+
function errMsg$6(err) {
|
|
92692
94690
|
return err instanceof Error ? err.message : String(err);
|
|
92693
94691
|
}
|
|
92694
94692
|
//#endregion
|
|
@@ -92715,7 +94713,7 @@ async function buildChildServicesFor(input) {
|
|
|
92715
94713
|
for (const h of handles) try {
|
|
92716
94714
|
await h.dispose();
|
|
92717
94715
|
} catch (err) {
|
|
92718
|
-
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) } });
|
|
92719
94717
|
}
|
|
92720
94718
|
} };
|
|
92721
94719
|
}
|
|
@@ -92731,7 +94729,7 @@ async function listChildren(ctx, parentNumericId) {
|
|
|
92731
94729
|
features: Array.isArray(c.features) ? c.features.filter((f) => typeof f === "string") : []
|
|
92732
94730
|
}));
|
|
92733
94731
|
} catch (err) {
|
|
92734
|
-
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) } });
|
|
92735
94733
|
return [];
|
|
92736
94734
|
}
|
|
92737
94735
|
}
|
|
@@ -92747,10 +94745,76 @@ function asDeviceType(raw) {
|
|
|
92747
94745
|
for (const value of Object.values(DeviceType)) if (value === lower) return value;
|
|
92748
94746
|
return DeviceType.Generic;
|
|
92749
94747
|
}
|
|
92750
|
-
function errMsg$
|
|
94748
|
+
function errMsg$5(err) {
|
|
92751
94749
|
return err instanceof Error ? err.message : String(err);
|
|
92752
94750
|
}
|
|
92753
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
|
|
92754
94818
|
//#region src/mappers/camera-accessory.ts
|
|
92755
94819
|
/**
|
|
92756
94820
|
* Camera accessory orchestrator — given a camstack deviceId, builds one
|
|
@@ -92783,9 +94847,9 @@ async function buildCameraAccessory(input) {
|
|
|
92783
94847
|
const capNames = new Set(proxy.binding?.entries.map((e) => e.capName) ?? []);
|
|
92784
94848
|
const isDoorbell = capNames.has("doorbell");
|
|
92785
94849
|
const category = isDoorbell ? import_dist.Categories.VIDEO_DOORBELL : import_dist.Categories.IP_CAMERA;
|
|
92786
|
-
const accessory = new import_dist.Accessory(displayName,
|
|
94850
|
+
const accessory = new import_dist.Accessory(displayName, accessoryUuidFor("camera", numericId));
|
|
92787
94851
|
accessory.category = category;
|
|
92788
|
-
await populateAccessoryInfo(accessory, proxy, displayName);
|
|
94852
|
+
await populateAccessoryInfo(accessory, proxy, displayName, "Camera");
|
|
92789
94853
|
const bctx = {
|
|
92790
94854
|
ctx,
|
|
92791
94855
|
accessory,
|
|
@@ -92843,55 +94907,462 @@ async function buildCameraAccessory(input) {
|
|
|
92843
94907
|
for (const h of handles) try {
|
|
92844
94908
|
await h.dispose();
|
|
92845
94909
|
} catch (err) {
|
|
92846
|
-
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) } });
|
|
92847
94911
|
}
|
|
92848
94912
|
try {
|
|
92849
94913
|
await streams.dispose();
|
|
92850
94914
|
} catch (err) {
|
|
92851
|
-
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$
|
|
94915
|
+
log.debug("export-hap: streams dispose failed", { meta: { error: errMsg$4(err) } });
|
|
92852
94916
|
}
|
|
92853
94917
|
}
|
|
92854
94918
|
};
|
|
92855
94919
|
}
|
|
92856
|
-
|
|
92857
|
-
|
|
92858
|
-
|
|
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
|
+
};
|
|
92859
94975
|
try {
|
|
92860
|
-
const
|
|
92861
|
-
|
|
92862
|
-
|
|
92863
|
-
|
|
92864
|
-
|
|
92865
|
-
|
|
92866
|
-
|
|
92867
|
-
|
|
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
|
+
} };
|
|
92868
95002
|
}
|
|
92869
|
-
function
|
|
92870
|
-
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
|
+
}
|
|
92871
95351
|
}
|
|
92872
95352
|
function errMsg$1(err) {
|
|
92873
95353
|
return err instanceof Error ? err.message : String(err);
|
|
92874
95354
|
}
|
|
92875
95355
|
//#endregion
|
|
92876
95356
|
//#region src/mappers/index.ts
|
|
92877
|
-
var
|
|
92878
|
-
|
|
95357
|
+
var REGISTRY = {
|
|
95358
|
+
camera: buildCameraAccessory,
|
|
95359
|
+
generic: buildGenericAccessory
|
|
95360
|
+
};
|
|
92879
95361
|
function getMapperFactory(kind) {
|
|
92880
95362
|
const factory = REGISTRY[kind];
|
|
92881
95363
|
if (!factory) throw new Error(`export-hap: no mapper registered for kind '${kind}'`);
|
|
92882
95364
|
return factory;
|
|
92883
95365
|
}
|
|
92884
|
-
/**
|
|
92885
|
-
* Resolve the best-fit mapper kind. With Round 2, the operator just
|
|
92886
|
-
* picks a camera and the orchestrator does the rest — the single
|
|
92887
|
-
* `camera` kind is returned unconditionally. We keep the function
|
|
92888
|
-
* signature for backwards-compat with the addon's existing
|
|
92889
|
-
* `exposeDevice` flow and to leave room for future device types (NVR,
|
|
92890
|
-
* climate sensor, ...).
|
|
92891
|
-
*/
|
|
92892
|
-
function pickMapperKind(_capabilities) {
|
|
92893
|
-
return "camera";
|
|
92894
|
-
}
|
|
92895
95366
|
//#endregion
|
|
92896
95367
|
//#region src/mappers/builders/stream-hwaccel-memo.ts
|
|
92897
95368
|
/**
|
|
@@ -93029,8 +95500,8 @@ function syncStateToJson(map) {
|
|
|
93029
95500
|
//#endregion
|
|
93030
95501
|
//#region src/hap-export.addon.ts
|
|
93031
95502
|
/**
|
|
93032
|
-
* HomeKit (HAP) export addon — publishes
|
|
93033
|
-
*
|
|
95503
|
+
* HomeKit (HAP) export addon — publishes selected camstack devices as
|
|
95504
|
+
* standalone HomeKit accessories.
|
|
93034
95505
|
*
|
|
93035
95506
|
* Operator flow:
|
|
93036
95507
|
* 1. Install + enable the addon (hub-only, group `export-hap`).
|
|
@@ -93041,10 +95512,10 @@ function syncStateToJson(map) {
|
|
|
93041
95512
|
* 3. The setup URI (`X-HM://…`) is logged AND surfaced via the
|
|
93042
95513
|
* `getStatus` cap method so the wizard UI can render the QR.
|
|
93043
95514
|
* 4. Operator opens iOS Home → + → scan QR → enter pincode.
|
|
93044
|
-
* 5. Operator hits "Expose to HomeKit" on individual camstack
|
|
93045
|
-
*
|
|
93046
|
-
*
|
|
93047
|
-
*
|
|
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.
|
|
93048
95519
|
*
|
|
93049
95520
|
* Exception — the hap-nodejs library's own `HAPStorage` lives under
|
|
93050
95521
|
* `ctx.dataDir/hap-store/`. That directory is library-internal: the
|
|
@@ -93053,23 +95524,20 @@ function syncStateToJson(map) {
|
|
|
93053
95524
|
* I/O for addon-owned data") explicitly scopes itself to OUR own data;
|
|
93054
95525
|
* library-managed blobs are out of scope.
|
|
93055
95526
|
*
|
|
93056
|
-
*
|
|
93057
|
-
*
|
|
93058
|
-
*
|
|
93059
|
-
*
|
|
93060
|
-
*
|
|
93061
|
-
*
|
|
93062
|
-
*
|
|
93063
|
-
*
|
|
93064
|
-
*
|
|
93065
|
-
*
|
|
93066
|
-
*
|
|
93067
|
-
*
|
|
93068
|
-
*
|
|
93069
|
-
*
|
|
93070
|
-
* What's deferred to Round 3+: cam→browser audio Opus transcode,
|
|
93071
|
-
* intercom upload bridge, HomeKit Secure Video, recording, native
|
|
93072
|
-
* 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.
|
|
93073
95541
|
*/
|
|
93074
95542
|
var DEFAULT_DEVICE_SETTINGS = {
|
|
93075
95543
|
streamPreference: "auto",
|
|
@@ -93112,7 +95580,6 @@ var DEFAULT_CONFIG = {
|
|
|
93112
95580
|
fixedPin: "",
|
|
93113
95581
|
interfaceName: "",
|
|
93114
95582
|
ptzPulseMs: 400,
|
|
93115
|
-
hksvPreview: false,
|
|
93116
95583
|
identity: {
|
|
93117
95584
|
username: "",
|
|
93118
95585
|
pincode: "",
|
|
@@ -93228,7 +95695,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93228
95695
|
...setup ? { setup } : {}
|
|
93229
95696
|
};
|
|
93230
95697
|
},
|
|
93231
|
-
listSupportedDeviceKinds: async () => [...
|
|
95698
|
+
listSupportedDeviceKinds: async () => [...HAP_EXPORTABLE_DEVICE_TYPES],
|
|
93232
95699
|
listExposedDevices: async () => Array.from(this.exposed.entries()).map(([deviceId, m]) => {
|
|
93233
95700
|
const entry = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93234
95701
|
return {
|
|
@@ -93313,9 +95780,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93313
95780
|
log.debug("export-hap: device already exposed — refreshing capabilities");
|
|
93314
95781
|
await this.detachMapper(deviceId);
|
|
93315
95782
|
}
|
|
93316
|
-
const
|
|
93317
|
-
|
|
93318
|
-
|
|
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}`;
|
|
93319
95787
|
const previous = this.config.exposed.find((e) => e.deviceId === deviceId);
|
|
93320
95788
|
const baseEntry = carryForward({
|
|
93321
95789
|
deviceId,
|
|
@@ -93345,10 +95813,11 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93345
95813
|
async unexposeDevice(deviceId, options = {}) {
|
|
93346
95814
|
const numericId = Number.parseInt(deviceId, 10);
|
|
93347
95815
|
const log = this.ctx.logger.withTags({ deviceId: numericId });
|
|
95816
|
+
const mapperKind = this.findEntry(numericId)?.mapperKind ?? "camera";
|
|
93348
95817
|
await this.detachMapper(deviceId);
|
|
93349
95818
|
const next = this.config.exposed.filter((e) => e.deviceId !== deviceId);
|
|
93350
95819
|
if (next.length !== this.config.exposed.length) await this.updateGlobalSettings({ exposed: next });
|
|
93351
|
-
if (options.clearPairing !== false) clearPairingFiles(
|
|
95820
|
+
if (options.clearPairing !== false) clearPairingFiles(accessoryUuidFor(mapperKind, numericId), this.ctx.logger);
|
|
93352
95821
|
await this.forgetFingerprint(numericId);
|
|
93353
95822
|
log.info("export-hap: unexposed device");
|
|
93354
95823
|
}
|
|
@@ -93398,10 +95867,10 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93398
95867
|
/** Pending rebuild timers per deviceId — used to debounce. Cleared
|
|
93399
95868
|
* on detach so stale timers can't republish a removed accessory. */
|
|
93400
95869
|
pendingRebuildTimers = /* @__PURE__ */ new Map();
|
|
93401
|
-
/** Deterministic HAP accessory UUID for a device
|
|
93402
|
-
*
|
|
95870
|
+
/** Deterministic HAP accessory UUID for a device — the same one the
|
|
95871
|
+
* orchestrator built it with (`mappers/kind.ts`). */
|
|
93403
95872
|
hapAccessoryUuid(deviceId) {
|
|
93404
|
-
return
|
|
95873
|
+
return accessoryUuidFor(this.findEntry(deviceId)?.mapperKind ?? "camera", deviceId);
|
|
93405
95874
|
}
|
|
93406
95875
|
/**
|
|
93407
95876
|
* Export fingerprint of a device from its PERSISTED features + type
|
|
@@ -93560,31 +96029,79 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93560
96029
|
log.warn("export-hap: reconcile rebuild failed", { meta: { error: errMsg(err) } });
|
|
93561
96030
|
}
|
|
93562
96031
|
}
|
|
93563
|
-
|
|
93564
|
-
|
|
93565
|
-
|
|
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;
|
|
93566
96041
|
try {
|
|
93567
|
-
const device = await this.ctx.api.deviceManager?.getDevice.query({ deviceId
|
|
93568
|
-
if (device
|
|
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
|
+
};
|
|
93569
96048
|
} catch (err) {
|
|
93570
|
-
this.ctx.logger.withTags({ deviceId
|
|
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";
|
|
96073
|
+
try {
|
|
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;
|
|
96082
|
+
} catch (err) {
|
|
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;
|
|
93571
96088
|
}
|
|
93572
|
-
return `Device ${deviceId}`;
|
|
93573
96089
|
}
|
|
93574
96090
|
globalSettingsSchema() {
|
|
93575
96091
|
return this.schema({ sections: [{
|
|
93576
96092
|
id: "export-hap",
|
|
93577
96093
|
title: "HomeKit Export",
|
|
93578
|
-
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.",
|
|
93579
96095
|
columns: 1,
|
|
93580
96096
|
fields: [
|
|
93581
96097
|
this.field({
|
|
93582
96098
|
type: "text",
|
|
93583
96099
|
key: "bridgeName",
|
|
93584
|
-
label: "
|
|
93585
|
-
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.",
|
|
93586
96102
|
default: DEFAULT_CONFIG.bridgeName,
|
|
93587
|
-
requiresRestart: true
|
|
96103
|
+
requiresRestart: true,
|
|
96104
|
+
placement: { tab: "advanced" }
|
|
93588
96105
|
}),
|
|
93589
96106
|
this.field({
|
|
93590
96107
|
type: "number",
|
|
@@ -93620,21 +96137,13 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93620
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.",
|
|
93621
96138
|
default: DEFAULT_CONFIG.ptzPulseMs,
|
|
93622
96139
|
placement: { tab: "advanced" }
|
|
93623
|
-
}),
|
|
93624
|
-
this.field({
|
|
93625
|
-
type: "boolean",
|
|
93626
|
-
key: "hksvPreview",
|
|
93627
|
-
label: "HKSV Developer Preview (experimental)",
|
|
93628
|
-
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.",
|
|
93629
|
-
default: DEFAULT_CONFIG.hksvPreview,
|
|
93630
|
-
requiresRestart: true,
|
|
93631
|
-
placement: { tab: "advanced" }
|
|
93632
96140
|
})
|
|
93633
96141
|
]
|
|
93634
96142
|
}] });
|
|
93635
96143
|
}
|
|
93636
96144
|
async buildDeviceSettingsContribution(deviceId) {
|
|
93637
|
-
|
|
96145
|
+
const kind = await this.resolveExportKind(deviceId);
|
|
96146
|
+
if (kind === null) return null;
|
|
93638
96147
|
const entry = this.findEntry(deviceId);
|
|
93639
96148
|
const settings = entry?.settings ?? DEFAULT_DEVICE_SETTINGS;
|
|
93640
96149
|
const enabled = entry !== null;
|
|
@@ -93651,6 +96160,32 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93651
96160
|
} catch (err) {
|
|
93652
96161
|
this.ctx.logger.withTags({ deviceId }).debug("export-hap: setupURI failed for per-device contribution", { meta: { error: errMsg(err) } });
|
|
93653
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
|
+
}];
|
|
93654
96189
|
return {
|
|
93655
96190
|
tabs: [{
|
|
93656
96191
|
id: "export",
|
|
@@ -93661,7 +96196,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93661
96196
|
sections: [{
|
|
93662
96197
|
id: "export-hap",
|
|
93663
96198
|
title: "HomeKit Export",
|
|
93664
|
-
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.",
|
|
93665
96200
|
tab: "export",
|
|
93666
96201
|
columns: 1,
|
|
93667
96202
|
order: 10,
|
|
@@ -93677,7 +96212,7 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93677
96212
|
type: "qr-code",
|
|
93678
96213
|
key: "__hap-pair-qr",
|
|
93679
96214
|
label: paired ? "Pairing QR (already paired)" : "Pairing QR",
|
|
93680
|
-
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}.`,
|
|
93681
96216
|
value: qrValue,
|
|
93682
96217
|
size: 192,
|
|
93683
96218
|
alt: `HomeKit pairing QR for ${name}`,
|
|
@@ -93690,38 +96225,12 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93690
96225
|
type: "boolean",
|
|
93691
96226
|
key: enabledKey,
|
|
93692
96227
|
label: "Expose to HomeKit",
|
|
93693
|
-
description: "Toggle to publish this
|
|
96228
|
+
description: "Toggle to publish this device as a HomeKit accessory.",
|
|
93694
96229
|
style: "switch",
|
|
93695
96230
|
value: enabled,
|
|
93696
96231
|
immediate: true
|
|
93697
96232
|
},
|
|
93698
|
-
|
|
93699
|
-
type: "select",
|
|
93700
|
-
key: streamPreferenceKey,
|
|
93701
|
-
label: "Source stream (HomeKit)",
|
|
93702
|
-
description: "Which camstack profile slot HomeKit pulls. Auto = the broker picks the slot closest to 1080p at session start.",
|
|
93703
|
-
options: HAP_STREAM_PREFERENCE_OPTIONS,
|
|
93704
|
-
required: true,
|
|
93705
|
-
value: settings.streamPreference,
|
|
93706
|
-
showWhen: {
|
|
93707
|
-
field: enabledKey,
|
|
93708
|
-
equals: true
|
|
93709
|
-
},
|
|
93710
|
-
immediate: true
|
|
93711
|
-
},
|
|
93712
|
-
{
|
|
93713
|
-
type: "boolean",
|
|
93714
|
-
key: hksvKey,
|
|
93715
|
-
label: "HomeKit recording (Secure Video)",
|
|
93716
|
-
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).",
|
|
93717
|
-
style: "switch",
|
|
93718
|
-
value: resolveHksvRecording(settings),
|
|
93719
|
-
showWhen: {
|
|
93720
|
-
field: enabledKey,
|
|
93721
|
-
equals: true
|
|
93722
|
-
},
|
|
93723
|
-
immediate: true
|
|
93724
|
-
}
|
|
96233
|
+
...cameraFields
|
|
93725
96234
|
]
|
|
93726
96235
|
}]
|
|
93727
96236
|
};
|
|
@@ -93787,23 +96296,6 @@ var ExportHapAddon = class extends BaseAddon {
|
|
|
93787
96296
|
}
|
|
93788
96297
|
return { success: true };
|
|
93789
96298
|
}
|
|
93790
|
-
/**
|
|
93791
|
-
* Camera-only gate — HomeKit export only knows how to mirror cameras
|
|
93792
|
-
* today. Mirrors the snapshot addon's source-side filter so the
|
|
93793
|
-
* device-details page doesn't render an "Export" tab on lights /
|
|
93794
|
-
* switches / sensors.
|
|
93795
|
-
*/
|
|
93796
|
-
async isCameraDevice(deviceId) {
|
|
93797
|
-
const api = this.ctx.api;
|
|
93798
|
-
if (!api.deviceManager) return true;
|
|
93799
|
-
try {
|
|
93800
|
-
const dev = await api.deviceManager.getDevice.query({ deviceId });
|
|
93801
|
-
if (!dev) return true;
|
|
93802
|
-
return dev.type === DeviceType.Camera;
|
|
93803
|
-
} catch {
|
|
93804
|
-
return true;
|
|
93805
|
-
}
|
|
93806
|
-
}
|
|
93807
96299
|
findEntry(deviceId) {
|
|
93808
96300
|
const id = String(deviceId);
|
|
93809
96301
|
return this.config.exposed.find((e) => e.deviceId === id) ?? null;
|