@camstack/addon-provider-onvif 1.2.114 → 1.2.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +3461 -28
- package/dist/addon.mjs +3461 -28
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -5364,7 +5364,7 @@ var ZodIssueCode = {
|
|
|
5364
5364
|
var ZodFirstPartyTypeKind;
|
|
5365
5365
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5366
5366
|
//#endregion
|
|
5367
|
-
//#region ../types/dist/sleep-
|
|
5367
|
+
//#region ../types/dist/sleep-i3eUVc-d.mjs
|
|
5368
5368
|
/**
|
|
5369
5369
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5370
5370
|
* window to float samples (D455).
|
|
@@ -6506,6 +6506,17 @@ var CamProfileSchema = _enum([
|
|
|
6506
6506
|
"mid",
|
|
6507
6507
|
"low"
|
|
6508
6508
|
]);
|
|
6509
|
+
/**
|
|
6510
|
+
* Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
|
|
6511
|
+
*
|
|
6512
|
+
* `auto` is a REAL value, not an absent one — it means "resolve it by the rule
|
|
6513
|
+
* that consumer owns". It lives beside `CamProfile` because a rung is the only
|
|
6514
|
+
* operator-facing way to name a stream: a cam-stream id is broker-internal and
|
|
6515
|
+
* moves when a slot is reassigned, so a stored id silently stops meaning what
|
|
6516
|
+
* the operator picked. Today's one user is the audio analyser, whose rule is
|
|
6517
|
+
* "the lowest AVAILABLE native rung" (D537).
|
|
6518
|
+
*/
|
|
6519
|
+
var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
|
|
6509
6520
|
var CamStreamKindSchema = _enum([
|
|
6510
6521
|
"pull-rtsp",
|
|
6511
6522
|
"pull-rtmp",
|
|
@@ -6924,6 +6935,18 @@ function systemMethod(input, output, options) {
|
|
|
6924
6935
|
systemOnly: true
|
|
6925
6936
|
};
|
|
6926
6937
|
}
|
|
6938
|
+
/**
|
|
6939
|
+
* A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
|
|
6940
|
+
* on `InferProvider`. The `providerOptional: true` literal is what
|
|
6941
|
+
* `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
|
|
6942
|
+
* for when this is the honest answer and when it is a soft stub.
|
|
6943
|
+
*/
|
|
6944
|
+
function optionalMethod(input, output, options) {
|
|
6945
|
+
return {
|
|
6946
|
+
...method(input, output, options),
|
|
6947
|
+
providerOptional: true
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6927
6950
|
var StaticDirOutputSchema$1 = object({ staticDir: string() });
|
|
6928
6951
|
var VersionOutputSchema$1 = object({ version: string() });
|
|
6929
6952
|
method(_void(), StaticDirOutputSchema$1, { auth: "admin" }), method(_void(), VersionOutputSchema$1, { auth: "admin" });
|
|
@@ -7350,6 +7373,8 @@ var EVENT_OWNER_TYPES = [
|
|
|
7350
7373
|
* nothing failing until a caller asked.
|
|
7351
7374
|
*/
|
|
7352
7375
|
var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
|
|
7376
|
+
/** The same list as a Zod enum, for the cap input that carries it. */
|
|
7377
|
+
var MediaPresenceOwnerKindSchema = _enum([...EVENT_OWNER_TYPES, "track"]);
|
|
7353
7378
|
new Set(EVENT_OWNER_TYPES);
|
|
7354
7379
|
var EncodeProfileSchema = object({
|
|
7355
7380
|
video: object({
|
|
@@ -7722,6 +7747,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7722
7747
|
"device-audio",
|
|
7723
7748
|
"broker-audio",
|
|
7724
7749
|
"audio-analysis",
|
|
7750
|
+
"event-saving",
|
|
7725
7751
|
"recording",
|
|
7726
7752
|
"notifications"
|
|
7727
7753
|
]);
|
|
@@ -7757,10 +7783,20 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
7757
7783
|
var CameraSwitchUnavailableReasonSchema = _enum([
|
|
7758
7784
|
"no-provider",
|
|
7759
7785
|
"source-unreachable",
|
|
7760
|
-
"not-configured",
|
|
7761
7786
|
"device-disabled"
|
|
7762
7787
|
]);
|
|
7763
7788
|
/**
|
|
7789
|
+
* Something true about a switch that is nonetheless OFFERED.
|
|
7790
|
+
*
|
|
7791
|
+
* The distinction from {@link CameraSwitchUnavailableReasonSchema} is the
|
|
7792
|
+
* whole point: a reason replaces the control, a warning travels WITH it. The
|
|
7793
|
+
* privacy mask taught us the difference — greying the one button that starts
|
|
7794
|
+
* the feature teaches the operator the feature is broken, while a live toggle
|
|
7795
|
+
* that flips nothing teaches them the switch is broken. Neither is true; the
|
|
7796
|
+
* honest surface is a working toggle that says what is missing.
|
|
7797
|
+
*/
|
|
7798
|
+
var CameraSwitchWarningSchema = _enum(["no-privacy-zones"]);
|
|
7799
|
+
/**
|
|
7764
7800
|
* One switch, resolved for one camera.
|
|
7765
7801
|
*
|
|
7766
7802
|
* `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
|
|
@@ -7779,6 +7815,11 @@ var CameraSwitchSchema = object({
|
|
|
7779
7815
|
/** False = do not render a control. `unavailableReason` says why. */
|
|
7780
7816
|
available: boolean(),
|
|
7781
7817
|
unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
|
|
7818
|
+
/**
|
|
7819
|
+
* Present on an AVAILABLE switch only. Rendered beside the live control;
|
|
7820
|
+
* never a substitute for one.
|
|
7821
|
+
*/
|
|
7822
|
+
warning: CameraSwitchWarningSchema.optional(),
|
|
7782
7823
|
/** Current state. Meaningless when `available` is false — read it as `true`. */
|
|
7783
7824
|
enabled: boolean(),
|
|
7784
7825
|
authority: CameraSwitchAuthoritySchema
|
|
@@ -8196,11 +8237,31 @@ var RecordingBandTriggersSchema = RecordingTriggersSchema;
|
|
|
8196
8237
|
* band engine's `applies` rule). `start`/`end` are `HH:MM`; an `end <= start`
|
|
8197
8238
|
* span wraps past midnight (handled by the band engine).
|
|
8198
8239
|
*/
|
|
8240
|
+
/**
|
|
8241
|
+
* Whether a band's footage carries sound.
|
|
8242
|
+
*
|
|
8243
|
+
* The recorder has always attached with `audio: 'aac'`, so every band of every
|
|
8244
|
+
* camera recorded sound whether anyone wanted it or not. The broker already
|
|
8245
|
+
* serves the other answer — `audio: 'none'` takes the video-only `/muted`
|
|
8246
|
+
* restream — so this was a choice the product could always have offered.
|
|
8247
|
+
*
|
|
8248
|
+
* It belongs on the BAND rather than the camera because one camera can want
|
|
8249
|
+
* both: silent across the shop floor during opening hours, sound overnight.
|
|
8250
|
+
* And it applies to BOTH modes — an events band firing on motion has the same
|
|
8251
|
+
* question to answer as a continuous one.
|
|
8252
|
+
*/
|
|
8253
|
+
var RecordingBandAudioSchema = _enum(["on", "muted"]);
|
|
8199
8254
|
var RecordingBandSchema = object({
|
|
8200
8255
|
days: array(RecordingWeekdaySchema),
|
|
8201
8256
|
start: string().regex(HHMM),
|
|
8202
8257
|
end: string().regex(HHMM),
|
|
8203
8258
|
mode: RecordingBandModeSchema,
|
|
8259
|
+
/**
|
|
8260
|
+
* Sound, or silence. OPTIONAL and absent-means-`on`: every recording
|
|
8261
|
+
* written before this existed carried sound, and a band whose key is
|
|
8262
|
+
* missing must keep meaning what it meant. See {@link RecordingBandAudioSchema}.
|
|
8263
|
+
*/
|
|
8264
|
+
audio: RecordingBandAudioSchema.optional(),
|
|
8204
8265
|
triggers: RecordingBandTriggersSchema.optional(),
|
|
8205
8266
|
/**
|
|
8206
8267
|
* RETIRED (D381). A band no longer carries a pre-buffer of its own.
|
|
@@ -13246,6 +13307,210 @@ method(object({ integrationId: string() }), object({ filters: array(AdoptionFilt
|
|
|
13246
13307
|
auth: "admin"
|
|
13247
13308
|
});
|
|
13248
13309
|
/**
|
|
13310
|
+
* device-admin-link — "this device has a management page of its own, and here
|
|
13311
|
+
* is its address".
|
|
13312
|
+
*
|
|
13313
|
+
* ## Why this is not a `deviceConfig` cap
|
|
13314
|
+
*
|
|
13315
|
+
* There is nothing to edit. A `deviceConfig` cap (D14) exists so the framework
|
|
13316
|
+
* can DERIVE a settings form from `getOptions` + `getStatus` and route a flat
|
|
13317
|
+
* patch back through a setter; it costs a `builderId` reducer in
|
|
13318
|
+
* `device-config-contribution.ts` and a `*-config-schema.ts` beside it, and it
|
|
13319
|
+
* renders a form section. This cap answers ONE question with ONE read and
|
|
13320
|
+
* renders a button. Nothing about it is a form, so it carries no `deviceConfig`
|
|
13321
|
+
* block, no `settings`, no `runtimeState` and no reducer — exactly like
|
|
13322
|
+
* `reboot`, the other pure-RPC device-native cap.
|
|
13323
|
+
*
|
|
13324
|
+
* ## Absent, and the difference between "no page" and "we cannot say"
|
|
13325
|
+
*
|
|
13326
|
+
* The two are answered at DIFFERENT layers, on purpose:
|
|
13327
|
+
*
|
|
13328
|
+
* - **"We cannot say"** → the provider never registers the cap for that
|
|
13329
|
+
* device. A VeSync humidifier, a Petkit feeder, a Dreame vacuum and a Dreo
|
|
13330
|
+
* fan are reached only through a vendor cloud; there is no address to hand
|
|
13331
|
+
* out and no page to open. A Tuya plug, a Wyze camera and a Gree air
|
|
13332
|
+
* conditioner DO have a LAN IP, and still have no HTTP management page
|
|
13333
|
+
* behind it. None of them register, so `deviceManager.getBindings` never
|
|
13334
|
+
* lists the cap and no surface asks.
|
|
13335
|
+
* - **"This device has no page, and I know that"** → the provider registers
|
|
13336
|
+
* and `getAdminLink` returns `null`. This is the answer for a device whose
|
|
13337
|
+
* sibling DOES have a page: a Reolink battery camera reached over UDP by
|
|
13338
|
+
* `uid` with a blank `host`, an Ecowitt gateway configured in `listener`
|
|
13339
|
+
* transport, a Home Assistant broker authenticated by supervisor token
|
|
13340
|
+
* (which carries no `baseUrl` at all).
|
|
13341
|
+
*
|
|
13342
|
+
* Both draw NOTHING. A button that opens a browser error is worse than no
|
|
13343
|
+
* button, and D62 is the same rule from the other side: an off switch is
|
|
13344
|
+
* reported off, never made to look broken. There is no third state where the
|
|
13345
|
+
* UI renders a disabled button "because the device might have a page".
|
|
13346
|
+
*
|
|
13347
|
+
* ## The URL never carries credentials
|
|
13348
|
+
*
|
|
13349
|
+
* Not in userinfo, not in a query string. Every provider builds through
|
|
13350
|
+
* `buildDeviceAdminUrl` (`device-admin-link-url.ts`), which takes host, port,
|
|
13351
|
+
* scheme and path as separate arguments — there is no parameter a secret could
|
|
13352
|
+
* arrive in — and re-checks its own output for the `scheme://user:pass@` shape
|
|
13353
|
+
* that `scripts/check-no-credential-urls-in-fixtures.ts` bans from recorded
|
|
13354
|
+
* output. `scripts/check-admin-link-builder-is-the-only-url-source.ts` is what
|
|
13355
|
+
* keeps providers from hand-rolling one anyway.
|
|
13356
|
+
*
|
|
13357
|
+
* This matters here more than anywhere else in the repo, because every provider
|
|
13358
|
+
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
13359
|
+
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
13360
|
+
* and `http://admin:hunter2@192.168.50.139/` is a URL a browser accepts. The
|
|
13361
|
+
* camera's own page will ask for its own login. That is correct, and pre-
|
|
13362
|
+
* filling it is the operator's business, not ours.
|
|
13363
|
+
*
|
|
13364
|
+
* ## It is a LAN fact
|
|
13365
|
+
*
|
|
13366
|
+
* The URL addresses the device where the NODE can see it. It is not proxied,
|
|
13367
|
+
* not made reachable from outside, and not sent anywhere. A surface renders it
|
|
13368
|
+
* as a link the operator's own browser follows, on the operator's own network,
|
|
13369
|
+
* or renders nothing.
|
|
13370
|
+
*/
|
|
13371
|
+
/**
|
|
13372
|
+
* Whose page is it. The distinction is for the OPERATOR, who needs to know
|
|
13373
|
+
* before clicking whether he is about to land on a camera's own web server or
|
|
13374
|
+
* inside Home Assistant.
|
|
13375
|
+
*/
|
|
13376
|
+
var AdminLinkTargetEnum = _enum(["device", "integration"]);
|
|
13377
|
+
var DeviceAdminLinkSchema = object({
|
|
13378
|
+
/**
|
|
13379
|
+
* Absolute `http(s)://` URL. Built by `buildDeviceAdminUrl` and therefore
|
|
13380
|
+
* free of userinfo and of any credential-shaped query key.
|
|
13381
|
+
*/
|
|
13382
|
+
url: string(),
|
|
13383
|
+
/**
|
|
13384
|
+
* What the surface calls it — "Web UI", "Home Assistant", "UniFi controller".
|
|
13385
|
+
* The PROVIDER names it, because only the provider knows what the page is;
|
|
13386
|
+
* a UI that invented the label from the addon id would call the Home
|
|
13387
|
+
* Assistant device page "Provider Homeassistant".
|
|
13388
|
+
*/
|
|
13389
|
+
label: string(),
|
|
13390
|
+
target: AdminLinkTargetEnum,
|
|
13391
|
+
/**
|
|
13392
|
+
* Host the URL points at, without scheme, port or path — for the tooltip, so
|
|
13393
|
+
* an operator can see WHERE the button goes before he follows it. Redundant
|
|
13394
|
+
* with `url` by construction; carried separately so no surface has to parse
|
|
13395
|
+
* a URL to show it.
|
|
13396
|
+
*/
|
|
13397
|
+
host: string()
|
|
13398
|
+
});
|
|
13399
|
+
var deviceAdminLinkCapability = {
|
|
13400
|
+
name: "device-admin-link",
|
|
13401
|
+
scope: "device",
|
|
13402
|
+
deviceNative: true,
|
|
13403
|
+
mode: "singleton",
|
|
13404
|
+
methods: {
|
|
13405
|
+
/**
|
|
13406
|
+
* The device's management page, or `null` when this device has none.
|
|
13407
|
+
*
|
|
13408
|
+
* `auth: 'admin'` deliberately. This is administration, not actuation —
|
|
13409
|
+
* the same bucket as `reboot` and `camera-credentials`, and explicitly NOT
|
|
13410
|
+
* the actuation set `scripts/check-actuation-not-admin.ts` protects (D403).
|
|
13411
|
+
* The URL is also a statement about the LAN, which a household member with
|
|
13412
|
+
* a `view` grant on a light has no reason to be handed.
|
|
13413
|
+
*
|
|
13414
|
+
* The surfaces gate on the QUERY, never on a role they guessed: a caller
|
|
13415
|
+
* without the right loses the query and draws nothing, which is the same
|
|
13416
|
+
* thing a device with no page draws. There is no path on which a button
|
|
13417
|
+
* appears and then fails — the D403 failure mode, from the other end.
|
|
13418
|
+
*/
|
|
13419
|
+
getAdminLink: method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" }) }
|
|
13420
|
+
};
|
|
13421
|
+
/**
|
|
13422
|
+
* Query keys that may never appear on an admin link. A credential smuggled as
|
|
13423
|
+
* `?password=` is the same leak as userinfo, in a form the userinfo check
|
|
13424
|
+
* cannot see: it survives copy-paste, referrer headers and proxy logs
|
|
13425
|
+
* identically.
|
|
13426
|
+
*/
|
|
13427
|
+
var CREDENTIAL_QUERY_KEYS = [
|
|
13428
|
+
"password",
|
|
13429
|
+
"passwd",
|
|
13430
|
+
"pwd",
|
|
13431
|
+
"pass",
|
|
13432
|
+
"user",
|
|
13433
|
+
"username",
|
|
13434
|
+
"usr",
|
|
13435
|
+
"login",
|
|
13436
|
+
"token",
|
|
13437
|
+
"access_token",
|
|
13438
|
+
"auth",
|
|
13439
|
+
"authorization",
|
|
13440
|
+
"apikey",
|
|
13441
|
+
"api_key",
|
|
13442
|
+
"secret",
|
|
13443
|
+
"credential",
|
|
13444
|
+
"credentials",
|
|
13445
|
+
"session",
|
|
13446
|
+
"sessionid",
|
|
13447
|
+
"key"
|
|
13448
|
+
];
|
|
13449
|
+
/**
|
|
13450
|
+
* The same userinfo shape `scripts/check-no-credential-urls-in-fixtures.ts`
|
|
13451
|
+
* scans recorded fixtures for, applied to what we are about to EMIT. Kept
|
|
13452
|
+
* structurally identical on purpose: a URL this function returns is a URL that
|
|
13453
|
+
* guard would pass.
|
|
13454
|
+
*/
|
|
13455
|
+
var CREDENTIAL_URL = /[a-z][a-z0-9+.-]*:\/\/[^/\s:@'"]+:[^/\s:@'"]+@/i;
|
|
13456
|
+
/** A host that is safe to place in an authority component verbatim. */
|
|
13457
|
+
var PLAIN_HOST = /^[a-zA-Z0-9._-]+$/;
|
|
13458
|
+
/** A bracketed IPv6 literal, the only other authority form we emit. */
|
|
13459
|
+
var IPV6_HOST = /^\[[0-9a-fA-F:.]+\]$/;
|
|
13460
|
+
function defaultPortFor(scheme) {
|
|
13461
|
+
return scheme === "https" ? 443 : 80;
|
|
13462
|
+
}
|
|
13463
|
+
/**
|
|
13464
|
+
* Build the admin-page URL, or refuse by name.
|
|
13465
|
+
*
|
|
13466
|
+
* The refusal is never thrown: a provider answering "no page for this device"
|
|
13467
|
+
* is an ordinary answer (`getAdminLink` → `null`), and a throw here would turn
|
|
13468
|
+
* a missing host on one device into a failed query for the whole surface.
|
|
13469
|
+
*/
|
|
13470
|
+
function buildDeviceAdminUrl(parts) {
|
|
13471
|
+
const host = parts.host.trim();
|
|
13472
|
+
if (host.length === 0) return {
|
|
13473
|
+
ok: false,
|
|
13474
|
+
reason: "empty-host"
|
|
13475
|
+
};
|
|
13476
|
+
if (host.includes("@")) return {
|
|
13477
|
+
ok: false,
|
|
13478
|
+
reason: "host-carries-userinfo"
|
|
13479
|
+
};
|
|
13480
|
+
if (!PLAIN_HOST.test(host) && !IPV6_HOST.test(host)) return {
|
|
13481
|
+
ok: false,
|
|
13482
|
+
reason: "host-not-plain"
|
|
13483
|
+
};
|
|
13484
|
+
if (parts.port !== null) {
|
|
13485
|
+
if (!Number.isInteger(parts.port) || parts.port < 1 || parts.port > 65535) return {
|
|
13486
|
+
ok: false,
|
|
13487
|
+
reason: "port-out-of-range"
|
|
13488
|
+
};
|
|
13489
|
+
}
|
|
13490
|
+
if (!parts.path.startsWith("/")) return {
|
|
13491
|
+
ok: false,
|
|
13492
|
+
reason: "path-not-absolute"
|
|
13493
|
+
};
|
|
13494
|
+
const query = parts.query ?? {};
|
|
13495
|
+
for (const key of Object.keys(query)) if (CREDENTIAL_QUERY_KEYS.includes(key.toLowerCase())) return {
|
|
13496
|
+
ok: false,
|
|
13497
|
+
reason: "credential-query-key"
|
|
13498
|
+
};
|
|
13499
|
+
const authority = parts.port === null || parts.port === defaultPortFor(parts.scheme) ? host : `${host}:${String(parts.port)}`;
|
|
13500
|
+
const search = new URLSearchParams();
|
|
13501
|
+
for (const [key, value] of Object.entries(query)) search.append(key, value);
|
|
13502
|
+
const suffix = search.size > 0 ? `?${search.toString()}` : "";
|
|
13503
|
+
const url = `${parts.scheme}://${authority}${parts.path}${suffix}`;
|
|
13504
|
+
if (CREDENTIAL_URL.test(url)) return {
|
|
13505
|
+
ok: false,
|
|
13506
|
+
reason: "userinfo-in-result"
|
|
13507
|
+
};
|
|
13508
|
+
return {
|
|
13509
|
+
ok: true,
|
|
13510
|
+
url
|
|
13511
|
+
};
|
|
13512
|
+
}
|
|
13513
|
+
/**
|
|
13249
13514
|
* `device-export` — collection cap for addons that export camstack
|
|
13250
13515
|
* devices to external ecosystems (HomeAssistant via MQTT discovery,
|
|
13251
13516
|
* HomeKit/HAP, Alexa Smart Home, …).
|
|
@@ -19934,6 +20199,7 @@ var MediaFileKindEnum = _enum([
|
|
|
19934
20199
|
"keyFrameSmall",
|
|
19935
20200
|
"thumbnailSmall",
|
|
19936
20201
|
"confirmationCrop",
|
|
20202
|
+
"debugCrop",
|
|
19937
20203
|
"sceneFrame"
|
|
19938
20204
|
]);
|
|
19939
20205
|
/**
|
|
@@ -21013,7 +21279,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
21013
21279
|
ownerType: EventOwnerTypeSchema,
|
|
21014
21280
|
eventId: number().int(),
|
|
21015
21281
|
deviceId: number()
|
|
21016
|
-
}), array(MediaFileInfoSchema).readonly()), method(
|
|
21282
|
+
}), array(MediaFileInfoSchema).readonly()), method(object({
|
|
21283
|
+
deviceId: number(),
|
|
21284
|
+
ownerKind: MediaPresenceOwnerKindSchema,
|
|
21285
|
+
ownerIds: array(string()).max(5e3)
|
|
21286
|
+
}), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
21017
21287
|
kind: "mutation",
|
|
21018
21288
|
auth: "admin"
|
|
21019
21289
|
}), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
|
|
@@ -21598,6 +21868,18 @@ var RunnerCameraConfigSchema = object({
|
|
|
21598
21868
|
motionStreamId: string(),
|
|
21599
21869
|
detectionStreamId: string(),
|
|
21600
21870
|
/**
|
|
21871
|
+
* Which rung this camera's audio analysis listens on — `auto` (the lowest
|
|
21872
|
+
* AVAILABLE native rung) or an explicit one.
|
|
21873
|
+
*
|
|
21874
|
+
* Not consumed by the runner: carried here so it travels with the camera's
|
|
21875
|
+
* config the way `audioMotionWindowMs` does, and so the orchestrator's
|
|
21876
|
+
* watchdog resolves the same stream the audio subscription did. OPTIONAL
|
|
21877
|
+
* with no default — absent means `auto`, which is what every camera written
|
|
21878
|
+
* before this field had, and an absent optional adds nothing to the
|
|
21879
|
+
* generated cap-input defaults (D537).
|
|
21880
|
+
*/
|
|
21881
|
+
audioStreamPick: AudioStreamPickSchema.optional(),
|
|
21882
|
+
/**
|
|
21601
21883
|
* Motion sources that can activate the pipeline. At least one must
|
|
21602
21884
|
* be present. Multiple sources OR-ed: any one firing arms the
|
|
21603
21885
|
* cooldown timer and transitions watching → active. The same
|
|
@@ -22668,10 +22950,9 @@ var ServerBootModeSchema = _enum([
|
|
|
22668
22950
|
* - `idle` / `checking` / `staging` — steady / in-flight registry work.
|
|
22669
22951
|
* - `pending-restart` — a version is staged and the node has NOT yet
|
|
22670
22952
|
* restarted onto it (still running the OLD version).
|
|
22671
|
-
* - `awaiting-confirmation` —
|
|
22672
|
-
*
|
|
22673
|
-
*
|
|
22674
|
-
* manually restarted, or the probation boot auto-rolls-back.
|
|
22953
|
+
* - `awaiting-confirmation` — RETIRED with the probation boot (single-copy
|
|
22954
|
+
* collapse, 2026-07-18). Kept in the enum so an older client still parses;
|
|
22955
|
+
* no provider emits it.
|
|
22675
22956
|
*/
|
|
22676
22957
|
var ServerUpdateStateSchema = _enum([
|
|
22677
22958
|
"idle",
|
|
@@ -22760,12 +23041,35 @@ var ServerRollbackInfoSchema = object({
|
|
|
22760
23041
|
var ServerPackageStatusSchema = object({
|
|
22761
23042
|
/** Root package name (`@camstack/server` on the hub). */
|
|
22762
23043
|
packageName: string(),
|
|
22763
|
-
/**
|
|
23044
|
+
/**
|
|
23045
|
+
* Version of the code THIS PROCESS loaded — captured once, at boot, from the
|
|
23046
|
+
* package.json of the copy the code was loaded from, and never re-read. A
|
|
23047
|
+
* copy replaced on disk under a live process must not move this number;
|
|
23048
|
+
* that divergence is what `installedVersion` is for. Before 2026-09-20 this
|
|
23049
|
+
* was a lazy disk read that would have followed such a swap silently.
|
|
23050
|
+
*/
|
|
22764
23051
|
runningVersion: string().nullable(),
|
|
22765
23052
|
/** Node.js runtime version the node's process runs on (`process.versions.node`). */
|
|
22766
23053
|
nodeRuntimeVersion: string().nullable(),
|
|
22767
23054
|
/** Active data-dir root version; null when booted from seed/workspace. */
|
|
22768
23055
|
activeVersion: string().nullable(),
|
|
23056
|
+
/**
|
|
23057
|
+
* Version of the root package in the data-root `current/` copy RIGHT NOW —
|
|
23058
|
+
* what the next boot would load if nothing is staged. Normally equal to
|
|
23059
|
+
* `runningVersion`; differs only when the copy on disk changed under the
|
|
23060
|
+
* live process. `null` when there is no data-root copy (baked seed or
|
|
23061
|
+
* workspace boot). Optional for version skew: an older provider omits it.
|
|
23062
|
+
*/
|
|
23063
|
+
installedVersion: string().nullable().optional(),
|
|
23064
|
+
/**
|
|
23065
|
+
* True when the code that would run after a restart is NOT the code running
|
|
23066
|
+
* now: a version is staged (`pendingVersion`), or `installedVersion` differs
|
|
23067
|
+
* from `runningVersion`. Derived on every read, never stored. This is the
|
|
23068
|
+
* one field that lets an operator or a script say "1.2.353 is installed and
|
|
23069
|
+
* will run after a restart" instead of reading it off a number that means
|
|
23070
|
+
* "installed". Optional for version skew.
|
|
23071
|
+
*/
|
|
23072
|
+
restartRequired: boolean().optional(),
|
|
22769
23073
|
/** N-1 version kept for rollback; null when no previous version exists. */
|
|
22770
23074
|
previousVersion: string().nullable(),
|
|
22771
23075
|
/** Version of the immutable baked seed closure (image fallback). */
|
|
@@ -22800,19 +23104,71 @@ var ServerPackageStatusSchema = object({
|
|
|
22800
23104
|
});
|
|
22801
23105
|
var ServerUpdateCheckResultSchema = object({
|
|
22802
23106
|
packageName: string(),
|
|
23107
|
+
/** Same meaning as on the status payload: what THIS process loaded. */
|
|
22803
23108
|
runningVersion: string().nullable(),
|
|
22804
23109
|
latestVersion: string().nullable(),
|
|
23110
|
+
/**
|
|
23111
|
+
* `latestVersion` is newer than `runningVersion` AND is not the version
|
|
23112
|
+
* already staged on this node. A staged-and-waiting version is not "an
|
|
23113
|
+
* update available" — applying it again is refused (`already-staged`), and
|
|
23114
|
+
* a button that announced "updating…" on this boolean then got refused
|
|
23115
|
+
* would be lying. It is reported through `pendingVersion` instead.
|
|
23116
|
+
*/
|
|
22805
23117
|
updateAvailable: boolean(),
|
|
22806
23118
|
checkedAtMs: number(),
|
|
22807
23119
|
/** Non-null when the registry lookup failed (offline, bad registry, …). */
|
|
22808
|
-
error: string().nullable()
|
|
23120
|
+
error: string().nullable(),
|
|
23121
|
+
/**
|
|
23122
|
+
* Version staged on this node and awaiting its restart, when one is. Before
|
|
23123
|
+
* 2026-09-20 a check answered `{runningVersion, updateAvailable:false}` for a
|
|
23124
|
+
* node whose staged closure had never booted, and the caller had no way to
|
|
23125
|
+
* see the difference. Optional for version skew.
|
|
23126
|
+
*/
|
|
23127
|
+
pendingVersion: string().nullable().optional(),
|
|
23128
|
+
/** See the status payload: staged, or installed-on-disk ≠ running. Optional for skew. */
|
|
23129
|
+
restartRequired: boolean().optional()
|
|
22809
23130
|
});
|
|
23131
|
+
/**
|
|
23132
|
+
* What an action DID — the discriminant `accepted` cannot carry. Before
|
|
23133
|
+
* 2026-09-20 `applyServerUpdate` answered `accepted:false` both for a refusal
|
|
23134
|
+
* where nothing was wrong (already current, already staged) and for a staging
|
|
23135
|
+
* install that died on `npm error code ECONNRESET`; the only difference was
|
|
23136
|
+
* prose in `message`, and an operator restarted a container on the strength of
|
|
23137
|
+
* it, booting the previous version.
|
|
23138
|
+
*
|
|
23139
|
+
* - `staged` — the closure is installed and validated; the node
|
|
23140
|
+
* restarts to apply it (`restarting:true`). NOTHING
|
|
23141
|
+
* runs the new code until that restart completes.
|
|
23142
|
+
* - `restart-scheduled` — a plain, version-preserving restart was accepted.
|
|
23143
|
+
* - `already-current` — the target IS the running version; nothing done.
|
|
23144
|
+
* - `refused` — dropped by a gate; `reason` names which.
|
|
23145
|
+
* - `failed` — attempted and did not land; `error` says why.
|
|
23146
|
+
*/
|
|
23147
|
+
var ServerUpdateOutcomeSchema = _enum([
|
|
23148
|
+
"staged",
|
|
23149
|
+
"restart-scheduled",
|
|
23150
|
+
"already-current",
|
|
23151
|
+
"refused",
|
|
23152
|
+
"failed"
|
|
23153
|
+
]);
|
|
22810
23154
|
var ServerUpdateActionResultSchema = object({
|
|
22811
23155
|
accepted: boolean(),
|
|
22812
23156
|
targetVersion: string().nullable(),
|
|
22813
23157
|
/** True when a graceful restart was scheduled to apply the change. */
|
|
22814
23158
|
restarting: boolean(),
|
|
22815
|
-
message: string()
|
|
23159
|
+
message: string(),
|
|
23160
|
+
/** See {@link ServerUpdateOutcomeSchema}. Optional for version skew. */
|
|
23161
|
+
outcome: ServerUpdateOutcomeSchema.optional(),
|
|
23162
|
+
/**
|
|
23163
|
+
* Stable, greppable cause for `refused` / `failed` (the engine's
|
|
23164
|
+
* `UpdateRefusalReason`, `staging-failed`, …). `null` on success.
|
|
23165
|
+
*/
|
|
23166
|
+
reason: string().nullable().optional(),
|
|
23167
|
+
/**
|
|
23168
|
+
* The failure itself, verbatim enough to act on (the npm output for a
|
|
23169
|
+
* staging install). Non-null only for `failed`.
|
|
23170
|
+
*/
|
|
23171
|
+
error: string().nullable().optional()
|
|
22816
23172
|
});
|
|
22817
23173
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
22818
23174
|
kind: "mutation",
|
|
@@ -24025,6 +24381,18 @@ method(VectorDeclareIndexInputSchema, _void(), {
|
|
|
24025
24381
|
kind: "mutation",
|
|
24026
24382
|
auth: "admin"
|
|
24027
24383
|
}), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
|
|
24384
|
+
_enum([
|
|
24385
|
+
"queue-full",
|
|
24386
|
+
"camera-backoff",
|
|
24387
|
+
"sleeping",
|
|
24388
|
+
"camera-refused",
|
|
24389
|
+
"no-keyframe",
|
|
24390
|
+
"no-catalog-row",
|
|
24391
|
+
"unsupported",
|
|
24392
|
+
"unknown-device",
|
|
24393
|
+
"uid-missing"
|
|
24394
|
+
]);
|
|
24395
|
+
_enum(["deferred", "final"]);
|
|
24028
24396
|
var ClipSchema = object({
|
|
24029
24397
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
24030
24398
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -24099,29 +24467,455 @@ var ClipSchema = object({
|
|
|
24099
24467
|
holes: array(object({
|
|
24100
24468
|
startMs: number(),
|
|
24101
24469
|
endMs: number()
|
|
24102
|
-
})).optional()
|
|
24470
|
+
})).optional(),
|
|
24471
|
+
/**
|
|
24472
|
+
* A URL that MAY produce this clip's still, asked only for rows actually on
|
|
24473
|
+
* screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
|
|
24474
|
+
* (a JPEG is already on disk), this one is an offer. The route answers 200
|
|
24475
|
+
* with the image, or 204 with `x-camstack-reason` when it could not mint one
|
|
24476
|
+
* — a surface latches that refusal to the instant rather than retrying.
|
|
24477
|
+
*
|
|
24478
|
+
* Never both: a clip with a vouched `thumbnail` needs no mint.
|
|
24479
|
+
*/
|
|
24480
|
+
thumbnailMint: string().optional(),
|
|
24481
|
+
/**
|
|
24482
|
+
* Why no still will be produced for this clip RIGHT NOW — set when the
|
|
24483
|
+
* provider already knows, so the surface draws the glyph and the reason
|
|
24484
|
+
* instead of firing a mint that cannot succeed.
|
|
24485
|
+
*
|
|
24486
|
+
* `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
|
|
24487
|
+
* `camera-refused` — the camera answered the CoverPreview with a refusal.
|
|
24488
|
+
* `no-keyframe` — the window holds no decodable I-frame (an event that sits
|
|
24489
|
+
* inside no file is the measured case).
|
|
24490
|
+
* `unsupported` — this source cannot mint stills at all.
|
|
24491
|
+
*/
|
|
24492
|
+
thumbnailUnavailable: object({ reason: _enum([
|
|
24493
|
+
"sleeping",
|
|
24494
|
+
"camera-refused",
|
|
24495
|
+
"no-keyframe",
|
|
24496
|
+
"unsupported"
|
|
24497
|
+
]) }).optional(),
|
|
24498
|
+
/**
|
|
24499
|
+
* When the catalog this row came from was last CONFIRMED against the device.
|
|
24500
|
+
* Absent means "this row was read live". A persisted catalog served while a
|
|
24501
|
+
* camera sleeps carries the age it really has — a cached list is never drawn
|
|
24502
|
+
* as current (D549 13).
|
|
24503
|
+
*/
|
|
24504
|
+
catalogAsOf: number().optional(),
|
|
24505
|
+
/**
|
|
24506
|
+
* The camera's OWN type strings for this clip, all of them, unmapped
|
|
24507
|
+
* (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
|
|
24508
|
+
* because a firmware inventing a type must not vanish: the mapping into our
|
|
24509
|
+
* filter vocabulary is lossy on purpose and this is the lossless copy
|
|
24510
|
+
* (D549 20).
|
|
24511
|
+
*/
|
|
24512
|
+
nativeTypes: array(string()).optional(),
|
|
24513
|
+
/**
|
|
24514
|
+
* What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
|
|
24515
|
+
* travels BESIDE a class, never instead of one, and the class filter ignores
|
|
24516
|
+
* it: "a person crossed a line" is still a person (D549 20).
|
|
24517
|
+
*/
|
|
24518
|
+
behaviours: array(string()).optional(),
|
|
24519
|
+
/**
|
|
24520
|
+
* The same recording as two files — the sub twin (what the row and its
|
|
24521
|
+
* thumbnail are) and its main twin, matched at listing time so a quality
|
|
24522
|
+
* change never re-searches the camera (D549 15). `getClipPlayback`'s
|
|
24523
|
+
* `profile` picks between them: `low | mid` → sub, `high` → main.
|
|
24524
|
+
*/
|
|
24525
|
+
streams: object({
|
|
24526
|
+
sub: object({
|
|
24527
|
+
id: string(),
|
|
24528
|
+
bytes: number().optional()
|
|
24529
|
+
}).optional(),
|
|
24530
|
+
main: object({
|
|
24531
|
+
id: string(),
|
|
24532
|
+
bytes: number().optional()
|
|
24533
|
+
}).optional()
|
|
24534
|
+
}).optional(),
|
|
24535
|
+
/** The camera says it holds a sub-stream copy of this recording. */
|
|
24536
|
+
supportSub: boolean().optional(),
|
|
24537
|
+
/**
|
|
24538
|
+
* Whether this row has bytes behind it. ABSENT means yes — every clip that
|
|
24539
|
+
* IS a file is playable, and only a source that lists EVENTS can produce a
|
|
24540
|
+
* row with nothing to play (the measured case: 1 of 29 hub events on 3628
|
|
24541
|
+
* fell inside no file). Such a row is shown, never dropped and never offered
|
|
24542
|
+
* as playable-then-failing (D549 19).
|
|
24543
|
+
*/
|
|
24544
|
+
playable: boolean().optional(),
|
|
24545
|
+
/** Why {@link playable} is false, verbatim (`no-file-for-window`). */
|
|
24546
|
+
unplayableReason: string().optional(),
|
|
24547
|
+
/**
|
|
24548
|
+
* This clip is STILL BEING WRITTEN, so {@link ClipSchema.timeRange}`.endMs`
|
|
24549
|
+
* is not its end.
|
|
24550
|
+
*
|
|
24551
|
+
* Absent — the common case — means the row is closed and its `endMs` is the
|
|
24552
|
+
* end of the recording. Present and `true` means the source told us the file
|
|
24553
|
+
* has no end yet, and the `endMs` we carry is whatever the camera's index
|
|
24554
|
+
* entry happened to hold: on a Reolink E1 Outdoor PoE (592, measured
|
|
24555
|
+
* 2026-09-20) the newest file `…_192927_000000_…_0.mp4` reported an `endTime`
|
|
24556
|
+
* of 19:29:56 and STILL reported it eight minutes later, while the file went
|
|
24557
|
+
* on growing. A surface that drew 29 s there was lying about a clip that
|
|
24558
|
+
* plays for minutes, on the one row the operator looks at first.
|
|
24559
|
+
*
|
|
24560
|
+
* `endMs` is deliberately still a number: it is the best bound anything has
|
|
24561
|
+
* for a mint window or a byte fetch, and every consumer already requires it.
|
|
24562
|
+
* This flag says what it is WORTH, and a duration is not drawn from it.
|
|
24563
|
+
*/
|
|
24564
|
+
inProgress: boolean().optional()
|
|
24103
24565
|
});
|
|
24104
24566
|
var ClipPlaybackSchema = object({
|
|
24105
|
-
/**
|
|
24567
|
+
/**
|
|
24568
|
+
* A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
|
|
24569
|
+
* says what kind — an HLS master playlist for a recording-derived clip, a
|
|
24570
|
+
* progressive MP4 for a native file the vendor addon muxed and serves with
|
|
24571
|
+
* `Range`. The URL is NOT a credential: both planes are registered
|
|
24572
|
+
* `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
|
|
24573
|
+
* the session cookie, so nothing is appended to it (D549 22).
|
|
24574
|
+
*/
|
|
24106
24575
|
playbackUrl: string(),
|
|
24576
|
+
/**
|
|
24577
|
+
* How to play {@link playbackUrl}. Absent means `hls` — the shape every
|
|
24578
|
+
* existing consumer already assumes. A player that branches on this is the
|
|
24579
|
+
* one change a native clip needs; a player that ignores it will hand an MP4
|
|
24580
|
+
* to hls.js and fail parsing it as a manifest.
|
|
24581
|
+
*/
|
|
24582
|
+
format: _enum(["hls", "mp4"]).optional(),
|
|
24583
|
+
/**
|
|
24584
|
+
* Which twin was actually served. A clip that holds only one stream answers
|
|
24585
|
+
* with the one it has, and the surface SAYS so — a missing main twin is
|
|
24586
|
+
* never served silently as if it were the asked-for quality (D549 15).
|
|
24587
|
+
*/
|
|
24588
|
+
served: CamProfileSchema.optional(),
|
|
24107
24589
|
/** Optional LAN/remote alternates for the same clip. */
|
|
24108
24590
|
playbackEndpoints: array(string()).optional(),
|
|
24109
24591
|
token: string().optional()
|
|
24110
24592
|
});
|
|
24593
|
+
/**
|
|
24594
|
+
* Why a source cannot answer right now — per SOURCE, never fleet-wide.
|
|
24595
|
+
*
|
|
24596
|
+
* `ok` is the only state whose clip list may be read as complete. The other
|
|
24597
|
+
* three exist because an empty list from a sleeping camera reads as "this
|
|
24598
|
+
* camera has no recordings", which is the defect this whole line of work is
|
|
24599
|
+
* about: a battery camera nobody woke (`sleeping`), a camera that could not be
|
|
24600
|
+
* reached at all (`unreachable`, also what a provider that THREW reports), a
|
|
24601
|
+
* camera whose SD card is not mounted (`no-storage`, measured on 640 —
|
|
24602
|
+
* `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
|
|
24603
|
+
* camera whose OWN index disagrees with its OWN calendar (`index-empty`,
|
|
24604
|
+
* measured on 618: the calendar marks 3–20 September, days 6–20 list zero
|
|
24605
|
+
* files on both streams and both filters, with 177 GB free). That last one is
|
|
24606
|
+
* a defect ON THE CAMERA, and the only honest thing a provider can do is say
|
|
24607
|
+
* which days it asked for and got nothing — "no clips" would be a lie about a
|
|
24608
|
+
* card that is full of them.
|
|
24609
|
+
*/
|
|
24610
|
+
/**
|
|
24611
|
+
* The operator's authorisation to WAKE a sleeping camera for one clip read.
|
|
24612
|
+
*
|
|
24613
|
+
* ONE value, absent by default, and it is a `force`-shaped operator signal in
|
|
24614
|
+
* exactly the sense `snapshot-wake-gate.ts` uses the word: *"`snapshot.getSnapshot`'s
|
|
24615
|
+
* `force` flag, and nothing else… a background caller must never set it…
|
|
24616
|
+
* Stale but honest beats woken"*. A scheduler, a retry, a reconcile and a
|
|
24617
|
+
* prefetch never set it; a surface sets it only behind the same confirm the
|
|
24618
|
+
* "Wake and refresh" gesture uses, and it is refused below 15 % battery
|
|
24619
|
+
* exactly as that gesture is.
|
|
24620
|
+
*
|
|
24621
|
+
* The gate is decided BEFORE `getApi()`, because on UDP the login IS the wake
|
|
24622
|
+
* (D549) — a read that opened a session and then checked would have woken the
|
|
24623
|
+
* camera to find out it was not allowed to.
|
|
24624
|
+
*
|
|
24625
|
+
* **One authorised yes is one wake.** `next-natural` — take the clip the next
|
|
24626
|
+
* time the camera is awake for its own reasons — is deliberately not a member:
|
|
24627
|
+
* it was proposed, it is free on the battery, and the operator declined it on
|
|
24628
|
+
* 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
|
|
24629
|
+
* D558 "Considered and not taken").
|
|
24630
|
+
*/
|
|
24631
|
+
var ClipWakeSchema = _enum(["authorised"]);
|
|
24632
|
+
/**
|
|
24633
|
+
* A clip's finished bytes, inline — the twin of `recordingExport.readExportBytes`.
|
|
24634
|
+
*
|
|
24635
|
+
* `bytes` is the DECODED length, so nobody infers it from the base64 length,
|
|
24636
|
+
* and `served` says which twin the caller actually got.
|
|
24637
|
+
*/
|
|
24638
|
+
var ClipBytesSchema = object({
|
|
24639
|
+
base64: string(),
|
|
24640
|
+
contentType: string(),
|
|
24641
|
+
/** Suggested filename, extension included. */
|
|
24642
|
+
name: string(),
|
|
24643
|
+
bytes: number().int().nonnegative(),
|
|
24644
|
+
/**
|
|
24645
|
+
* Which twin was actually served — the same contract
|
|
24646
|
+
* {@link ClipPlaybackSchema.served} carries, and REQUIRED here because the
|
|
24647
|
+
* export record persists it: a row read a week later must say the same thing
|
|
24648
|
+
* the panel said at the moment of the tap. A missing main twin is never
|
|
24649
|
+
* served silently as if it were the asked-for quality (D549 15).
|
|
24650
|
+
*/
|
|
24651
|
+
served: CamProfileSchema,
|
|
24652
|
+
/**
|
|
24653
|
+
* The DECODED duration of the delivered file, when the fetch measured one.
|
|
24654
|
+
*
|
|
24655
|
+
* A clip fetch verifies its own completion against the catalog row and
|
|
24656
|
+
* retries a materially short pass (D568); this is that measurement, carried
|
|
24657
|
+
* so a consumer can say the same thing rather than re-deriving it. Absent
|
|
24658
|
+
* when the producer did not measure — never zero, which would say the file
|
|
24659
|
+
* is empty.
|
|
24660
|
+
*/
|
|
24661
|
+
durationMs: number().positive().optional()
|
|
24662
|
+
});
|
|
24663
|
+
/**
|
|
24664
|
+
* Where a clip's STREAM can be dialled (D597) — the answer to
|
|
24665
|
+
* {@link videoclipsCapability.methods.dialClipStream}.
|
|
24666
|
+
*
|
|
24667
|
+
* `url` is plain `http://` on the provider's own token-gated listener, one
|
|
24668
|
+
* shot, short-lived. It is NOT the hub-proxied `/addon/<id>/clip-stream/…`
|
|
24669
|
+
* path: that plane takes only a user credential, which an addon does not
|
|
24670
|
+
* hold, and the framework offers no addon→addon byte transport — the one
|
|
24671
|
+
* precedent is the broker's own restream URLs (`getStreamWithCodec`), which
|
|
24672
|
+
* a consumer dials directly, and this is that precedent one addon over.
|
|
24673
|
+
*/
|
|
24674
|
+
/**
|
|
24675
|
+
* The audio a clip stream can be asked for, and the audio it carries.
|
|
24676
|
+
*
|
|
24677
|
+
* The camera stores AAC and no WebRTC session speaks it, so a stream that
|
|
24678
|
+
* carries sound carries it TRANSCODED, muxed as a second track beside the
|
|
24679
|
+
* video by the one ffmpeg the provider already runs (D600). `opus` is
|
|
24680
|
+
* 48 kHz mono, `pcmu` is 8 kHz µ-law — the two codecs a broker session
|
|
24681
|
+
* negotiates — and `none` is a stream asked for, or served, without sound.
|
|
24682
|
+
*/
|
|
24683
|
+
var ClipStreamAudioSchema = _enum([
|
|
24684
|
+
"opus",
|
|
24685
|
+
"pcmu",
|
|
24686
|
+
"none"
|
|
24687
|
+
]);
|
|
24688
|
+
/**
|
|
24689
|
+
* Why a stream that was ASKED for sound is not carrying it (D600).
|
|
24690
|
+
*
|
|
24691
|
+
* Only ever set beside `servedAudio: 'none'` after a non-`none` ask. Absent
|
|
24692
|
+
* means the ask was honoured, or there was no ask.
|
|
24693
|
+
*
|
|
24694
|
+
* - `not-requested` — the caller asked for `none`.
|
|
24695
|
+
* - `unsupported-ask` — the provider cannot produce that codec.
|
|
24696
|
+
*
|
|
24697
|
+
* Whether the RECORDING has sound at all is not knowable here: the camera
|
|
24698
|
+
* declares no audio track before its first frame arrives. That answer is in
|
|
24699
|
+
* the stream itself — an fMP4 with no audio track — and the broker reads it
|
|
24700
|
+
* there. A dial that says `opus` promises the codec, never the content.
|
|
24701
|
+
*/
|
|
24702
|
+
var ClipStreamAudioReasonSchema = _enum(["not-requested", "unsupported-ask"]);
|
|
24703
|
+
var ClipStreamDialSchema = object({
|
|
24704
|
+
url: string().min(1),
|
|
24705
|
+
/** After this the token is gone and the URL answers 404 `dial-expired`. */
|
|
24706
|
+
expiresAtMs: number(),
|
|
24707
|
+
/** What the stream IS — a consumer that cannot honour it must not dial. */
|
|
24708
|
+
transport: literal("forward-only"),
|
|
24709
|
+
/**
|
|
24710
|
+
* Which twin the stream ACTUALLY carries — not what was asked for.
|
|
24711
|
+
*
|
|
24712
|
+
* The stream serves every profile (D599), and a recording whose main twin
|
|
24713
|
+
* does not exist is streamed as its sub copy, which this field reports as
|
|
24714
|
+
* `low`. That is the whole reason the dial no longer refuses `high`:
|
|
24715
|
+
* stating what was served is what makes accepting honest, where a silent
|
|
24716
|
+
* substitution would be the 33x lie D576 forbids. Stated rather than
|
|
24717
|
+
* assumed, the way {@link ClipPlaybackSchema.served} is (D549 15).
|
|
24718
|
+
*/
|
|
24719
|
+
served: CamProfileSchema,
|
|
24720
|
+
/**
|
|
24721
|
+
* Which audio the stream will ACTUALLY carry — `served`'s companion for
|
|
24722
|
+
* sound, and the same reasoning (D599, D600).
|
|
24723
|
+
*
|
|
24724
|
+
* The broker asks for the codec its WebRTC session negotiated, and the
|
|
24725
|
+
* provider either muxes that codec or carries none. Stating it is what
|
|
24726
|
+
* lets the broker wire the matching sink before the first byte, and what
|
|
24727
|
+
* keeps a session from being handed a codec it cannot send — the audio
|
|
24728
|
+
* shape of the substitution `served` exists to prevent.
|
|
24729
|
+
*
|
|
24730
|
+
* It is a promise about the CODEC, not about the content: a recording with
|
|
24731
|
+
* no microphone track answers `opus` here and then arrives with no audio
|
|
24732
|
+
* track at all. That second answer is in the stream, and the broker reads
|
|
24733
|
+
* it from the `moov`.
|
|
24734
|
+
*/
|
|
24735
|
+
servedAudio: ClipStreamAudioSchema,
|
|
24736
|
+
/** Why `servedAudio` is `none` although sound was asked for. */
|
|
24737
|
+
audioReason: ClipStreamAudioReasonSchema.optional()
|
|
24738
|
+
});
|
|
24739
|
+
var ClipSourceAvailabilitySchema = object({
|
|
24740
|
+
state: _enum([
|
|
24741
|
+
"ok",
|
|
24742
|
+
"sleeping",
|
|
24743
|
+
"unreachable",
|
|
24744
|
+
"no-storage",
|
|
24745
|
+
"index-empty"
|
|
24746
|
+
]),
|
|
24747
|
+
/** Free text, shown verbatim. Names the camera's own refusal when there is one. */
|
|
24748
|
+
reason: string().optional(),
|
|
24749
|
+
/** When this source's catalog was last CONFIRMED. A cached list is never
|
|
24750
|
+
* drawn as current: the surface shows the age whenever it is older than the
|
|
24751
|
+
* refresh interval. */
|
|
24752
|
+
catalogAsOf: number().optional()
|
|
24753
|
+
});
|
|
24754
|
+
/**
|
|
24755
|
+
* One SOURCE of clips for a camera — a row of the picker, and the namespace
|
|
24756
|
+
* every clip id from it is prefixed with (`analytics`,
|
|
24757
|
+
* `native:reolink:onboard`, `hksv`, …).
|
|
24758
|
+
*
|
|
24759
|
+
* A provider lists the sources IT serves for that device, and answers for each
|
|
24760
|
+
* of them whether it can answer at all. A provider with nothing to offer on a
|
|
24761
|
+
* camera returns `[]` — it is not that camera's business.
|
|
24762
|
+
*/
|
|
24763
|
+
var ClipSourceSchema = object({
|
|
24764
|
+
/** The value this source stamps on {@link ClipSchema.source}, and the prefix
|
|
24765
|
+
* of every clip id it mints. `getClipPlayback` routes on it. */
|
|
24766
|
+
source: string(),
|
|
24767
|
+
/** Operator-facing name of the source ("CamStack recordings", "SD card"). */
|
|
24768
|
+
label: string(),
|
|
24769
|
+
/**
|
|
24770
|
+
* The addon that SERVES this row.
|
|
24771
|
+
*
|
|
24772
|
+
* A surface cannot otherwise resolve a source to who answers it, and the
|
|
24773
|
+
* alternative — a `native:reolink:* → provider-reolink` table inside the
|
|
24774
|
+
* widget — is a second authority on provider identity living in the one
|
|
24775
|
+
* package with no business knowing it, wrong the day a third source appears
|
|
24776
|
+
* (D557). One addon may serve SEVERAL sources, which is why the per-device
|
|
24777
|
+
* switch is keyed by SOURCE and not by this (D555) — `addon-provider-reolink`
|
|
24778
|
+
* served a hub child two of them until the two views were measured to be one
|
|
24779
|
+
* store read twice (D555) and then read ONE way (D565).
|
|
24780
|
+
*
|
|
24781
|
+
* Optional for version skew only. The collection dispatcher stamps it from
|
|
24782
|
+
* the registry, so a row that travelled through the fan-out carries the
|
|
24783
|
+
* authoritative id whatever the provider filled in.
|
|
24784
|
+
*/
|
|
24785
|
+
addonId: string().optional(),
|
|
24786
|
+
availability: ClipSourceAvailabilitySchema
|
|
24787
|
+
});
|
|
24111
24788
|
DeviceType.Camera, method(object({
|
|
24112
24789
|
deviceId: number(),
|
|
24113
24790
|
since: number(),
|
|
24114
24791
|
until: number(),
|
|
24115
|
-
limit: number().int().positive().optional()
|
|
24792
|
+
limit: number().int().positive().optional(),
|
|
24793
|
+
/**
|
|
24794
|
+
* WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
|
|
24795
|
+
* carries, never a source id and never a list. **Required** (D554 amended).
|
|
24796
|
+
*
|
|
24797
|
+
* A provider the device is not bound to is refused by name rather than
|
|
24798
|
+
* answered by another one (D552's `rejectUnresolvedAddonPin` rule).
|
|
24799
|
+
*
|
|
24800
|
+
* It was optional, documented as "absent means the device's BOUND
|
|
24801
|
+
* provider, which is CamStack on every camera". No code implemented
|
|
24802
|
+
* that. Measured on the live hub 2026-09-20 — device 592, bound to
|
|
24803
|
+
* `recorder` AND `provider-reolink` — a bare call with `limit: 3`
|
|
24804
|
+
* answered SIX rows, three from each source, merged newest-first:
|
|
24805
|
+
* `device-collection-dispatch.ts` simply left the fan-out un-narrowed,
|
|
24806
|
+
* so absence bought the union this method exists not to be, and
|
|
24807
|
+
* `limit` meant `limit × sources`.
|
|
24808
|
+
*
|
|
24809
|
+
* There is nothing to restore the default to. `getBindings` answers a
|
|
24810
|
+
* collection cap with a DERIVED, PLURAL set (D554 amended, step 0);
|
|
24811
|
+
* `setWrapperActive` — the singleton authority that could name one —
|
|
24812
|
+
* throws for a collection cap by design. Naming CamStack here instead
|
|
24813
|
+
* would privilege one addon by id inside a surface whose premise is
|
|
24814
|
+
* that sources are peers, and would be wrong on the first camera with
|
|
24815
|
+
* no recorder binding. So absence is REFUSED, in the schema, where the
|
|
24816
|
+
* generated types make it unomittable rather than merely discouraged.
|
|
24817
|
+
*
|
|
24818
|
+
* The default belongs to the SURFACE, which has the `listSources` rows
|
|
24819
|
+
* and can say which one it picked (D569 § 1–3; the viewer already
|
|
24820
|
+
* always sends this).
|
|
24821
|
+
*
|
|
24822
|
+
* It replaced `sources?: string[]`, a VIEW filter over source ids that
|
|
24823
|
+
* assumed the answer was a fan-out over everything a camera has. The
|
|
24824
|
+
* operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
|
|
24825
|
+
* solo"* — so the list is asked of one provider at a time and there is
|
|
24826
|
+
* nothing to filter out of it.
|
|
24827
|
+
*/
|
|
24828
|
+
provider: string().min(1)
|
|
24116
24829
|
}), array(ClipSchema).readonly(), {
|
|
24117
24830
|
kind: "query",
|
|
24118
|
-
auth: "
|
|
24831
|
+
auth: "protected"
|
|
24832
|
+
}), method(object({ deviceId: number() }), array(ClipSourceSchema).readonly(), {
|
|
24833
|
+
kind: "query",
|
|
24834
|
+
auth: "protected"
|
|
24119
24835
|
}), method(object({
|
|
24120
24836
|
deviceId: number(),
|
|
24121
|
-
clipId: string()
|
|
24837
|
+
clipId: string(),
|
|
24838
|
+
/**
|
|
24839
|
+
* Which twin to serve, on the ONE quality scale the system already has
|
|
24840
|
+
* (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
|
|
24841
|
+
* twin; both ids are already on the row so this never re-searches the
|
|
24842
|
+
* camera. Absent means the provider's own default (the sub file, which
|
|
24843
|
+
* every source is measured to hold).
|
|
24844
|
+
*
|
|
24845
|
+
* `auto` is deliberately NOT accepted here: a stored file has no
|
|
24846
|
+
* broker session, so the adaptive tier cannot be resolved for it. The
|
|
24847
|
+
* viewer resolves `auto` to a profile the same way live does, before
|
|
24848
|
+
* it calls (D549 15).
|
|
24849
|
+
*/
|
|
24850
|
+
profile: CamProfileSchema.optional()
|
|
24122
24851
|
}), ClipPlaybackSchema, {
|
|
24123
24852
|
kind: "query",
|
|
24124
|
-
auth: "
|
|
24853
|
+
auth: "protected"
|
|
24854
|
+
}), optionalMethod(object({
|
|
24855
|
+
deviceId: number(),
|
|
24856
|
+
clipId: string().min(1),
|
|
24857
|
+
/**
|
|
24858
|
+
* WHICH provider holds the bytes — the `addonId` a
|
|
24859
|
+
* {@link ClipSourceSchema} row carries. **Required**, for the reason
|
|
24860
|
+
* `listClips` states one method above and for a second one this method
|
|
24861
|
+
* learned the hard way.
|
|
24862
|
+
*
|
|
24863
|
+
* The caller is the stream broker, and the broker lives in
|
|
24864
|
+
* `addon-pipeline` — the same addon that registers the RECORDER's
|
|
24865
|
+
* videoclips provider. With no provider named, the in-process
|
|
24866
|
+
* resolution handed it its own sibling, which implements this method
|
|
24867
|
+
* solely to explain that an analytics visit has no file behind it. So
|
|
24868
|
+
* every onboard clip answered with that sentence, a flash of video and
|
|
24869
|
+
* nothing more, while the SAME call from outside the addon reached the
|
|
24870
|
+
* right provider and returned the bytes (measured on 3628,
|
|
24871
|
+
* 2026-09-21).
|
|
24872
|
+
*/
|
|
24873
|
+
provider: string().min(1),
|
|
24874
|
+
/**
|
|
24875
|
+
* Which twin to fetch, on the mapping D549 15 already fixed:
|
|
24876
|
+
* `low | mid` → the sub file, `high` → the main twin. `auto` is not
|
|
24877
|
+
* accepted here for the same reason it is not accepted by
|
|
24878
|
+
* `getClipPlayback` — a stored file has no broker session, so the
|
|
24879
|
+
* adaptive tier cannot be resolved for it.
|
|
24880
|
+
*/
|
|
24881
|
+
profile: CamProfileSchema.optional(),
|
|
24882
|
+
/**
|
|
24883
|
+
* The CALLER's byte bound, so an over-size clip is refused before it is
|
|
24884
|
+
* read and encoded rather than after. Capped by
|
|
24885
|
+
* {@link VIDEOCLIPS_MAX_READ_BYTES} whatever is passed; absent means
|
|
24886
|
+
* that ceiling.
|
|
24887
|
+
*/
|
|
24888
|
+
maxBytes: number().int().positive().optional(),
|
|
24889
|
+
/**
|
|
24890
|
+
* The operator's authorisation to wake a sleeping camera for this
|
|
24891
|
+
* read. Absent — the default — means a sleeping standalone battery
|
|
24892
|
+
* camera is REFUSED by name, before any session is opened.
|
|
24893
|
+
*/
|
|
24894
|
+
wake: ClipWakeSchema.optional()
|
|
24895
|
+
}), ClipBytesSchema, {
|
|
24896
|
+
kind: "query",
|
|
24897
|
+
auth: "protected"
|
|
24898
|
+
}), optionalMethod(object({
|
|
24899
|
+
deviceId: number(),
|
|
24900
|
+
clipId: string().min(1),
|
|
24901
|
+
/** WHICH provider holds the camera — see `readClipBytes.provider`. */
|
|
24902
|
+
provider: string().min(1),
|
|
24903
|
+
/** Which twin — `high` → the main file, everything else the sub (D599). */
|
|
24904
|
+
profile: CamProfileSchema.optional(),
|
|
24905
|
+
/**
|
|
24906
|
+
* Which audio codec the caller's session negotiated, so the provider
|
|
24907
|
+
* can mux it (D600). The ask travels on the DIAL because the mux is
|
|
24908
|
+
* spawned once, before the first byte, and cannot gain a track later.
|
|
24909
|
+
*
|
|
24910
|
+
* ABSENT IS `none`, never a guess: a caller that does not state an
|
|
24911
|
+
* ask is served the video-only stream that shipped first, so a broker
|
|
24912
|
+
* older than this field keeps working unchanged (D382 — an unstated
|
|
24913
|
+
* ask is zero).
|
|
24914
|
+
*/
|
|
24915
|
+
audio: ClipStreamAudioSchema.optional()
|
|
24916
|
+
}), ClipStreamDialSchema, {
|
|
24917
|
+
kind: "query",
|
|
24918
|
+
auth: "protected"
|
|
24125
24919
|
});
|
|
24126
24920
|
/**
|
|
24127
24921
|
* Optional client-side hints sent at session creation to help the provider
|
|
@@ -24193,6 +24987,104 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
24193
24987
|
/** Bitrate in kbps (null for adaptive). */
|
|
24194
24988
|
bitrateKbps: number().nullable()
|
|
24195
24989
|
});
|
|
24990
|
+
/**
|
|
24991
|
+
* The last move the adaptive ladder made on this session, and the numbers it
|
|
24992
|
+
* rested on. Mirrors `LadderDecision` in the stream-broker's
|
|
24993
|
+
* `adaptive-controller.ts` — the log line's evidence, made readable.
|
|
24994
|
+
*
|
|
24995
|
+
* Nullability is load-bearing: `lossRatio: null` means the loss tracker had
|
|
24996
|
+
* no window to report, which is NOT a link with zero loss.
|
|
24997
|
+
*/
|
|
24998
|
+
var WebrtcLadderDecisionSchema = object({
|
|
24999
|
+
/** `loss` — RTCP / client-reported packet loss; `stall` — the picture
|
|
25000
|
+
* stopped moving; `healthy` — a promotion. */
|
|
25001
|
+
cause: _enum([
|
|
25002
|
+
"loss",
|
|
25003
|
+
"stall",
|
|
25004
|
+
"healthy"
|
|
25005
|
+
]),
|
|
25006
|
+
/** The rung the ladder moved TO. Null for a move that changed no rung
|
|
25007
|
+
* (an MTU reduction, a forced transcode-down at the bottom). */
|
|
25008
|
+
tier: CamProfileSchema.nullable(),
|
|
25009
|
+
/** Whether that move forced the egress transcode-down rung. */
|
|
25010
|
+
forcedTranscodeDown: boolean(),
|
|
25011
|
+
/** Loss ratio over the tracker's window at the moment of the decision. */
|
|
25012
|
+
lossRatio: number().nullable(),
|
|
25013
|
+
/** The client's reported downlink at that moment, in kbps. */
|
|
25014
|
+
clientKbps: number().nullable(),
|
|
25015
|
+
/** How long the stall detector had been seeing nothing arrive. */
|
|
25016
|
+
stalledForMs: number().nullable(),
|
|
25017
|
+
/** Whether the broker WAS sending while that was measured — the half that
|
|
25018
|
+
* separates "the camera stopped" from "the link stopped". */
|
|
25019
|
+
serverSending: boolean().nullable(),
|
|
25020
|
+
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
25021
|
+
* must not read like one from this second. */
|
|
25022
|
+
at: number()
|
|
25023
|
+
});
|
|
25024
|
+
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
25025
|
+
var WebrtcTierFailureSchema = object({
|
|
25026
|
+
tier: CamProfileSchema,
|
|
25027
|
+
failures: number().int().nonnegative()
|
|
25028
|
+
});
|
|
25029
|
+
/**
|
|
25030
|
+
* What the SERVER decided for one WebRTC session — the half the client cannot
|
|
25031
|
+
* measure. See `getSessionDebug`.
|
|
25032
|
+
*/
|
|
25033
|
+
var WebrtcSessionDebugSchema = object({
|
|
25034
|
+
sessionId: string(),
|
|
25035
|
+
/** Whether the session still exists here. Everything below is null when
|
|
25036
|
+
* it does not — a dead session has no measurements, not zero ones. */
|
|
25037
|
+
alive: boolean(),
|
|
25038
|
+
/** Epoch ms this snapshot was taken, so the overlay can show its age. */
|
|
25039
|
+
observedAt: number(),
|
|
25040
|
+
/** The broker actually feeding this session (`<deviceId>/<camStreamId>`). */
|
|
25041
|
+
brokerId: string().nullable(),
|
|
25042
|
+
camStreamId: string().nullable(),
|
|
25043
|
+
/** `derived` = this hub BUILT the rung being served (a transcode/scale of
|
|
25044
|
+
* another stream); `native` = the camera's own encode. */
|
|
25045
|
+
sourceKind: _enum([
|
|
25046
|
+
"native",
|
|
25047
|
+
"derived",
|
|
25048
|
+
"unknown"
|
|
25049
|
+
]),
|
|
25050
|
+
/** Whether the ladder is running at all (an `adaptive` target). */
|
|
25051
|
+
adaptive: boolean(),
|
|
25052
|
+
/** The rung `auto` ACTUALLY resolved to right now. Null when the broker
|
|
25053
|
+
* is bound to no profile slot (a manually-activated stream). */
|
|
25054
|
+
servedTier: CamProfileSchema.nullable(),
|
|
25055
|
+
/** Which node ingests the source. Null when unresolved. */
|
|
25056
|
+
ingestNodeId: string().nullable(),
|
|
25057
|
+
/** Whether that node is a remote one (the media crosses a node boundary
|
|
25058
|
+
* before it reaches this session). Null when unresolved. */
|
|
25059
|
+
ingestIsRemote: boolean().nullable(),
|
|
25060
|
+
/** Codec off the source, and the one negotiated with this client. */
|
|
25061
|
+
sourceCodec: string().nullable(),
|
|
25062
|
+
negotiatedCodec: string().nullable(),
|
|
25063
|
+
/** Whether the EGRESS leg re-encodes for this client or forwards the
|
|
25064
|
+
* source bytes untouched. */
|
|
25065
|
+
egressLeg: _enum([
|
|
25066
|
+
"copy",
|
|
25067
|
+
"transcode",
|
|
25068
|
+
"unknown"
|
|
25069
|
+
]),
|
|
25070
|
+
/** What the broker itself reports about the rung it is serving. */
|
|
25071
|
+
brokerStatus: string().nullable(),
|
|
25072
|
+
brokerCodec: string().nullable(),
|
|
25073
|
+
brokerBitrateKbps: number().nullable(),
|
|
25074
|
+
brokerInputFps: number().nullable(),
|
|
25075
|
+
brokerUptimeMs: number().nullable(),
|
|
25076
|
+
/** RTP packets this session has sent — the server's own "am I sending?". */
|
|
25077
|
+
rtpPacketsSent: number().nullable(),
|
|
25078
|
+
/** D42: the track may be carrying the recorded feeder, not live. The ladder
|
|
25079
|
+
* is suspended there, so a frozen picture in `recorded` mode means
|
|
25080
|
+
* something entirely different. */
|
|
25081
|
+
trackMode: _enum(["live", "recorded"]).nullable(),
|
|
25082
|
+
lastDecision: WebrtcLadderDecisionSchema.nullable(),
|
|
25083
|
+
tierFailures: array(WebrtcTierFailureSchema),
|
|
25084
|
+
/** Successful promotions in this viewer session (drives the progressive
|
|
25085
|
+
* healthy timer). Null when no ladder is running. */
|
|
25086
|
+
upgradeCount: number().nullable()
|
|
25087
|
+
});
|
|
24196
25088
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
24197
25089
|
deviceId: number().int().nonnegative(),
|
|
24198
25090
|
target: WebrtcStreamTargetSchema,
|
|
@@ -24357,7 +25249,10 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
|
|
|
24357
25249
|
* the same answer as "it ended": either way nothing is holding it up.
|
|
24358
25250
|
*/
|
|
24359
25251
|
alive: boolean()
|
|
24360
|
-
}))
|
|
25252
|
+
})), method(object({
|
|
25253
|
+
deviceId: number().int().nonnegative(),
|
|
25254
|
+
sessionId: string()
|
|
25255
|
+
}), WebrtcSessionDebugSchema, { kind: "query" });
|
|
24361
25256
|
object({
|
|
24362
25257
|
/** All accessory children of the parent. */
|
|
24363
25258
|
childDeviceIds: array(number()).readonly(),
|
|
@@ -25578,6 +26473,307 @@ DeviceType.Camera, method(object({ deviceId: number() }), DayNightOptionsSchema)
|
|
|
25578
26473
|
auth: "admin"
|
|
25579
26474
|
});
|
|
25580
26475
|
/**
|
|
26476
|
+
* Vendor-neutral **onboard** recording + storage cap — what the CAMERA
|
|
26477
|
+
* writes to the CAMERA's own card, on the camera's own schedule.
|
|
26478
|
+
*
|
|
26479
|
+
* This is NOT `recording.cap.ts`. That one is CamStack's recorder: our
|
|
26480
|
+
* footage ledger, our storage locations, our retention. This one has a
|
|
26481
|
+
* different authority — the camera's firmware — and per D62 it stores
|
|
26482
|
+
* nothing of its own. Every value here is read from the camera and every
|
|
26483
|
+
* write goes back to the camera; there is no CamStack-side mirror that
|
|
26484
|
+
* could disagree with the device.
|
|
26485
|
+
*
|
|
26486
|
+
* ## One shape, two firmwares
|
|
26487
|
+
*
|
|
26488
|
+
* Measured 2026-09-22 against the live fleet:
|
|
26489
|
+
*
|
|
26490
|
+
* | fact | Hikvision (ISAPI) | Reolink (Baichuan) |
|
|
26491
|
+
* | --- | --- | --- |
|
|
26492
|
+
* | storage | `ContentMgmt/Storage` `<hdd>` rows: status, capacity, freeSpace (MB) | `getHddInfoList` (cmd 102): `mount`, `format`, `capacity` GB + `capacityM` MB remainder |
|
|
26493
|
+
* | tracks | several (101 **and** 103 on both 1436 and 3833), each with its own schedule | one per channel |
|
|
26494
|
+
* | schedule | per track, 7 `ScheduleAction` blocks: DayOfWeek + TimeOfDay range + ONE `ActionRecordingMode` | per trigger type, a 168-char weekly HOUR mask |
|
|
26495
|
+
* | triggers | `CMR`, `MOTION` | `Normal`, `MD`, `people`, `vehicle`, `dog_cat`, `crossline`, `intrude`, `loitering` |
|
|
26496
|
+
* | pre-record | `PreRecordTimeSeconds` | `preRecordTime` |
|
|
26497
|
+
* | post-record | `PostRecordTimeSeconds` | `recordDelayTime` |
|
|
26498
|
+
* | overwrite | per track `LoopEnable` | `cycle`, with `cycleList` enumerating the accepted values |
|
|
26499
|
+
* | segment length | not exposed on V5.7.1 | `packageTime` (minutes) |
|
|
26500
|
+
*
|
|
26501
|
+
* The two schedule models look different and are the same thing in
|
|
26502
|
+
* different coordinates: both answer "for this trigger, during which
|
|
26503
|
+
* weekly windows does the camera record". {@link RecordWindow} is that
|
|
26504
|
+
* question in one shape — Hikvision's ranges map straight onto it,
|
|
26505
|
+
* Reolink's mask expands into hour-aligned windows.
|
|
26506
|
+
*
|
|
26507
|
+
* ## Union, not intersection
|
|
26508
|
+
*
|
|
26509
|
+
* **The same fields exist on every camera.** What differs per device is
|
|
26510
|
+
* which VALUES that device accepts, and that is what {@link
|
|
26511
|
+
* RecordingOnboardOptions} reports — a `{ readable, writable, reason }`
|
|
26512
|
+
* per field plus the schedule's own limits. A control a camera cannot
|
|
26513
|
+
* honour is rendered DISABLED WITH ITS REASON, never missing and never
|
|
26514
|
+
* dead: disabled must not look like broken.
|
|
26515
|
+
*
|
|
26516
|
+
* ## Refusal by name
|
|
26517
|
+
*
|
|
26518
|
+
* A write a camera cannot honour is refused with a sentence the operator
|
|
26519
|
+
* can read — never accepted and dropped. Both providers refuse through
|
|
26520
|
+
* {@link describeOnboardRefusal}, so the vocabulary is one function and
|
|
26521
|
+
* one test, not two hand-written vendor opinions.
|
|
26522
|
+
*
|
|
26523
|
+
* Follows the D14 `deviceConfig` archetype (see `stream-params.cap.ts`):
|
|
26524
|
+
* `getOptions` advertises per-camera availability, `getStatus` (auto-
|
|
26525
|
+
* injected from `status`) reports the live values, and a single
|
|
26526
|
+
* `setSettings` mutation applies a partial change. No hand-written
|
|
26527
|
+
* settings-contribution methods.
|
|
26528
|
+
*/
|
|
26529
|
+
/**
|
|
26530
|
+
* What makes the camera start recording during a window.
|
|
26531
|
+
*
|
|
26532
|
+
* The union of both vendors' vocabularies. `continuous` is Hikvision's
|
|
26533
|
+
* `CMR` and Reolink's `Normal`; `motion` is `MOTION` / `MD`. The
|
|
26534
|
+
* object-class triggers are Reolink-only today and the smart-event ones
|
|
26535
|
+
* (`lineCrossing`, `intrusion`, `loitering`) are Reolink-only on the
|
|
26536
|
+
* firmwares measured — a camera that cannot record on a trigger simply
|
|
26537
|
+
* does not list it in `options.schedule.triggers`, and a window naming
|
|
26538
|
+
* it is REFUSED, not dropped.
|
|
26539
|
+
*/
|
|
26540
|
+
var RecordTriggerSchema = _enum([
|
|
26541
|
+
"continuous",
|
|
26542
|
+
"motion",
|
|
26543
|
+
"person",
|
|
26544
|
+
"vehicle",
|
|
26545
|
+
"animal",
|
|
26546
|
+
"lineCrossing",
|
|
26547
|
+
"intrusion",
|
|
26548
|
+
"loitering",
|
|
26549
|
+
"alarmInput"
|
|
26550
|
+
]);
|
|
26551
|
+
/**
|
|
26552
|
+
* One weekly recording window: "on `day`, from `startMinute` to
|
|
26553
|
+
* `endMinute`, record on `trigger`".
|
|
26554
|
+
*
|
|
26555
|
+
* `day` is 0 = Monday … 6 = Sunday (ISO order, which is also the order
|
|
26556
|
+
* both firmwares enumerate). Minutes are local camera time since
|
|
26557
|
+
* midnight; `endMinute` may be 1440, meaning end of day — that is
|
|
26558
|
+
* Hikvision's literal `24:00` and Reolink's 24th mask slot, and
|
|
26559
|
+
* collapsing it to 0 would turn a whole-day window into an empty one.
|
|
26560
|
+
*/
|
|
26561
|
+
var RecordWindowSchema = object({
|
|
26562
|
+
trigger: RecordTriggerSchema,
|
|
26563
|
+
day: number().int().min(0).max(6),
|
|
26564
|
+
startMinute: number().int().min(0).max(1439),
|
|
26565
|
+
endMinute: number().int().min(1).max(1440)
|
|
26566
|
+
});
|
|
26567
|
+
/** Status of one physical volume, as the camera itself describes it. */
|
|
26568
|
+
var OnboardStorageVolumeSchema = object({
|
|
26569
|
+
/** The camera's own id for the volume (`hdd/id`, Reolink `HddInfo/number`). */
|
|
26570
|
+
id: string(),
|
|
26571
|
+
/** The camera's own name for it, when it gives one (`hddName`). */
|
|
26572
|
+
label: string().optional(),
|
|
26573
|
+
status: _enum([
|
|
26574
|
+
"ok",
|
|
26575
|
+
"unformatted",
|
|
26576
|
+
"error",
|
|
26577
|
+
"offline",
|
|
26578
|
+
"unknown"
|
|
26579
|
+
]),
|
|
26580
|
+
/**
|
|
26581
|
+
* Total size in MB, or **null when the camera did not say**.
|
|
26582
|
+
*
|
|
26583
|
+
* Never 0 for an unreadable value: a measurement that failed is not a
|
|
26584
|
+
* measurement (D393), and a card whose size is unknown must not be
|
|
26585
|
+
* rendered as a card of size zero.
|
|
26586
|
+
*/
|
|
26587
|
+
capacityMb: number().nullable(),
|
|
26588
|
+
/**
|
|
26589
|
+
* Free space in MB, or null when unknown.
|
|
26590
|
+
*
|
|
26591
|
+
* **Not a proxy for "has footage".** Measured 2026-09-22: 1436 and
|
|
26592
|
+
* 1439 both report exactly 11776 MB free — the fixed reserve a looping
|
|
26593
|
+
* card converges on once it has wrapped. At loop steady state the
|
|
26594
|
+
* number is identical whether the camera recorded yesterday or stopped
|
|
26595
|
+
* a month ago.
|
|
26596
|
+
*/
|
|
26597
|
+
freeMb: number().nullable(),
|
|
26598
|
+
/** True when the camera reports the volume writable (`property` RW). */
|
|
26599
|
+
writable: boolean().optional()
|
|
26600
|
+
});
|
|
26601
|
+
object({
|
|
26602
|
+
storage: discriminatedUnion("kind", [
|
|
26603
|
+
object({
|
|
26604
|
+
kind: literal("present"),
|
|
26605
|
+
volumes: array(OnboardStorageVolumeSchema)
|
|
26606
|
+
}),
|
|
26607
|
+
object({
|
|
26608
|
+
kind: literal("absent"),
|
|
26609
|
+
reason: string()
|
|
26610
|
+
}),
|
|
26611
|
+
object({
|
|
26612
|
+
kind: literal("unknown"),
|
|
26613
|
+
reason: string()
|
|
26614
|
+
})
|
|
26615
|
+
]),
|
|
26616
|
+
tracks: array(object({
|
|
26617
|
+
id: string(),
|
|
26618
|
+
enabled: boolean(),
|
|
26619
|
+
isVideo: boolean(),
|
|
26620
|
+
/** From the camera's own track description. Null when it does not say. */
|
|
26621
|
+
codec: string().nullable(),
|
|
26622
|
+
resolution: string().nullable(),
|
|
26623
|
+
/** Per-track overwrite flag, where the firmware keeps it per track. */
|
|
26624
|
+
overwriteWhenFull: boolean().nullable()
|
|
26625
|
+
})),
|
|
26626
|
+
/**
|
|
26627
|
+
* The track the write path targets — the enabled VIDEO one. Null when
|
|
26628
|
+
* no track could be identified, which is itself a refusal reason.
|
|
26629
|
+
*/
|
|
26630
|
+
primaryTrackId: string().nullable(),
|
|
26631
|
+
/** Master "record to the card at all" switch. */
|
|
26632
|
+
enabled: boolean().nullable(),
|
|
26633
|
+
overwriteWhenFull: boolean().nullable(),
|
|
26634
|
+
preRecordSec: number().nullable(),
|
|
26635
|
+
postRecordSec: number().nullable(),
|
|
26636
|
+
/** Length of one recorded file, in minutes. */
|
|
26637
|
+
segmentMinutes: number().nullable(),
|
|
26638
|
+
/** The primary track's weekly windows, flattened. */
|
|
26639
|
+
windows: array(RecordWindowSchema),
|
|
26640
|
+
/**
|
|
26641
|
+
* How many windows the camera described that CamStack could NOT read —
|
|
26642
|
+
* an unrecognised trigger, an unparseable clock, a weekday it does not
|
|
26643
|
+
* name.
|
|
26644
|
+
*
|
|
26645
|
+
* A dropped window is work the reader threw away, and a schedule that
|
|
26646
|
+
* silently shows fewer rows than the camera holds is how an operator
|
|
26647
|
+
* saves back a schedule shorter than the one they were looking at
|
|
26648
|
+
* (D391). Non-zero means the window list is INCOMPLETE and a write
|
|
26649
|
+
* that replaces it would delete what was not shown — which is why a
|
|
26650
|
+
* provider reporting a non-zero count also reports the schedule as not
|
|
26651
|
+
* writable.
|
|
26652
|
+
*/
|
|
26653
|
+
unreadableWindows: number(),
|
|
26654
|
+
/**
|
|
26655
|
+
* The camera is scheduled to record and has NO usable storage.
|
|
26656
|
+
*
|
|
26657
|
+
* A first-class fact because it is the fleet's most common silent
|
|
26658
|
+
* defect: measured 2026-09-22, 1441 and 3831 are both motion-recording
|
|
26659
|
+
* to a card that is not there. Neither the schedule nor the storage
|
|
26660
|
+
* read says anything wrong on its own; only the pair does.
|
|
26661
|
+
*/
|
|
26662
|
+
recordingToNowhere: boolean(),
|
|
26663
|
+
lastFetchedAt: number()
|
|
26664
|
+
});
|
|
26665
|
+
/** Numeric range descriptor — `{ min, max, step }` per the getOptions convention. */
|
|
26666
|
+
var RangeSchema = object({
|
|
26667
|
+
min: number(),
|
|
26668
|
+
max: number(),
|
|
26669
|
+
step: number()
|
|
26670
|
+
});
|
|
26671
|
+
/**
|
|
26672
|
+
* The values a camera actually takes for a numeric field, when they are a SET
|
|
26673
|
+
* rather than a range.
|
|
26674
|
+
*
|
|
26675
|
+
* `{min,max,step}` cannot say what these two firmwares do. Measured on 1436
|
|
26676
|
+
* (I91DN) on 2026-09-22 by writing each value and reading it back:
|
|
26677
|
+
*
|
|
26678
|
+
* - pre-record: `0, 5, 10, 15, 20, 25, 30` and `2147483647` (INT32_MAX, the
|
|
26679
|
+
* camera's "no limit" — `-1` and `4294967295` both land on it);
|
|
26680
|
+
* - post-record: `5, 10, 30, 60, 120, 300, 600`.
|
|
26681
|
+
*
|
|
26682
|
+
* Neither is expressible as a step: the first has a sentinel two billion away
|
|
26683
|
+
* from its neighbours, the second doubles and then jumps. A range that tried
|
|
26684
|
+
* would forbid values the camera takes AND permit values it silently replaces
|
|
26685
|
+
* with 5 — wrong in both directions at once.
|
|
26686
|
+
*
|
|
26687
|
+
* `sentinel` names the member that is not a duration, so a surface can render
|
|
26688
|
+
* "no limit" instead of `2147483647` seconds.
|
|
26689
|
+
*/
|
|
26690
|
+
var AllowedValuesSchema = object({
|
|
26691
|
+
values: array(number()).min(1),
|
|
26692
|
+
sentinel: object({
|
|
26693
|
+
value: number(),
|
|
26694
|
+
meaning: _enum(["no-limit", "disabled"])
|
|
26695
|
+
}).optional()
|
|
26696
|
+
});
|
|
26697
|
+
/**
|
|
26698
|
+
* Per-field availability on ONE camera.
|
|
26699
|
+
*
|
|
26700
|
+
* The field exists on every camera — this says whether this one can be
|
|
26701
|
+
* read and whether it can be written, and `reason` says why not when
|
|
26702
|
+
* either is false. The UI renders the control DISABLED with the reason
|
|
26703
|
+
* rather than hiding it, so a limitation is legible instead of looking
|
|
26704
|
+
* like a missing feature.
|
|
26705
|
+
*/
|
|
26706
|
+
var OnboardFieldSupportSchema = object({
|
|
26707
|
+
readable: boolean(),
|
|
26708
|
+
writable: boolean(),
|
|
26709
|
+
/** Required whenever `readable` or `writable` is false. */
|
|
26710
|
+
reason: string().optional()
|
|
26711
|
+
});
|
|
26712
|
+
/** What this camera's schedule model can express. */
|
|
26713
|
+
var OnboardScheduleSupportSchema = object({
|
|
26714
|
+
support: OnboardFieldSupportSchema,
|
|
26715
|
+
/**
|
|
26716
|
+
* The smallest time step the camera can express, in minutes.
|
|
26717
|
+
*
|
|
26718
|
+
* Hikvision takes arbitrary minutes (`00:05:00`–`23:57:00` observed on
|
|
26719
|
+
* 1436's track 103). Reolink's schedule is a 7×24 HOUR mask, so 60. A
|
|
26720
|
+
* window whose edges are not a multiple of this is REFUSED rather than
|
|
26721
|
+
* quietly rounded — rounding is how an operator's 06:30 becomes 06:00
|
|
26722
|
+
* and nothing says so.
|
|
26723
|
+
*/
|
|
26724
|
+
granularityMinutes: number(),
|
|
26725
|
+
/** Triggers this camera can record on. A window naming another is refused. */
|
|
26726
|
+
triggers: array(RecordTriggerSchema),
|
|
26727
|
+
/**
|
|
26728
|
+
* False when the camera stores ONE trigger per time range, so two
|
|
26729
|
+
* windows overlapping on the same day cannot carry different triggers.
|
|
26730
|
+
* True on Reolink, whose mask is per-trigger and independent.
|
|
26731
|
+
*/
|
|
26732
|
+
supportsOverlappingTriggers: boolean()
|
|
26733
|
+
});
|
|
26734
|
+
var RecordingOnboardOptionsSchema = object({
|
|
26735
|
+
enabled: OnboardFieldSupportSchema,
|
|
26736
|
+
overwriteWhenFull: OnboardFieldSupportSchema,
|
|
26737
|
+
preRecordSec: OnboardFieldSupportSchema,
|
|
26738
|
+
preRecordSecRange: RangeSchema.optional(),
|
|
26739
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
26740
|
+
preRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
26741
|
+
postRecordSec: OnboardFieldSupportSchema,
|
|
26742
|
+
postRecordSecRange: RangeSchema.optional(),
|
|
26743
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
26744
|
+
postRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
26745
|
+
segmentMinutes: OnboardFieldSupportSchema,
|
|
26746
|
+
segmentMinutesRange: RangeSchema.optional(),
|
|
26747
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
26748
|
+
segmentMinutesAllowed: AllowedValuesSchema.optional(),
|
|
26749
|
+
schedule: OnboardScheduleSupportSchema
|
|
26750
|
+
});
|
|
26751
|
+
/**
|
|
26752
|
+
* A partial change. Every field optional.
|
|
26753
|
+
*
|
|
26754
|
+
* Unlike the other `deviceConfig` caps, a provider here does **NOT**
|
|
26755
|
+
* silently ignore a field it cannot support — it refuses, by name,
|
|
26756
|
+
* through {@link describeOnboardRefusal}. Silence on a recording setting
|
|
26757
|
+
* is the failure D62 exists to prevent: the operator believes the camera
|
|
26758
|
+
* is recording the way the form says, and it is not.
|
|
26759
|
+
*/
|
|
26760
|
+
var RecordingOnboardPatchSchema = object({
|
|
26761
|
+
enabled: boolean().optional(),
|
|
26762
|
+
overwriteWhenFull: boolean().optional(),
|
|
26763
|
+
preRecordSec: number().optional(),
|
|
26764
|
+
postRecordSec: number().optional(),
|
|
26765
|
+
segmentMinutes: number().optional(),
|
|
26766
|
+
/** The complete new window set for the primary track — not a delta. */
|
|
26767
|
+
windows: array(RecordWindowSchema).optional()
|
|
26768
|
+
});
|
|
26769
|
+
DeviceType.Camera, method(object({ deviceId: number() }), RecordingOnboardOptionsSchema), method(object({
|
|
26770
|
+
deviceId: number(),
|
|
26771
|
+
settings: RecordingOnboardPatchSchema
|
|
26772
|
+
}), _void(), {
|
|
26773
|
+
kind: "mutation",
|
|
26774
|
+
auth: "admin"
|
|
26775
|
+
});
|
|
26776
|
+
/**
|
|
25581
26777
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
25582
26778
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
25583
26779
|
* cap-keyed slice for the basic device flags every consumer expects to
|
|
@@ -27445,16 +28641,37 @@ var GridProfileOfferSchema = object({
|
|
|
27445
28641
|
]),
|
|
27446
28642
|
offered: boolean(),
|
|
27447
28643
|
/** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
|
|
27448
|
-
missingSources: array(number().int().positive())
|
|
28644
|
+
missingSources: array(number().int().positive()),
|
|
28645
|
+
/**
|
|
28646
|
+
* The canvas this profile composes onto, `WxH`, or empty when it is not
|
|
28647
|
+
* offered. DERIVED from the cells and the sources' own size at this profile —
|
|
28648
|
+
* it is reported because nothing else in the system would ever say what the
|
|
28649
|
+
* grid came out as, and because it is the number an operator would otherwise
|
|
28650
|
+
* expect to type.
|
|
28651
|
+
*/
|
|
28652
|
+
canvas: string(),
|
|
28653
|
+
/**
|
|
28654
|
+
* Whether this profile is PUBLISHED, of the ones the grid could serve.
|
|
28655
|
+
*
|
|
28656
|
+
* A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
|
|
28657
|
+
* a 4K canvas built from 4K decodes — something to opt into, not something a
|
|
28658
|
+
* viewer's adaptive should be handed by climbing to the top rung it can see.
|
|
28659
|
+
* Default is `mid` + `low`.
|
|
28660
|
+
*/
|
|
28661
|
+
published: boolean()
|
|
27449
28662
|
});
|
|
27450
28663
|
var GridLayoutViewSchema = object({
|
|
27451
28664
|
/** The persisted grid row this camera was declared from. */
|
|
27452
28665
|
instanceId: string(),
|
|
27453
28666
|
deviceId: number().int().nonnegative(),
|
|
27454
28667
|
name: string(),
|
|
27455
|
-
/**
|
|
27456
|
-
|
|
27457
|
-
|
|
28668
|
+
/**
|
|
28669
|
+
* NO canvas size. A grid's resolution is not authored: each profile derives
|
|
28670
|
+
* its own from the cells and its sources' dimensions. The two numbers that
|
|
28671
|
+
* used to be here were a text field that silently decided both how much the
|
|
28672
|
+
* composite cost and how sharp it was — see `profiles[].canvas` for what it
|
|
28673
|
+
* came out as.
|
|
28674
|
+
*/
|
|
27458
28675
|
fps: number().int(),
|
|
27459
28676
|
cells: array(GridLayoutCellSchema),
|
|
27460
28677
|
/** What the catalog will publish, and what it refuses to. Read-only. */
|
|
@@ -27463,9 +28680,13 @@ var GridLayoutViewSchema = object({
|
|
|
27463
28680
|
var GridLayoutPatchSchema = object({
|
|
27464
28681
|
deviceId: number().int().nonnegative(),
|
|
27465
28682
|
name: string().min(1).max(160).optional(),
|
|
27466
|
-
width: number().int().min(160).max(7680).optional(),
|
|
27467
|
-
height: number().int().min(120).max(4320).optional(),
|
|
27468
28683
|
fps: number().int().min(1).max(60).optional(),
|
|
28684
|
+
/** Which profiles to publish. See `GridProfileOffer.published`. */
|
|
28685
|
+
publishedProfiles: array(_enum([
|
|
28686
|
+
"high",
|
|
28687
|
+
"mid",
|
|
28688
|
+
"low"
|
|
28689
|
+
])).max(3).optional(),
|
|
27469
28690
|
/**
|
|
27470
28691
|
* The whole cell list at once. A per-cell patch would need an ordering the
|
|
27471
28692
|
* editor does not have, and a half-applied layout is a picture nobody asked
|
|
@@ -29790,7 +31011,7 @@ method(object({
|
|
|
29790
31011
|
auth: "admin"
|
|
29791
31012
|
}), method(object({ deviceId: number() }), RecordingConfigSchema, {
|
|
29792
31013
|
kind: "query",
|
|
29793
|
-
auth: "
|
|
31014
|
+
auth: "protected"
|
|
29794
31015
|
}), method(object({
|
|
29795
31016
|
deviceId: number(),
|
|
29796
31017
|
profile: string(),
|
|
@@ -30020,13 +31241,101 @@ var ExportStateSchema = _enum([
|
|
|
30020
31241
|
"expired",
|
|
30021
31242
|
"deleted"
|
|
30022
31243
|
]);
|
|
30023
|
-
/**
|
|
31244
|
+
/**
|
|
31245
|
+
* WHAT an export is — the authority, as opposed to the four top-level fields
|
|
31246
|
+
* the Library sorts and labels on (D558 § 2.2).
|
|
31247
|
+
*
|
|
31248
|
+
* Read it through {@link exportSubjectOf}, never off the record directly: the
|
|
31249
|
+
* field is optional for the history rows written before it existed, and that
|
|
31250
|
+
* absence has exactly one interpreter.
|
|
31251
|
+
*/
|
|
31252
|
+
var ExportSubjectSchema = discriminatedUnion("kind", [object({
|
|
31253
|
+
kind: literal("footage"),
|
|
31254
|
+
deviceId: number(),
|
|
31255
|
+
profiles: array(string()).min(1),
|
|
31256
|
+
fromMs: number(),
|
|
31257
|
+
toMs: number()
|
|
31258
|
+
}), object({
|
|
31259
|
+
kind: literal("clip"),
|
|
31260
|
+
deviceId: number(),
|
|
31261
|
+
provider: string().min(1),
|
|
31262
|
+
source: string().min(1),
|
|
31263
|
+
sourceLabel: string().min(1),
|
|
31264
|
+
clipId: string().min(1),
|
|
31265
|
+
/**
|
|
31266
|
+
* WHERE IN THE CATALOG to confirm this clip — the day window the surface was
|
|
31267
|
+
* already listing when the operator picked the segment.
|
|
31268
|
+
*
|
|
31269
|
+
* It is **not** a time range control and it never becomes one: the record's
|
|
31270
|
+
* `fromMs`/`toMs` come from the catalog ROW and from nothing a caller
|
|
31271
|
+
* supplied (D558 § 5.4.3), and a window that does not contain the clip is a
|
|
31272
|
+
* `catalog-miss`, not a silently wider search. It exists because
|
|
31273
|
+
* `videoclips.listClips` takes `since`/`until` and has no by-handle twin:
|
|
31274
|
+
* the catalog check D558 asks for is literally that call, and a call needs a
|
|
31275
|
+
* window. The surface has one — `ClipsBrowser`'s `dayWindow`, one local
|
|
31276
|
+
* wall-clock day, which is also the only width measured to be cheap (one
|
|
31277
|
+
* day on 592 lists 171 clips in 712 ms, a busy day 546 in 2.0 s; twenty days
|
|
31278
|
+
* of one child's events took 3.6–5.4 s).
|
|
31279
|
+
*/
|
|
31280
|
+
catalogWindow: object({
|
|
31281
|
+
sinceMs: number(),
|
|
31282
|
+
untilMs: number()
|
|
31283
|
+
}),
|
|
31284
|
+
profile: _enum([
|
|
31285
|
+
"high",
|
|
31286
|
+
"mid",
|
|
31287
|
+
"low"
|
|
31288
|
+
]),
|
|
31289
|
+
/**
|
|
31290
|
+
* The operator's authorisation to wake a sleeping camera for this export.
|
|
31291
|
+
* ONE definition, in the cap that owns the clip read ({@link ClipWakeSchema}),
|
|
31292
|
+
* because the gate that honours it is the clip provider's sleep gate — a
|
|
31293
|
+
* second enum here would be a second contract. Absent by default; never
|
|
31294
|
+
* settable by a scheduler or a retry.
|
|
31295
|
+
*
|
|
31296
|
+
* **One authorised yes is ONE wake.** A failed clip export is retried only by
|
|
31297
|
+
* an operator act that asks again; a queued job that outlived its wake fails
|
|
31298
|
+
* with a reason rather than waking on its turn; and this subject carries
|
|
31299
|
+
* exactly one profile precisely so one tap is never two fetches (D558 § 5.2).
|
|
31300
|
+
*/
|
|
31301
|
+
wake: ClipWakeSchema.optional()
|
|
31302
|
+
})]);
|
|
31303
|
+
/**
|
|
31304
|
+
* One export job / history row.
|
|
31305
|
+
*
|
|
31306
|
+
* **`subject` is the AUTHORITY on what was exported. `deviceId`, `profile`,
|
|
31307
|
+
* `fromMs` and `toMs` are its PROJECTION** — kept top-level because the whole
|
|
31308
|
+
* Library sorts and labels on them (`library-items.ts` orders an export by
|
|
31309
|
+
* `fromMs`; `export-format.ts` draws `rangeLabel` from the pair), and a row
|
|
31310
|
+
* that did not fill them would sort under the epoch and render a blank range.
|
|
31311
|
+
* Write to the subject and read from the projection and the two will disagree;
|
|
31312
|
+
* the projection is derived at creation and never edited afterwards.
|
|
31313
|
+
*
|
|
31314
|
+
* And they mean DIFFERENT FACTS for the two kinds, which is the part a reader
|
|
31315
|
+
* who knows only the recording export will get wrong:
|
|
31316
|
+
*
|
|
31317
|
+
* | field | `kind:'footage'` | `kind:'clip'` |
|
|
31318
|
+
* | --- | --- | --- |
|
|
31319
|
+
* | `fromMs`/`toMs` | the stretch the operator ASKED for | the camera's own boundaries, always `clip.timeRange`, never anything a caller supplied |
|
|
31320
|
+
* | `profile` | the stream rendered | the twin actually SERVED (`subject.profile` is the one asked for) |
|
|
31321
|
+
*
|
|
31322
|
+
* Same type, different fact — the shape this repo keeps getting wrong (D385's
|
|
31323
|
+
* two authorities, D224's second copy).
|
|
31324
|
+
*/
|
|
30024
31325
|
var ExportRecordSchema = object({
|
|
30025
31326
|
id: string(),
|
|
30026
31327
|
deviceId: number(),
|
|
30027
31328
|
profile: string(),
|
|
30028
31329
|
fromMs: number(),
|
|
30029
31330
|
toMs: number(),
|
|
31331
|
+
/**
|
|
31332
|
+
* What this export IS. Optional ONLY for the rows written before D558: the
|
|
31333
|
+
* store parses every row through this schema on every read, so a required
|
|
31334
|
+
* field would make the export AUDIT — which is the whole reason rows survive
|
|
31335
|
+
* file deletion — unreadable in one release. Absence means `footage`, and
|
|
31336
|
+
* {@link exportSubjectOf} is the one place that says so.
|
|
31337
|
+
*/
|
|
31338
|
+
subject: ExportSubjectSchema.optional(),
|
|
30030
31339
|
options: ExportOptionsSchema,
|
|
30031
31340
|
state: ExportStateSchema,
|
|
30032
31341
|
/** 0–100 while rendering; null otherwise. */
|
|
@@ -30043,6 +31352,18 @@ var ExportRecordSchema = object({
|
|
|
30043
31352
|
/** Failure reason when state is 'failed'; null otherwise. */
|
|
30044
31353
|
error: string().nullable()
|
|
30045
31354
|
});
|
|
31355
|
+
_enum([
|
|
31356
|
+
"catalog-miss",
|
|
31357
|
+
"catalog-unreachable",
|
|
31358
|
+
"no-file-for-window",
|
|
31359
|
+
"clip-in-progress",
|
|
31360
|
+
"unsupported-option",
|
|
31361
|
+
"sleeping",
|
|
31362
|
+
"camera-refused",
|
|
31363
|
+
"fetch-failed",
|
|
31364
|
+
"too-large-to-transfer",
|
|
31365
|
+
"wake-expired"
|
|
31366
|
+
]);
|
|
30046
31367
|
/** Candidate download URLs (LAN first, then operator extra hosts). */
|
|
30047
31368
|
var ExportDownloadSchema = object({
|
|
30048
31369
|
url: string(),
|
|
@@ -30061,16 +31382,50 @@ var ExportBytesSchema = object({
|
|
|
30061
31382
|
name: string(),
|
|
30062
31383
|
bytes: number().int().nonnegative()
|
|
30063
31384
|
});
|
|
31385
|
+
/** Canonical `profiles[]`, falling back to the legacy singular `profile`. */
|
|
31386
|
+
function resolveExportProfiles(input) {
|
|
31387
|
+
if (input.profiles !== void 0 && input.profiles.length > 0) return [...input.profiles];
|
|
31388
|
+
if (typeof input.profile === "string" && input.profile.length > 0) return [input.profile];
|
|
31389
|
+
return [];
|
|
31390
|
+
}
|
|
30064
31391
|
method(object({
|
|
30065
31392
|
deviceId: number(),
|
|
30066
31393
|
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
30067
31394
|
profile: string().optional(),
|
|
30068
31395
|
profiles: array(string()).min(1).optional(),
|
|
30069
|
-
|
|
30070
|
-
|
|
31396
|
+
/** Footage only — a clip's boundaries are the camera's. */
|
|
31397
|
+
fromMs: number().optional(),
|
|
31398
|
+
toMs: number().optional(),
|
|
31399
|
+
/** What to export. Absent means the legacy flat footage request. */
|
|
31400
|
+
subject: ExportSubjectSchema.optional(),
|
|
30071
31401
|
options: ExportOptionsSchema
|
|
30072
31402
|
}).superRefine((v, ctx) => {
|
|
30073
|
-
if (
|
|
31403
|
+
if (v.subject?.kind === "clip") {
|
|
31404
|
+
if (v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
31405
|
+
code: ZodIssueCode.custom,
|
|
31406
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId}) — the top-level field is what per-device scope enforcement reads`,
|
|
31407
|
+
path: ["subject", "deviceId"]
|
|
31408
|
+
});
|
|
31409
|
+
if (v.fromMs !== void 0 || v.toMs !== void 0) ctx.addIssue({
|
|
31410
|
+
code: ZodIssueCode.custom,
|
|
31411
|
+
message: "a clip export asks for no time range: the camera chose the boundaries and they are read from the catalog row",
|
|
31412
|
+
path: ["fromMs"]
|
|
31413
|
+
});
|
|
31414
|
+
return;
|
|
31415
|
+
}
|
|
31416
|
+
if (v.subject?.kind === "footage" && v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
31417
|
+
code: ZodIssueCode.custom,
|
|
31418
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId})`,
|
|
31419
|
+
path: ["subject", "deviceId"]
|
|
31420
|
+
});
|
|
31421
|
+
const fromMs = v.subject?.kind === "footage" ? v.subject.fromMs : v.fromMs;
|
|
31422
|
+
const toMs = v.subject?.kind === "footage" ? v.subject.toMs : v.toMs;
|
|
31423
|
+
if (typeof fromMs !== "number" || typeof toMs !== "number") ctx.addIssue({
|
|
31424
|
+
code: ZodIssueCode.custom,
|
|
31425
|
+
message: "a footage export needs fromMs and toMs",
|
|
31426
|
+
path: ["fromMs"]
|
|
31427
|
+
});
|
|
31428
|
+
if ((v.subject?.kind === "footage" ? [...v.subject.profiles] : resolveExportProfiles(v)).length < 1) ctx.addIssue({
|
|
30074
31429
|
code: ZodIssueCode.custom,
|
|
30075
31430
|
message: "pass profiles[] (min 1) or legacy profile",
|
|
30076
31431
|
path: ["profiles"]
|
|
@@ -32637,6 +33992,1329 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
32637
33992
|
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
|
|
32638
33993
|
new Set(Object.values(DeviceType));
|
|
32639
33994
|
DeviceFeature.BatteryOperated;
|
|
33995
|
+
/**
|
|
33996
|
+
* The vocabulary, in the order a picker offers it.
|
|
33997
|
+
*
|
|
33998
|
+
* Adding a key here is not enough to make it pickable: both glyph tables must
|
|
33999
|
+
* gain it too, and their tests fail until they do. That is the intended
|
|
34000
|
+
* friction — it is the only thing that keeps the two surfaces honest.
|
|
34001
|
+
*/
|
|
34002
|
+
var DISPLAY_ICON_VOCABULARY = [
|
|
34003
|
+
{
|
|
34004
|
+
key: "camera",
|
|
34005
|
+
group: "device",
|
|
34006
|
+
synonyms: [
|
|
34007
|
+
"telecamera",
|
|
34008
|
+
"videocamera",
|
|
34009
|
+
"camera",
|
|
34010
|
+
"cam"
|
|
34011
|
+
]
|
|
34012
|
+
},
|
|
34013
|
+
{
|
|
34014
|
+
key: "network",
|
|
34015
|
+
group: "device",
|
|
34016
|
+
synonyms: [
|
|
34017
|
+
"rete",
|
|
34018
|
+
"network",
|
|
34019
|
+
"lan"
|
|
34020
|
+
]
|
|
34021
|
+
},
|
|
34022
|
+
{
|
|
34023
|
+
key: "toggle-left",
|
|
34024
|
+
group: "device",
|
|
34025
|
+
synonyms: [
|
|
34026
|
+
"interruttore",
|
|
34027
|
+
"switch",
|
|
34028
|
+
"toggle"
|
|
34029
|
+
]
|
|
34030
|
+
},
|
|
34031
|
+
{
|
|
34032
|
+
key: "toggle-right",
|
|
34033
|
+
group: "device",
|
|
34034
|
+
synonyms: [
|
|
34035
|
+
"interruttore",
|
|
34036
|
+
"switch",
|
|
34037
|
+
"toggle",
|
|
34038
|
+
"acceso"
|
|
34039
|
+
]
|
|
34040
|
+
},
|
|
34041
|
+
{
|
|
34042
|
+
key: "circle-dot",
|
|
34043
|
+
group: "device",
|
|
34044
|
+
synonyms: [
|
|
34045
|
+
"pulsante",
|
|
34046
|
+
"bottone",
|
|
34047
|
+
"button",
|
|
34048
|
+
"dot"
|
|
34049
|
+
]
|
|
34050
|
+
},
|
|
34051
|
+
{
|
|
34052
|
+
key: "refresh-cw",
|
|
34053
|
+
group: "device",
|
|
34054
|
+
synonyms: [
|
|
34055
|
+
"aggiorna",
|
|
34056
|
+
"ricarica",
|
|
34057
|
+
"refresh",
|
|
34058
|
+
"reload",
|
|
34059
|
+
"ciclo"
|
|
34060
|
+
]
|
|
34061
|
+
},
|
|
34062
|
+
{
|
|
34063
|
+
key: "box",
|
|
34064
|
+
group: "device",
|
|
34065
|
+
synonyms: [
|
|
34066
|
+
"scatola",
|
|
34067
|
+
"box",
|
|
34068
|
+
"contenitore"
|
|
34069
|
+
]
|
|
34070
|
+
},
|
|
34071
|
+
{
|
|
34072
|
+
key: "scroll-text",
|
|
34073
|
+
group: "device",
|
|
34074
|
+
synonyms: [
|
|
34075
|
+
"registro",
|
|
34076
|
+
"log",
|
|
34077
|
+
"testo"
|
|
34078
|
+
]
|
|
34079
|
+
},
|
|
34080
|
+
{
|
|
34081
|
+
key: "workflow",
|
|
34082
|
+
group: "device",
|
|
34083
|
+
synonyms: [
|
|
34084
|
+
"automazione",
|
|
34085
|
+
"flusso",
|
|
34086
|
+
"workflow",
|
|
34087
|
+
"scenario"
|
|
34088
|
+
]
|
|
34089
|
+
},
|
|
34090
|
+
{
|
|
34091
|
+
key: "lock",
|
|
34092
|
+
group: "device",
|
|
34093
|
+
synonyms: [
|
|
34094
|
+
"serratura",
|
|
34095
|
+
"chiave",
|
|
34096
|
+
"lucchetto",
|
|
34097
|
+
"chiuso",
|
|
34098
|
+
"locked"
|
|
34099
|
+
]
|
|
34100
|
+
},
|
|
34101
|
+
{
|
|
34102
|
+
key: "lock-open",
|
|
34103
|
+
group: "device",
|
|
34104
|
+
synonyms: [
|
|
34105
|
+
"serratura",
|
|
34106
|
+
"aperta",
|
|
34107
|
+
"sbloccato",
|
|
34108
|
+
"unlocked"
|
|
34109
|
+
]
|
|
34110
|
+
},
|
|
34111
|
+
{
|
|
34112
|
+
key: "blinds",
|
|
34113
|
+
group: "device",
|
|
34114
|
+
synonyms: [
|
|
34115
|
+
"tapparella",
|
|
34116
|
+
"persiana",
|
|
34117
|
+
"serranda",
|
|
34118
|
+
"avvolgibile",
|
|
34119
|
+
"shutter",
|
|
34120
|
+
"blind",
|
|
34121
|
+
"veneziana"
|
|
34122
|
+
]
|
|
34123
|
+
},
|
|
34124
|
+
{
|
|
34125
|
+
key: "air-vent",
|
|
34126
|
+
group: "device",
|
|
34127
|
+
synonyms: [
|
|
34128
|
+
"bocchetta",
|
|
34129
|
+
"aria",
|
|
34130
|
+
"ventilazione",
|
|
34131
|
+
"condizionatore",
|
|
34132
|
+
"vent"
|
|
34133
|
+
]
|
|
34134
|
+
},
|
|
34135
|
+
{
|
|
34136
|
+
key: "monitor-play",
|
|
34137
|
+
group: "device",
|
|
34138
|
+
synonyms: [
|
|
34139
|
+
"schermo",
|
|
34140
|
+
"riproduzione",
|
|
34141
|
+
"player",
|
|
34142
|
+
"display"
|
|
34143
|
+
]
|
|
34144
|
+
},
|
|
34145
|
+
{
|
|
34146
|
+
key: "sliders-horizontal",
|
|
34147
|
+
group: "device",
|
|
34148
|
+
synonyms: [
|
|
34149
|
+
"regolazione",
|
|
34150
|
+
"cursori",
|
|
34151
|
+
"impostazioni",
|
|
34152
|
+
"slider"
|
|
34153
|
+
]
|
|
34154
|
+
},
|
|
34155
|
+
{
|
|
34156
|
+
key: "map-pin",
|
|
34157
|
+
group: "device",
|
|
34158
|
+
synonyms: [
|
|
34159
|
+
"posizione",
|
|
34160
|
+
"luogo",
|
|
34161
|
+
"mappa",
|
|
34162
|
+
"pin"
|
|
34163
|
+
]
|
|
34164
|
+
},
|
|
34165
|
+
{
|
|
34166
|
+
key: "bot",
|
|
34167
|
+
group: "device",
|
|
34168
|
+
synonyms: [
|
|
34169
|
+
"robot",
|
|
34170
|
+
"aspirapolvere",
|
|
34171
|
+
"bot"
|
|
34172
|
+
]
|
|
34173
|
+
},
|
|
34174
|
+
{
|
|
34175
|
+
key: "tractor",
|
|
34176
|
+
group: "device",
|
|
34177
|
+
synonyms: [
|
|
34178
|
+
"trattore",
|
|
34179
|
+
"tosaerba",
|
|
34180
|
+
"giardino"
|
|
34181
|
+
]
|
|
34182
|
+
},
|
|
34183
|
+
{
|
|
34184
|
+
key: "image",
|
|
34185
|
+
group: "device",
|
|
34186
|
+
synonyms: [
|
|
34187
|
+
"immagine",
|
|
34188
|
+
"foto",
|
|
34189
|
+
"snapshot"
|
|
34190
|
+
]
|
|
34191
|
+
},
|
|
34192
|
+
{
|
|
34193
|
+
key: "bone",
|
|
34194
|
+
group: "device",
|
|
34195
|
+
synonyms: ["osso", "cane"]
|
|
34196
|
+
},
|
|
34197
|
+
{
|
|
34198
|
+
key: "door-closed",
|
|
34199
|
+
group: "device",
|
|
34200
|
+
synonyms: [
|
|
34201
|
+
"porta",
|
|
34202
|
+
"chiusa",
|
|
34203
|
+
"contatto"
|
|
34204
|
+
]
|
|
34205
|
+
},
|
|
34206
|
+
{
|
|
34207
|
+
key: "door-open",
|
|
34208
|
+
group: "device",
|
|
34209
|
+
synonyms: [
|
|
34210
|
+
"porta",
|
|
34211
|
+
"aperta",
|
|
34212
|
+
"contatto",
|
|
34213
|
+
"ingresso",
|
|
34214
|
+
"uscita"
|
|
34215
|
+
]
|
|
34216
|
+
},
|
|
34217
|
+
{
|
|
34218
|
+
key: "crosshair",
|
|
34219
|
+
group: "device",
|
|
34220
|
+
synonyms: [
|
|
34221
|
+
"mirino",
|
|
34222
|
+
"puntamento",
|
|
34223
|
+
"centro"
|
|
34224
|
+
]
|
|
34225
|
+
},
|
|
34226
|
+
{
|
|
34227
|
+
key: "eye",
|
|
34228
|
+
group: "device",
|
|
34229
|
+
synonyms: [
|
|
34230
|
+
"occhio",
|
|
34231
|
+
"visibile",
|
|
34232
|
+
"visto"
|
|
34233
|
+
]
|
|
34234
|
+
},
|
|
34235
|
+
{
|
|
34236
|
+
key: "eye-off",
|
|
34237
|
+
group: "device",
|
|
34238
|
+
synonyms: [
|
|
34239
|
+
"privacy",
|
|
34240
|
+
"nascosto",
|
|
34241
|
+
"oscurato",
|
|
34242
|
+
"mascherato",
|
|
34243
|
+
"invisibile"
|
|
34244
|
+
]
|
|
34245
|
+
},
|
|
34246
|
+
{
|
|
34247
|
+
key: "smartphone",
|
|
34248
|
+
group: "device",
|
|
34249
|
+
synonyms: [
|
|
34250
|
+
"telefono",
|
|
34251
|
+
"cellulare",
|
|
34252
|
+
"smartphone"
|
|
34253
|
+
]
|
|
34254
|
+
},
|
|
34255
|
+
{
|
|
34256
|
+
key: "phone",
|
|
34257
|
+
group: "device",
|
|
34258
|
+
synonyms: ["telefono", "chiamata"]
|
|
34259
|
+
},
|
|
34260
|
+
{
|
|
34261
|
+
key: "tablet",
|
|
34262
|
+
group: "device",
|
|
34263
|
+
synonyms: ["tablet", "ipad"]
|
|
34264
|
+
},
|
|
34265
|
+
{
|
|
34266
|
+
key: "laptop",
|
|
34267
|
+
group: "device",
|
|
34268
|
+
synonyms: [
|
|
34269
|
+
"portatile",
|
|
34270
|
+
"laptop",
|
|
34271
|
+
"notebook"
|
|
34272
|
+
]
|
|
34273
|
+
},
|
|
34274
|
+
{
|
|
34275
|
+
key: "monitor",
|
|
34276
|
+
group: "device",
|
|
34277
|
+
synonyms: ["monitor", "schermo"]
|
|
34278
|
+
},
|
|
34279
|
+
{
|
|
34280
|
+
key: "tv",
|
|
34281
|
+
group: "device",
|
|
34282
|
+
synonyms: ["televisore", "tv"]
|
|
34283
|
+
},
|
|
34284
|
+
{
|
|
34285
|
+
key: "speaker",
|
|
34286
|
+
group: "device",
|
|
34287
|
+
synonyms: [
|
|
34288
|
+
"altoparlante",
|
|
34289
|
+
"cassa",
|
|
34290
|
+
"speaker"
|
|
34291
|
+
]
|
|
34292
|
+
},
|
|
34293
|
+
{
|
|
34294
|
+
key: "printer",
|
|
34295
|
+
group: "device",
|
|
34296
|
+
synonyms: ["stampante"]
|
|
34297
|
+
},
|
|
34298
|
+
{
|
|
34299
|
+
key: "projector",
|
|
34300
|
+
group: "device",
|
|
34301
|
+
synonyms: ["proiettore"]
|
|
34302
|
+
},
|
|
34303
|
+
{
|
|
34304
|
+
key: "gamepad-2",
|
|
34305
|
+
group: "device",
|
|
34306
|
+
synonyms: [
|
|
34307
|
+
"console",
|
|
34308
|
+
"gioco",
|
|
34309
|
+
"joystick"
|
|
34310
|
+
]
|
|
34311
|
+
},
|
|
34312
|
+
{
|
|
34313
|
+
key: "radio",
|
|
34314
|
+
group: "device",
|
|
34315
|
+
synonyms: ["radio"]
|
|
34316
|
+
},
|
|
34317
|
+
{
|
|
34318
|
+
key: "router",
|
|
34319
|
+
group: "device",
|
|
34320
|
+
synonyms: ["router", "modem"]
|
|
34321
|
+
},
|
|
34322
|
+
{
|
|
34323
|
+
key: "bluetooth",
|
|
34324
|
+
group: "device",
|
|
34325
|
+
synonyms: ["bluetooth"]
|
|
34326
|
+
},
|
|
34327
|
+
{
|
|
34328
|
+
key: "cpu",
|
|
34329
|
+
group: "device",
|
|
34330
|
+
synonyms: [
|
|
34331
|
+
"processore",
|
|
34332
|
+
"cpu",
|
|
34333
|
+
"chip",
|
|
34334
|
+
"dispositivo"
|
|
34335
|
+
]
|
|
34336
|
+
},
|
|
34337
|
+
{
|
|
34338
|
+
key: "hard-drive",
|
|
34339
|
+
group: "device",
|
|
34340
|
+
synonyms: [
|
|
34341
|
+
"disco",
|
|
34342
|
+
"hdd",
|
|
34343
|
+
"archiviazione",
|
|
34344
|
+
"storage"
|
|
34345
|
+
]
|
|
34346
|
+
},
|
|
34347
|
+
{
|
|
34348
|
+
key: "server",
|
|
34349
|
+
group: "device",
|
|
34350
|
+
synonyms: ["server", "nas"]
|
|
34351
|
+
},
|
|
34352
|
+
{
|
|
34353
|
+
key: "mic",
|
|
34354
|
+
group: "device",
|
|
34355
|
+
synonyms: ["microfono", "audio"]
|
|
34356
|
+
},
|
|
34357
|
+
{
|
|
34358
|
+
key: "mic-off",
|
|
34359
|
+
group: "device",
|
|
34360
|
+
synonyms: [
|
|
34361
|
+
"microfono",
|
|
34362
|
+
"spento",
|
|
34363
|
+
"muto"
|
|
34364
|
+
]
|
|
34365
|
+
},
|
|
34366
|
+
{
|
|
34367
|
+
key: "video",
|
|
34368
|
+
group: "device",
|
|
34369
|
+
synonyms: ["video"]
|
|
34370
|
+
},
|
|
34371
|
+
{
|
|
34372
|
+
key: "video-off",
|
|
34373
|
+
group: "device",
|
|
34374
|
+
synonyms: ["video", "spento"]
|
|
34375
|
+
},
|
|
34376
|
+
{
|
|
34377
|
+
key: "music",
|
|
34378
|
+
group: "device",
|
|
34379
|
+
synonyms: ["musica"]
|
|
34380
|
+
},
|
|
34381
|
+
{
|
|
34382
|
+
key: "plug",
|
|
34383
|
+
group: "device",
|
|
34384
|
+
synonyms: [
|
|
34385
|
+
"presa",
|
|
34386
|
+
"spina",
|
|
34387
|
+
"plug"
|
|
34388
|
+
]
|
|
34389
|
+
},
|
|
34390
|
+
{
|
|
34391
|
+
key: "plug-zap",
|
|
34392
|
+
group: "device",
|
|
34393
|
+
synonyms: ["presa", "alimentata"]
|
|
34394
|
+
},
|
|
34395
|
+
{
|
|
34396
|
+
key: "power",
|
|
34397
|
+
group: "device",
|
|
34398
|
+
synonyms: [
|
|
34399
|
+
"accensione",
|
|
34400
|
+
"alimentazione",
|
|
34401
|
+
"power"
|
|
34402
|
+
]
|
|
34403
|
+
},
|
|
34404
|
+
{
|
|
34405
|
+
key: "power-off",
|
|
34406
|
+
group: "device",
|
|
34407
|
+
synonyms: ["spegnimento", "spento"]
|
|
34408
|
+
},
|
|
34409
|
+
{
|
|
34410
|
+
key: "cable",
|
|
34411
|
+
group: "device",
|
|
34412
|
+
synonyms: ["cavo"]
|
|
34413
|
+
},
|
|
34414
|
+
{
|
|
34415
|
+
key: "battery",
|
|
34416
|
+
group: "device",
|
|
34417
|
+
synonyms: ["batteria"]
|
|
34418
|
+
},
|
|
34419
|
+
{
|
|
34420
|
+
key: "battery-charging",
|
|
34421
|
+
group: "device",
|
|
34422
|
+
synonyms: [
|
|
34423
|
+
"batteria",
|
|
34424
|
+
"carica",
|
|
34425
|
+
"ricarica"
|
|
34426
|
+
]
|
|
34427
|
+
},
|
|
34428
|
+
{
|
|
34429
|
+
key: "battery-full",
|
|
34430
|
+
group: "device",
|
|
34431
|
+
synonyms: [
|
|
34432
|
+
"batteria",
|
|
34433
|
+
"carica",
|
|
34434
|
+
"piena"
|
|
34435
|
+
]
|
|
34436
|
+
},
|
|
34437
|
+
{
|
|
34438
|
+
key: "battery-low",
|
|
34439
|
+
group: "device",
|
|
34440
|
+
synonyms: ["batteria", "scarica"]
|
|
34441
|
+
},
|
|
34442
|
+
{
|
|
34443
|
+
key: "key",
|
|
34444
|
+
group: "device",
|
|
34445
|
+
synonyms: ["chiave"]
|
|
34446
|
+
},
|
|
34447
|
+
{
|
|
34448
|
+
key: "key-round",
|
|
34449
|
+
group: "device",
|
|
34450
|
+
synonyms: ["chiave", "tonda"]
|
|
34451
|
+
},
|
|
34452
|
+
{
|
|
34453
|
+
key: "mailbox",
|
|
34454
|
+
group: "device",
|
|
34455
|
+
synonyms: [
|
|
34456
|
+
"cassetta",
|
|
34457
|
+
"postale",
|
|
34458
|
+
"posta"
|
|
34459
|
+
]
|
|
34460
|
+
},
|
|
34461
|
+
{
|
|
34462
|
+
key: "package",
|
|
34463
|
+
group: "device",
|
|
34464
|
+
synonyms: ["pacco", "consegna"]
|
|
34465
|
+
},
|
|
34466
|
+
{
|
|
34467
|
+
key: "shield",
|
|
34468
|
+
group: "device",
|
|
34469
|
+
synonyms: [
|
|
34470
|
+
"scudo",
|
|
34471
|
+
"protezione",
|
|
34472
|
+
"sicurezza",
|
|
34473
|
+
"allarme"
|
|
34474
|
+
]
|
|
34475
|
+
},
|
|
34476
|
+
{
|
|
34477
|
+
key: "shield-check",
|
|
34478
|
+
group: "device",
|
|
34479
|
+
synonyms: [
|
|
34480
|
+
"protetto",
|
|
34481
|
+
"sicuro",
|
|
34482
|
+
"armato"
|
|
34483
|
+
]
|
|
34484
|
+
},
|
|
34485
|
+
{
|
|
34486
|
+
key: "shield-off",
|
|
34487
|
+
group: "device",
|
|
34488
|
+
synonyms: [
|
|
34489
|
+
"protezione",
|
|
34490
|
+
"disattivata",
|
|
34491
|
+
"disarmato"
|
|
34492
|
+
]
|
|
34493
|
+
},
|
|
34494
|
+
{
|
|
34495
|
+
key: "briefcase",
|
|
34496
|
+
group: "device",
|
|
34497
|
+
synonyms: [
|
|
34498
|
+
"ufficio",
|
|
34499
|
+
"lavoro",
|
|
34500
|
+
"valigetta",
|
|
34501
|
+
"studio"
|
|
34502
|
+
]
|
|
34503
|
+
},
|
|
34504
|
+
{
|
|
34505
|
+
key: "lightbulb",
|
|
34506
|
+
group: "lighting",
|
|
34507
|
+
synonyms: [
|
|
34508
|
+
"luce",
|
|
34509
|
+
"lampadina",
|
|
34510
|
+
"lampada",
|
|
34511
|
+
"light"
|
|
34512
|
+
]
|
|
34513
|
+
},
|
|
34514
|
+
{
|
|
34515
|
+
key: "lamp",
|
|
34516
|
+
group: "lighting",
|
|
34517
|
+
synonyms: ["lampada", "abatjour"]
|
|
34518
|
+
},
|
|
34519
|
+
{
|
|
34520
|
+
key: "lamp-ceiling",
|
|
34521
|
+
group: "lighting",
|
|
34522
|
+
synonyms: [
|
|
34523
|
+
"plafoniera",
|
|
34524
|
+
"lampadario",
|
|
34525
|
+
"luce",
|
|
34526
|
+
"soffitto"
|
|
34527
|
+
]
|
|
34528
|
+
},
|
|
34529
|
+
{
|
|
34530
|
+
key: "lamp-desk",
|
|
34531
|
+
group: "lighting",
|
|
34532
|
+
synonyms: [
|
|
34533
|
+
"lampada",
|
|
34534
|
+
"scrivania",
|
|
34535
|
+
"abatjour"
|
|
34536
|
+
]
|
|
34537
|
+
},
|
|
34538
|
+
{
|
|
34539
|
+
key: "lamp-floor",
|
|
34540
|
+
group: "lighting",
|
|
34541
|
+
synonyms: [
|
|
34542
|
+
"piantana",
|
|
34543
|
+
"lampada",
|
|
34544
|
+
"terra"
|
|
34545
|
+
]
|
|
34546
|
+
},
|
|
34547
|
+
{
|
|
34548
|
+
key: "flashlight",
|
|
34549
|
+
group: "lighting",
|
|
34550
|
+
synonyms: [
|
|
34551
|
+
"faretto",
|
|
34552
|
+
"torcia",
|
|
34553
|
+
"spot",
|
|
34554
|
+
"faro"
|
|
34555
|
+
]
|
|
34556
|
+
},
|
|
34557
|
+
{
|
|
34558
|
+
key: "heater",
|
|
34559
|
+
group: "lighting",
|
|
34560
|
+
synonyms: [
|
|
34561
|
+
"riscaldamento",
|
|
34562
|
+
"termosifone",
|
|
34563
|
+
"stufa",
|
|
34564
|
+
"calore"
|
|
34565
|
+
]
|
|
34566
|
+
},
|
|
34567
|
+
{
|
|
34568
|
+
key: "snowflake",
|
|
34569
|
+
group: "lighting",
|
|
34570
|
+
synonyms: [
|
|
34571
|
+
"freddo",
|
|
34572
|
+
"raffrescamento",
|
|
34573
|
+
"aria",
|
|
34574
|
+
"condizionata",
|
|
34575
|
+
"neve"
|
|
34576
|
+
]
|
|
34577
|
+
},
|
|
34578
|
+
{
|
|
34579
|
+
key: "thermometer-sun",
|
|
34580
|
+
group: "lighting",
|
|
34581
|
+
synonyms: [
|
|
34582
|
+
"caldo",
|
|
34583
|
+
"temperatura",
|
|
34584
|
+
"alta"
|
|
34585
|
+
]
|
|
34586
|
+
},
|
|
34587
|
+
{
|
|
34588
|
+
key: "thermometer-snowflake",
|
|
34589
|
+
group: "lighting",
|
|
34590
|
+
synonyms: [
|
|
34591
|
+
"freddo",
|
|
34592
|
+
"temperatura",
|
|
34593
|
+
"bassa"
|
|
34594
|
+
]
|
|
34595
|
+
},
|
|
34596
|
+
{
|
|
34597
|
+
key: "refrigerator",
|
|
34598
|
+
group: "lighting",
|
|
34599
|
+
synonyms: ["frigorifero", "frigo"]
|
|
34600
|
+
},
|
|
34601
|
+
{
|
|
34602
|
+
key: "microwave",
|
|
34603
|
+
group: "lighting",
|
|
34604
|
+
synonyms: ["microonde", "forno"]
|
|
34605
|
+
},
|
|
34606
|
+
{
|
|
34607
|
+
key: "washing-machine",
|
|
34608
|
+
group: "lighting",
|
|
34609
|
+
synonyms: [
|
|
34610
|
+
"lavatrice",
|
|
34611
|
+
"lavastoviglie",
|
|
34612
|
+
"asciugatrice"
|
|
34613
|
+
]
|
|
34614
|
+
},
|
|
34615
|
+
{
|
|
34616
|
+
key: "cooking-pot",
|
|
34617
|
+
group: "lighting",
|
|
34618
|
+
synonyms: [
|
|
34619
|
+
"pentola",
|
|
34620
|
+
"cucina",
|
|
34621
|
+
"fornello"
|
|
34622
|
+
]
|
|
34623
|
+
},
|
|
34624
|
+
{
|
|
34625
|
+
key: "utensils",
|
|
34626
|
+
group: "lighting",
|
|
34627
|
+
synonyms: [
|
|
34628
|
+
"cucina",
|
|
34629
|
+
"posate",
|
|
34630
|
+
"sala",
|
|
34631
|
+
"pranzo"
|
|
34632
|
+
]
|
|
34633
|
+
},
|
|
34634
|
+
{
|
|
34635
|
+
key: "thermometer",
|
|
34636
|
+
group: "sensing",
|
|
34637
|
+
synonyms: [
|
|
34638
|
+
"temperatura",
|
|
34639
|
+
"termometro",
|
|
34640
|
+
"termostato"
|
|
34641
|
+
]
|
|
34642
|
+
},
|
|
34643
|
+
{
|
|
34644
|
+
key: "droplets",
|
|
34645
|
+
group: "sensing",
|
|
34646
|
+
synonyms: [
|
|
34647
|
+
"umidita",
|
|
34648
|
+
"acqua",
|
|
34649
|
+
"allagamento",
|
|
34650
|
+
"perdita"
|
|
34651
|
+
]
|
|
34652
|
+
},
|
|
34653
|
+
{
|
|
34654
|
+
key: "droplet",
|
|
34655
|
+
group: "sensing",
|
|
34656
|
+
synonyms: [
|
|
34657
|
+
"goccia",
|
|
34658
|
+
"acqua",
|
|
34659
|
+
"perdita",
|
|
34660
|
+
"allagamento"
|
|
34661
|
+
]
|
|
34662
|
+
},
|
|
34663
|
+
{
|
|
34664
|
+
key: "gauge",
|
|
34665
|
+
group: "sensing",
|
|
34666
|
+
synonyms: [
|
|
34667
|
+
"misura",
|
|
34668
|
+
"livello",
|
|
34669
|
+
"pressione",
|
|
34670
|
+
"indicatore"
|
|
34671
|
+
]
|
|
34672
|
+
},
|
|
34673
|
+
{
|
|
34674
|
+
key: "gauge2",
|
|
34675
|
+
group: "sensing",
|
|
34676
|
+
synonyms: [
|
|
34677
|
+
"misura",
|
|
34678
|
+
"livello",
|
|
34679
|
+
"pressione",
|
|
34680
|
+
"indicatore"
|
|
34681
|
+
],
|
|
34682
|
+
aliasOf: "gauge"
|
|
34683
|
+
},
|
|
34684
|
+
{
|
|
34685
|
+
key: "sun",
|
|
34686
|
+
group: "sensing",
|
|
34687
|
+
synonyms: [
|
|
34688
|
+
"sole",
|
|
34689
|
+
"luminosita",
|
|
34690
|
+
"giorno",
|
|
34691
|
+
"esterno"
|
|
34692
|
+
]
|
|
34693
|
+
},
|
|
34694
|
+
{
|
|
34695
|
+
key: "sun-medium",
|
|
34696
|
+
group: "sensing",
|
|
34697
|
+
synonyms: ["sole", "luminosita"]
|
|
34698
|
+
},
|
|
34699
|
+
{
|
|
34700
|
+
key: "sunrise",
|
|
34701
|
+
group: "sensing",
|
|
34702
|
+
synonyms: ["alba"]
|
|
34703
|
+
},
|
|
34704
|
+
{
|
|
34705
|
+
key: "moon",
|
|
34706
|
+
group: "sensing",
|
|
34707
|
+
synonyms: [
|
|
34708
|
+
"notte",
|
|
34709
|
+
"luna",
|
|
34710
|
+
"notturna",
|
|
34711
|
+
"nightvision"
|
|
34712
|
+
]
|
|
34713
|
+
},
|
|
34714
|
+
{
|
|
34715
|
+
key: "wind",
|
|
34716
|
+
group: "sensing",
|
|
34717
|
+
synonyms: [
|
|
34718
|
+
"vento",
|
|
34719
|
+
"aria",
|
|
34720
|
+
"gas",
|
|
34721
|
+
"monossido",
|
|
34722
|
+
"co"
|
|
34723
|
+
]
|
|
34724
|
+
},
|
|
34725
|
+
{
|
|
34726
|
+
key: "cloud",
|
|
34727
|
+
group: "sensing",
|
|
34728
|
+
synonyms: ["nuvola", "nuvoloso"]
|
|
34729
|
+
},
|
|
34730
|
+
{
|
|
34731
|
+
key: "cloud-rain",
|
|
34732
|
+
group: "sensing",
|
|
34733
|
+
synonyms: ["pioggia"]
|
|
34734
|
+
},
|
|
34735
|
+
{
|
|
34736
|
+
key: "cloud-sun",
|
|
34737
|
+
group: "sensing",
|
|
34738
|
+
synonyms: [
|
|
34739
|
+
"variabile",
|
|
34740
|
+
"sole",
|
|
34741
|
+
"nuvole"
|
|
34742
|
+
]
|
|
34743
|
+
},
|
|
34744
|
+
{
|
|
34745
|
+
key: "waves",
|
|
34746
|
+
group: "sensing",
|
|
34747
|
+
synonyms: [
|
|
34748
|
+
"onde",
|
|
34749
|
+
"acqua",
|
|
34750
|
+
"vibrazione"
|
|
34751
|
+
]
|
|
34752
|
+
},
|
|
34753
|
+
{
|
|
34754
|
+
key: "sprout",
|
|
34755
|
+
group: "sensing",
|
|
34756
|
+
synonyms: [
|
|
34757
|
+
"irrigazione",
|
|
34758
|
+
"pianta",
|
|
34759
|
+
"germoglio",
|
|
34760
|
+
"orto"
|
|
34761
|
+
]
|
|
34762
|
+
},
|
|
34763
|
+
{
|
|
34764
|
+
key: "leaf",
|
|
34765
|
+
group: "sensing",
|
|
34766
|
+
synonyms: [
|
|
34767
|
+
"foglia",
|
|
34768
|
+
"pianta",
|
|
34769
|
+
"verde"
|
|
34770
|
+
]
|
|
34771
|
+
},
|
|
34772
|
+
{
|
|
34773
|
+
key: "flower",
|
|
34774
|
+
group: "sensing",
|
|
34775
|
+
synonyms: ["fiore", "pianta"]
|
|
34776
|
+
},
|
|
34777
|
+
{
|
|
34778
|
+
key: "zap",
|
|
34779
|
+
group: "sensing",
|
|
34780
|
+
synonyms: [
|
|
34781
|
+
"energia",
|
|
34782
|
+
"corrente",
|
|
34783
|
+
"consumo",
|
|
34784
|
+
"watt",
|
|
34785
|
+
"elettricita"
|
|
34786
|
+
]
|
|
34787
|
+
},
|
|
34788
|
+
{
|
|
34789
|
+
key: "flame",
|
|
34790
|
+
group: "sensing",
|
|
34791
|
+
synonyms: [
|
|
34792
|
+
"fiamma",
|
|
34793
|
+
"gas",
|
|
34794
|
+
"fuoco",
|
|
34795
|
+
"incendio"
|
|
34796
|
+
]
|
|
34797
|
+
},
|
|
34798
|
+
{
|
|
34799
|
+
key: "bell",
|
|
34800
|
+
group: "sensing",
|
|
34801
|
+
synonyms: [
|
|
34802
|
+
"campanello",
|
|
34803
|
+
"suoneria",
|
|
34804
|
+
"notifica",
|
|
34805
|
+
"bell",
|
|
34806
|
+
"chime"
|
|
34807
|
+
]
|
|
34808
|
+
},
|
|
34809
|
+
{
|
|
34810
|
+
key: "bell-ring",
|
|
34811
|
+
group: "sensing",
|
|
34812
|
+
synonyms: [
|
|
34813
|
+
"campanello",
|
|
34814
|
+
"suona",
|
|
34815
|
+
"doorbell",
|
|
34816
|
+
"notifica"
|
|
34817
|
+
]
|
|
34818
|
+
},
|
|
34819
|
+
{
|
|
34820
|
+
key: "bell-off",
|
|
34821
|
+
group: "sensing",
|
|
34822
|
+
synonyms: [
|
|
34823
|
+
"silenzioso",
|
|
34824
|
+
"notifiche",
|
|
34825
|
+
"disattivate",
|
|
34826
|
+
"muto"
|
|
34827
|
+
]
|
|
34828
|
+
},
|
|
34829
|
+
{
|
|
34830
|
+
key: "siren",
|
|
34831
|
+
group: "sensing",
|
|
34832
|
+
synonyms: ["sirena", "allarme"]
|
|
34833
|
+
},
|
|
34834
|
+
{
|
|
34835
|
+
key: "radar",
|
|
34836
|
+
group: "sensing",
|
|
34837
|
+
synonyms: [
|
|
34838
|
+
"movimento",
|
|
34839
|
+
"presenza",
|
|
34840
|
+
"pir",
|
|
34841
|
+
"rilevamento",
|
|
34842
|
+
"radar"
|
|
34843
|
+
]
|
|
34844
|
+
},
|
|
34845
|
+
{
|
|
34846
|
+
key: "activity",
|
|
34847
|
+
group: "sensing",
|
|
34848
|
+
synonyms: [
|
|
34849
|
+
"attivita",
|
|
34850
|
+
"sensore",
|
|
34851
|
+
"generico"
|
|
34852
|
+
]
|
|
34853
|
+
},
|
|
34854
|
+
{
|
|
34855
|
+
key: "hash",
|
|
34856
|
+
group: "sensing",
|
|
34857
|
+
synonyms: [
|
|
34858
|
+
"numero",
|
|
34859
|
+
"contatore",
|
|
34860
|
+
"valore"
|
|
34861
|
+
]
|
|
34862
|
+
},
|
|
34863
|
+
{
|
|
34864
|
+
key: "clock",
|
|
34865
|
+
group: "sensing",
|
|
34866
|
+
synonyms: [
|
|
34867
|
+
"orologio",
|
|
34868
|
+
"ora",
|
|
34869
|
+
"tempo"
|
|
34870
|
+
]
|
|
34871
|
+
},
|
|
34872
|
+
{
|
|
34873
|
+
key: "timer",
|
|
34874
|
+
group: "sensing",
|
|
34875
|
+
synonyms: [
|
|
34876
|
+
"timer",
|
|
34877
|
+
"conto",
|
|
34878
|
+
"rovescia"
|
|
34879
|
+
]
|
|
34880
|
+
},
|
|
34881
|
+
{
|
|
34882
|
+
key: "calendar",
|
|
34883
|
+
group: "sensing",
|
|
34884
|
+
synonyms: [
|
|
34885
|
+
"calendario",
|
|
34886
|
+
"data",
|
|
34887
|
+
"pianificazione"
|
|
34888
|
+
]
|
|
34889
|
+
},
|
|
34890
|
+
{
|
|
34891
|
+
key: "pipette",
|
|
34892
|
+
group: "sensing",
|
|
34893
|
+
synonyms: [
|
|
34894
|
+
"qualita",
|
|
34895
|
+
"aria",
|
|
34896
|
+
"misura",
|
|
34897
|
+
"campione"
|
|
34898
|
+
]
|
|
34899
|
+
},
|
|
34900
|
+
{
|
|
34901
|
+
key: "fan",
|
|
34902
|
+
group: "sensing",
|
|
34903
|
+
synonyms: [
|
|
34904
|
+
"ventilatore",
|
|
34905
|
+
"ventola",
|
|
34906
|
+
"aria"
|
|
34907
|
+
]
|
|
34908
|
+
},
|
|
34909
|
+
{
|
|
34910
|
+
key: "bath",
|
|
34911
|
+
group: "sensing",
|
|
34912
|
+
synonyms: [
|
|
34913
|
+
"bagno",
|
|
34914
|
+
"vasca",
|
|
34915
|
+
"doccia"
|
|
34916
|
+
]
|
|
34917
|
+
},
|
|
34918
|
+
{
|
|
34919
|
+
key: "footprints",
|
|
34920
|
+
group: "sensing",
|
|
34921
|
+
synonyms: [
|
|
34922
|
+
"movimento",
|
|
34923
|
+
"passi",
|
|
34924
|
+
"presenza",
|
|
34925
|
+
"motion"
|
|
34926
|
+
]
|
|
34927
|
+
},
|
|
34928
|
+
{
|
|
34929
|
+
key: "alarm-smoke",
|
|
34930
|
+
group: "sensing",
|
|
34931
|
+
synonyms: [
|
|
34932
|
+
"fumo",
|
|
34933
|
+
"antincendio",
|
|
34934
|
+
"rilevatore"
|
|
34935
|
+
]
|
|
34936
|
+
},
|
|
34937
|
+
{
|
|
34938
|
+
key: "shield-alert",
|
|
34939
|
+
group: "sensing",
|
|
34940
|
+
synonyms: [
|
|
34941
|
+
"manomissione",
|
|
34942
|
+
"allarme",
|
|
34943
|
+
"tamper",
|
|
34944
|
+
"avviso"
|
|
34945
|
+
]
|
|
34946
|
+
},
|
|
34947
|
+
{
|
|
34948
|
+
key: "vibrate",
|
|
34949
|
+
group: "sensing",
|
|
34950
|
+
synonyms: [
|
|
34951
|
+
"vibrazione",
|
|
34952
|
+
"urto",
|
|
34953
|
+
"tamper"
|
|
34954
|
+
]
|
|
34955
|
+
},
|
|
34956
|
+
{
|
|
34957
|
+
key: "wifi",
|
|
34958
|
+
group: "sensing",
|
|
34959
|
+
synonyms: [
|
|
34960
|
+
"wifi",
|
|
34961
|
+
"rete",
|
|
34962
|
+
"segnale"
|
|
34963
|
+
]
|
|
34964
|
+
},
|
|
34965
|
+
{
|
|
34966
|
+
key: "wifi-off",
|
|
34967
|
+
group: "sensing",
|
|
34968
|
+
synonyms: [
|
|
34969
|
+
"wifi",
|
|
34970
|
+
"assente",
|
|
34971
|
+
"offline",
|
|
34972
|
+
"disconnesso"
|
|
34973
|
+
]
|
|
34974
|
+
},
|
|
34975
|
+
{
|
|
34976
|
+
key: "volume-2",
|
|
34977
|
+
group: "sensing",
|
|
34978
|
+
synonyms: [
|
|
34979
|
+
"volume",
|
|
34980
|
+
"audio",
|
|
34981
|
+
"altoparlante",
|
|
34982
|
+
"suono"
|
|
34983
|
+
]
|
|
34984
|
+
},
|
|
34985
|
+
{
|
|
34986
|
+
key: "volume-x",
|
|
34987
|
+
group: "sensing",
|
|
34988
|
+
synonyms: [
|
|
34989
|
+
"muto",
|
|
34990
|
+
"silenziato",
|
|
34991
|
+
"volume",
|
|
34992
|
+
"spento"
|
|
34993
|
+
]
|
|
34994
|
+
},
|
|
34995
|
+
{
|
|
34996
|
+
key: "heart",
|
|
34997
|
+
group: "sensing",
|
|
34998
|
+
synonyms: [
|
|
34999
|
+
"cuore",
|
|
35000
|
+
"preferito",
|
|
35001
|
+
"salute"
|
|
35002
|
+
]
|
|
35003
|
+
},
|
|
35004
|
+
{
|
|
35005
|
+
key: "heart-pulse",
|
|
35006
|
+
group: "sensing",
|
|
35007
|
+
synonyms: [
|
|
35008
|
+
"battito",
|
|
35009
|
+
"salute",
|
|
35010
|
+
"monitoraggio"
|
|
35011
|
+
]
|
|
35012
|
+
},
|
|
35013
|
+
{
|
|
35014
|
+
key: "star",
|
|
35015
|
+
group: "sensing",
|
|
35016
|
+
synonyms: [
|
|
35017
|
+
"preferito",
|
|
35018
|
+
"stella",
|
|
35019
|
+
"importante"
|
|
35020
|
+
]
|
|
35021
|
+
},
|
|
35022
|
+
{
|
|
35023
|
+
key: "sparkles",
|
|
35024
|
+
group: "sensing",
|
|
35025
|
+
synonyms: [
|
|
35026
|
+
"automatico",
|
|
35027
|
+
"intelligente",
|
|
35028
|
+
"ai",
|
|
35029
|
+
"scena"
|
|
35030
|
+
]
|
|
35031
|
+
},
|
|
35032
|
+
{
|
|
35033
|
+
key: "house",
|
|
35034
|
+
group: "place",
|
|
35035
|
+
synonyms: [
|
|
35036
|
+
"casa",
|
|
35037
|
+
"abitazione",
|
|
35038
|
+
"home"
|
|
35039
|
+
]
|
|
35040
|
+
},
|
|
35041
|
+
{
|
|
35042
|
+
key: "sofa",
|
|
35043
|
+
group: "place",
|
|
35044
|
+
synonyms: [
|
|
35045
|
+
"salone",
|
|
35046
|
+
"soggiorno",
|
|
35047
|
+
"divano",
|
|
35048
|
+
"living"
|
|
35049
|
+
]
|
|
35050
|
+
},
|
|
35051
|
+
{
|
|
35052
|
+
key: "bed-double",
|
|
35053
|
+
group: "place",
|
|
35054
|
+
synonyms: [
|
|
35055
|
+
"camera",
|
|
35056
|
+
"letto",
|
|
35057
|
+
"matrimoniale"
|
|
35058
|
+
]
|
|
35059
|
+
},
|
|
35060
|
+
{
|
|
35061
|
+
key: "bed",
|
|
35062
|
+
group: "place",
|
|
35063
|
+
synonyms: [
|
|
35064
|
+
"cameretta",
|
|
35065
|
+
"letto",
|
|
35066
|
+
"singolo"
|
|
35067
|
+
]
|
|
35068
|
+
},
|
|
35069
|
+
{
|
|
35070
|
+
key: "warehouse",
|
|
35071
|
+
group: "place",
|
|
35072
|
+
synonyms: [
|
|
35073
|
+
"magazzino",
|
|
35074
|
+
"capanno",
|
|
35075
|
+
"deposito",
|
|
35076
|
+
"cantina"
|
|
35077
|
+
]
|
|
35078
|
+
},
|
|
35079
|
+
{
|
|
35080
|
+
key: "trees",
|
|
35081
|
+
group: "place",
|
|
35082
|
+
synonyms: [
|
|
35083
|
+
"giardino",
|
|
35084
|
+
"alberi",
|
|
35085
|
+
"esterno",
|
|
35086
|
+
"parco"
|
|
35087
|
+
]
|
|
35088
|
+
},
|
|
35089
|
+
{
|
|
35090
|
+
key: "tree-pine",
|
|
35091
|
+
group: "place",
|
|
35092
|
+
synonyms: [
|
|
35093
|
+
"albero",
|
|
35094
|
+
"pino",
|
|
35095
|
+
"giardino"
|
|
35096
|
+
]
|
|
35097
|
+
},
|
|
35098
|
+
{
|
|
35099
|
+
key: "fence",
|
|
35100
|
+
group: "place",
|
|
35101
|
+
synonyms: [
|
|
35102
|
+
"recinto",
|
|
35103
|
+
"cancello",
|
|
35104
|
+
"staccionata",
|
|
35105
|
+
"perimetro"
|
|
35106
|
+
]
|
|
35107
|
+
},
|
|
35108
|
+
{
|
|
35109
|
+
key: "store",
|
|
35110
|
+
group: "place",
|
|
35111
|
+
synonyms: [
|
|
35112
|
+
"negozio",
|
|
35113
|
+
"ingresso",
|
|
35114
|
+
"vetrina"
|
|
35115
|
+
]
|
|
35116
|
+
},
|
|
35117
|
+
{
|
|
35118
|
+
key: "building",
|
|
35119
|
+
group: "place",
|
|
35120
|
+
synonyms: ["edificio", "palazzo"]
|
|
35121
|
+
},
|
|
35122
|
+
{
|
|
35123
|
+
key: "building-2",
|
|
35124
|
+
group: "place",
|
|
35125
|
+
synonyms: [
|
|
35126
|
+
"edificio",
|
|
35127
|
+
"uffici",
|
|
35128
|
+
"condominio"
|
|
35129
|
+
]
|
|
35130
|
+
},
|
|
35131
|
+
{
|
|
35132
|
+
key: "mountain",
|
|
35133
|
+
group: "place",
|
|
35134
|
+
synonyms: [
|
|
35135
|
+
"montagna",
|
|
35136
|
+
"esterno",
|
|
35137
|
+
"panorama"
|
|
35138
|
+
]
|
|
35139
|
+
},
|
|
35140
|
+
{
|
|
35141
|
+
key: "umbrella",
|
|
35142
|
+
group: "place",
|
|
35143
|
+
synonyms: [
|
|
35144
|
+
"tenda",
|
|
35145
|
+
"markise",
|
|
35146
|
+
"ombrellone",
|
|
35147
|
+
"pergola",
|
|
35148
|
+
"veranda"
|
|
35149
|
+
]
|
|
35150
|
+
},
|
|
35151
|
+
{
|
|
35152
|
+
key: "dumbbell",
|
|
35153
|
+
group: "place",
|
|
35154
|
+
synonyms: ["palestra", "attrezzi"]
|
|
35155
|
+
},
|
|
35156
|
+
{
|
|
35157
|
+
key: "wrench",
|
|
35158
|
+
group: "place",
|
|
35159
|
+
synonyms: [
|
|
35160
|
+
"officina",
|
|
35161
|
+
"manutenzione",
|
|
35162
|
+
"attrezzi",
|
|
35163
|
+
"utenze"
|
|
35164
|
+
]
|
|
35165
|
+
},
|
|
35166
|
+
{
|
|
35167
|
+
key: "hammer",
|
|
35168
|
+
group: "place",
|
|
35169
|
+
synonyms: [
|
|
35170
|
+
"officina",
|
|
35171
|
+
"martello",
|
|
35172
|
+
"lavori"
|
|
35173
|
+
]
|
|
35174
|
+
},
|
|
35175
|
+
{
|
|
35176
|
+
key: "drill",
|
|
35177
|
+
group: "place",
|
|
35178
|
+
synonyms: [
|
|
35179
|
+
"officina",
|
|
35180
|
+
"trapano",
|
|
35181
|
+
"lavori"
|
|
35182
|
+
]
|
|
35183
|
+
},
|
|
35184
|
+
{
|
|
35185
|
+
key: "waves-ladder",
|
|
35186
|
+
group: "place",
|
|
35187
|
+
synonyms: ["piscina", "vasca"]
|
|
35188
|
+
},
|
|
35189
|
+
{
|
|
35190
|
+
key: "circle-parking",
|
|
35191
|
+
group: "place",
|
|
35192
|
+
synonyms: [
|
|
35193
|
+
"parcheggio",
|
|
35194
|
+
"posto",
|
|
35195
|
+
"auto",
|
|
35196
|
+
"garage"
|
|
35197
|
+
]
|
|
35198
|
+
},
|
|
35199
|
+
{
|
|
35200
|
+
key: "route",
|
|
35201
|
+
group: "place",
|
|
35202
|
+
synonyms: [
|
|
35203
|
+
"percorso",
|
|
35204
|
+
"strada",
|
|
35205
|
+
"vialetto",
|
|
35206
|
+
"tragitto"
|
|
35207
|
+
]
|
|
35208
|
+
},
|
|
35209
|
+
{
|
|
35210
|
+
key: "car",
|
|
35211
|
+
group: "life",
|
|
35212
|
+
synonyms: [
|
|
35213
|
+
"auto",
|
|
35214
|
+
"macchina",
|
|
35215
|
+
"veicolo",
|
|
35216
|
+
"parcheggio"
|
|
35217
|
+
]
|
|
35218
|
+
},
|
|
35219
|
+
{
|
|
35220
|
+
key: "bike",
|
|
35221
|
+
group: "life",
|
|
35222
|
+
synonyms: [
|
|
35223
|
+
"bicicletta",
|
|
35224
|
+
"bici",
|
|
35225
|
+
"moto"
|
|
35226
|
+
]
|
|
35227
|
+
},
|
|
35228
|
+
{
|
|
35229
|
+
key: "bus",
|
|
35230
|
+
group: "life",
|
|
35231
|
+
synonyms: ["autobus", "pullman"]
|
|
35232
|
+
},
|
|
35233
|
+
{
|
|
35234
|
+
key: "truck",
|
|
35235
|
+
group: "life",
|
|
35236
|
+
synonyms: [
|
|
35237
|
+
"camion",
|
|
35238
|
+
"furgone",
|
|
35239
|
+
"consegna"
|
|
35240
|
+
]
|
|
35241
|
+
},
|
|
35242
|
+
{
|
|
35243
|
+
key: "plane",
|
|
35244
|
+
group: "life",
|
|
35245
|
+
synonyms: ["aereo"]
|
|
35246
|
+
},
|
|
35247
|
+
{
|
|
35248
|
+
key: "train-front",
|
|
35249
|
+
group: "life",
|
|
35250
|
+
synonyms: ["treno"]
|
|
35251
|
+
},
|
|
35252
|
+
{
|
|
35253
|
+
key: "sailboat",
|
|
35254
|
+
group: "life",
|
|
35255
|
+
synonyms: ["barca", "imbarcazione"]
|
|
35256
|
+
},
|
|
35257
|
+
{
|
|
35258
|
+
key: "dog",
|
|
35259
|
+
group: "life",
|
|
35260
|
+
synonyms: ["cane", "animale"]
|
|
35261
|
+
},
|
|
35262
|
+
{
|
|
35263
|
+
key: "cat",
|
|
35264
|
+
group: "life",
|
|
35265
|
+
synonyms: ["gatto", "animale"]
|
|
35266
|
+
},
|
|
35267
|
+
{
|
|
35268
|
+
key: "bird",
|
|
35269
|
+
group: "life",
|
|
35270
|
+
synonyms: ["uccello", "animale"]
|
|
35271
|
+
},
|
|
35272
|
+
{
|
|
35273
|
+
key: "paw-print",
|
|
35274
|
+
group: "life",
|
|
35275
|
+
synonyms: [
|
|
35276
|
+
"animale",
|
|
35277
|
+
"zampa",
|
|
35278
|
+
"pet"
|
|
35279
|
+
]
|
|
35280
|
+
},
|
|
35281
|
+
{
|
|
35282
|
+
key: "baby",
|
|
35283
|
+
group: "life",
|
|
35284
|
+
synonyms: [
|
|
35285
|
+
"neonato",
|
|
35286
|
+
"bimbo",
|
|
35287
|
+
"cameretta",
|
|
35288
|
+
"babymonitor"
|
|
35289
|
+
]
|
|
35290
|
+
},
|
|
35291
|
+
{
|
|
35292
|
+
key: "user",
|
|
35293
|
+
group: "life",
|
|
35294
|
+
synonyms: ["persona", "utente"]
|
|
35295
|
+
},
|
|
35296
|
+
{
|
|
35297
|
+
key: "users",
|
|
35298
|
+
group: "life",
|
|
35299
|
+
synonyms: [
|
|
35300
|
+
"persone",
|
|
35301
|
+
"gruppo",
|
|
35302
|
+
"famiglia"
|
|
35303
|
+
]
|
|
35304
|
+
},
|
|
35305
|
+
{
|
|
35306
|
+
key: "person-standing",
|
|
35307
|
+
group: "life",
|
|
35308
|
+
synonyms: [
|
|
35309
|
+
"persona",
|
|
35310
|
+
"piedi",
|
|
35311
|
+
"presenza"
|
|
35312
|
+
]
|
|
35313
|
+
}
|
|
35314
|
+
];
|
|
35315
|
+
DISPLAY_ICON_VOCABULARY.map((e) => e.key);
|
|
35316
|
+
DISPLAY_ICON_VOCABULARY.filter((e) => e.aliasOf === void 0).map((e) => e.key);
|
|
35317
|
+
new Map(DISPLAY_ICON_VOCABULARY.map((e) => [e.key, e]));
|
|
32640
35318
|
/** Reject after `ms`; always clears its own timer. */
|
|
32641
35319
|
async function withTimeout$1(promise, ms, label) {
|
|
32642
35320
|
let timer;
|
|
@@ -32704,6 +35382,12 @@ function startReachabilityPoll(options) {
|
|
|
32704
35382
|
} };
|
|
32705
35383
|
}
|
|
32706
35384
|
Object.freeze({
|
|
35385
|
+
"accessories.getStatus": {
|
|
35386
|
+
capName: "accessories",
|
|
35387
|
+
capScope: "device",
|
|
35388
|
+
addonId: null,
|
|
35389
|
+
access: "view"
|
|
35390
|
+
},
|
|
32707
35391
|
"accessories.setChildHidden": {
|
|
32708
35392
|
capName: "accessories",
|
|
32709
35393
|
capScope: "device",
|
|
@@ -32986,6 +35670,12 @@ Object.freeze({
|
|
|
32986
35670
|
addonId: null,
|
|
32987
35671
|
access: "view"
|
|
32988
35672
|
},
|
|
35673
|
+
"airQualitySensor.getStatus": {
|
|
35674
|
+
capName: "air-quality-sensor",
|
|
35675
|
+
capScope: "device",
|
|
35676
|
+
addonId: null,
|
|
35677
|
+
access: "view"
|
|
35678
|
+
},
|
|
32989
35679
|
"alarmPanel.arm": {
|
|
32990
35680
|
capName: "alarm-panel",
|
|
32991
35681
|
capScope: "device",
|
|
@@ -32998,6 +35688,12 @@ Object.freeze({
|
|
|
32998
35688
|
addonId: null,
|
|
32999
35689
|
access: "create"
|
|
33000
35690
|
},
|
|
35691
|
+
"alarmPanel.getStatus": {
|
|
35692
|
+
capName: "alarm-panel",
|
|
35693
|
+
capScope: "device",
|
|
35694
|
+
addonId: null,
|
|
35695
|
+
access: "view"
|
|
35696
|
+
},
|
|
33001
35697
|
"alarmPanel.trigger": {
|
|
33002
35698
|
capName: "alarm-panel",
|
|
33003
35699
|
capScope: "device",
|
|
@@ -33046,6 +35742,30 @@ Object.freeze({
|
|
|
33046
35742
|
addonId: null,
|
|
33047
35743
|
access: "create"
|
|
33048
35744
|
},
|
|
35745
|
+
"ambientLightSensor.getStatus": {
|
|
35746
|
+
capName: "ambient-light-sensor",
|
|
35747
|
+
capScope: "device",
|
|
35748
|
+
addonId: null,
|
|
35749
|
+
access: "view"
|
|
35750
|
+
},
|
|
35751
|
+
"audioAnalysis.applyDeviceSettingsPatch": {
|
|
35752
|
+
capName: "audio-analysis",
|
|
35753
|
+
capScope: "device",
|
|
35754
|
+
addonId: null,
|
|
35755
|
+
access: "create"
|
|
35756
|
+
},
|
|
35757
|
+
"audioAnalysis.getDeviceLiveContribution": {
|
|
35758
|
+
capName: "audio-analysis",
|
|
35759
|
+
capScope: "device",
|
|
35760
|
+
addonId: null,
|
|
35761
|
+
access: "view"
|
|
35762
|
+
},
|
|
35763
|
+
"audioAnalysis.getDeviceSettingsContribution": {
|
|
35764
|
+
capName: "audio-analysis",
|
|
35765
|
+
capScope: "device",
|
|
35766
|
+
addonId: null,
|
|
35767
|
+
access: "view"
|
|
35768
|
+
},
|
|
33049
35769
|
"audioAnalysis.resolveDeviceSettings": {
|
|
33050
35770
|
capName: "audio-analysis",
|
|
33051
35771
|
capScope: "device",
|
|
@@ -33208,6 +35928,12 @@ Object.freeze({
|
|
|
33208
35928
|
addonId: null,
|
|
33209
35929
|
access: "create"
|
|
33210
35930
|
},
|
|
35931
|
+
"automationControl.getStatus": {
|
|
35932
|
+
capName: "automation-control",
|
|
35933
|
+
capScope: "device",
|
|
35934
|
+
addonId: null,
|
|
35935
|
+
access: "view"
|
|
35936
|
+
},
|
|
33211
35937
|
"automationControl.trigger": {
|
|
33212
35938
|
capName: "automation-control",
|
|
33213
35939
|
capScope: "device",
|
|
@@ -33304,12 +36030,30 @@ Object.freeze({
|
|
|
33304
36030
|
addonId: null,
|
|
33305
36031
|
access: "create"
|
|
33306
36032
|
},
|
|
36033
|
+
"battery.getStatus": {
|
|
36034
|
+
capName: "battery",
|
|
36035
|
+
capScope: "device",
|
|
36036
|
+
addonId: null,
|
|
36037
|
+
access: "view"
|
|
36038
|
+
},
|
|
33307
36039
|
"battery.wakeForStream": {
|
|
33308
36040
|
capName: "battery",
|
|
33309
36041
|
capScope: "device",
|
|
33310
36042
|
addonId: null,
|
|
33311
36043
|
access: "create"
|
|
33312
36044
|
},
|
|
36045
|
+
"binary.getStatus": {
|
|
36046
|
+
capName: "binary",
|
|
36047
|
+
capScope: "device",
|
|
36048
|
+
addonId: null,
|
|
36049
|
+
access: "view"
|
|
36050
|
+
},
|
|
36051
|
+
"brightness.getStatus": {
|
|
36052
|
+
capName: "brightness",
|
|
36053
|
+
capScope: "device",
|
|
36054
|
+
addonId: null,
|
|
36055
|
+
access: "view"
|
|
36056
|
+
},
|
|
33313
36057
|
"brightness.setBrightness": {
|
|
33314
36058
|
capName: "brightness",
|
|
33315
36059
|
capScope: "device",
|
|
@@ -33424,6 +36168,30 @@ Object.freeze({
|
|
|
33424
36168
|
addonId: null,
|
|
33425
36169
|
access: "view"
|
|
33426
36170
|
},
|
|
36171
|
+
"cameraCredentials.getStatus": {
|
|
36172
|
+
capName: "camera-credentials",
|
|
36173
|
+
capScope: "device",
|
|
36174
|
+
addonId: null,
|
|
36175
|
+
access: "view"
|
|
36176
|
+
},
|
|
36177
|
+
"cameraGridLayout.applyDeviceSettingsPatch": {
|
|
36178
|
+
capName: "camera-grid-layout",
|
|
36179
|
+
capScope: "device",
|
|
36180
|
+
addonId: null,
|
|
36181
|
+
access: "create"
|
|
36182
|
+
},
|
|
36183
|
+
"cameraGridLayout.getDeviceLiveContribution": {
|
|
36184
|
+
capName: "camera-grid-layout",
|
|
36185
|
+
capScope: "device",
|
|
36186
|
+
addonId: null,
|
|
36187
|
+
access: "view"
|
|
36188
|
+
},
|
|
36189
|
+
"cameraGridLayout.getDeviceSettingsContribution": {
|
|
36190
|
+
capName: "camera-grid-layout",
|
|
36191
|
+
capScope: "device",
|
|
36192
|
+
addonId: null,
|
|
36193
|
+
access: "view"
|
|
36194
|
+
},
|
|
33427
36195
|
"cameraGridLayout.getLayout": {
|
|
33428
36196
|
capName: "camera-grid-layout",
|
|
33429
36197
|
capScope: "device",
|
|
@@ -33436,6 +36204,24 @@ Object.freeze({
|
|
|
33436
36204
|
addonId: null,
|
|
33437
36205
|
access: "create"
|
|
33438
36206
|
},
|
|
36207
|
+
"cameraPipelineConfig.applyDeviceSettingsPatch": {
|
|
36208
|
+
capName: "camera-pipeline-config",
|
|
36209
|
+
capScope: "device",
|
|
36210
|
+
addonId: null,
|
|
36211
|
+
access: "create"
|
|
36212
|
+
},
|
|
36213
|
+
"cameraPipelineConfig.getDeviceLiveContribution": {
|
|
36214
|
+
capName: "camera-pipeline-config",
|
|
36215
|
+
capScope: "device",
|
|
36216
|
+
addonId: null,
|
|
36217
|
+
access: "view"
|
|
36218
|
+
},
|
|
36219
|
+
"cameraPipelineConfig.getDeviceSettingsContribution": {
|
|
36220
|
+
capName: "camera-pipeline-config",
|
|
36221
|
+
capScope: "device",
|
|
36222
|
+
addonId: null,
|
|
36223
|
+
access: "view"
|
|
36224
|
+
},
|
|
33439
36225
|
"cameraStreams.getBrokerStreams": {
|
|
33440
36226
|
capName: "camera-streams",
|
|
33441
36227
|
capScope: "device",
|
|
@@ -33466,6 +36252,18 @@ Object.freeze({
|
|
|
33466
36252
|
addonId: null,
|
|
33467
36253
|
access: "view"
|
|
33468
36254
|
},
|
|
36255
|
+
"carbonMonoxide.getStatus": {
|
|
36256
|
+
capName: "carbon-monoxide",
|
|
36257
|
+
capScope: "device",
|
|
36258
|
+
addonId: null,
|
|
36259
|
+
access: "view"
|
|
36260
|
+
},
|
|
36261
|
+
"climateControl.getStatus": {
|
|
36262
|
+
capName: "climate-control",
|
|
36263
|
+
capScope: "device",
|
|
36264
|
+
addonId: null,
|
|
36265
|
+
access: "view"
|
|
36266
|
+
},
|
|
33469
36267
|
"climateControl.setFanMode": {
|
|
33470
36268
|
capName: "climate-control",
|
|
33471
36269
|
capScope: "device",
|
|
@@ -33514,6 +36312,12 @@ Object.freeze({
|
|
|
33514
36312
|
addonId: null,
|
|
33515
36313
|
access: "create"
|
|
33516
36314
|
},
|
|
36315
|
+
"color.getStatus": {
|
|
36316
|
+
capName: "color",
|
|
36317
|
+
capScope: "device",
|
|
36318
|
+
addonId: null,
|
|
36319
|
+
access: "view"
|
|
36320
|
+
},
|
|
33517
36321
|
"color.setColor": {
|
|
33518
36322
|
capName: "color",
|
|
33519
36323
|
capScope: "device",
|
|
@@ -33532,12 +36336,36 @@ Object.freeze({
|
|
|
33532
36336
|
addonId: null,
|
|
33533
36337
|
access: "create"
|
|
33534
36338
|
},
|
|
36339
|
+
"connectivity.getStatus": {
|
|
36340
|
+
capName: "connectivity",
|
|
36341
|
+
capScope: "device",
|
|
36342
|
+
addonId: null,
|
|
36343
|
+
access: "view"
|
|
36344
|
+
},
|
|
36345
|
+
"consumables.getStatus": {
|
|
36346
|
+
capName: "consumables",
|
|
36347
|
+
capScope: "device",
|
|
36348
|
+
addonId: null,
|
|
36349
|
+
access: "view"
|
|
36350
|
+
},
|
|
33535
36351
|
"consumables.reset": {
|
|
33536
36352
|
capName: "consumables",
|
|
33537
36353
|
capScope: "device",
|
|
33538
36354
|
addonId: null,
|
|
33539
36355
|
access: "delete"
|
|
33540
36356
|
},
|
|
36357
|
+
"contact.getStatus": {
|
|
36358
|
+
capName: "contact",
|
|
36359
|
+
capScope: "device",
|
|
36360
|
+
addonId: null,
|
|
36361
|
+
access: "view"
|
|
36362
|
+
},
|
|
36363
|
+
"control.getStatus": {
|
|
36364
|
+
capName: "control",
|
|
36365
|
+
capScope: "device",
|
|
36366
|
+
addonId: null,
|
|
36367
|
+
access: "view"
|
|
36368
|
+
},
|
|
33541
36369
|
"control.setValue": {
|
|
33542
36370
|
capName: "control",
|
|
33543
36371
|
capScope: "device",
|
|
@@ -33604,6 +36432,12 @@ Object.freeze({
|
|
|
33604
36432
|
addonId: null,
|
|
33605
36433
|
access: "create"
|
|
33606
36434
|
},
|
|
36435
|
+
"cover.getStatus": {
|
|
36436
|
+
capName: "cover",
|
|
36437
|
+
capScope: "device",
|
|
36438
|
+
addonId: null,
|
|
36439
|
+
access: "view"
|
|
36440
|
+
},
|
|
33607
36441
|
"cover.open": {
|
|
33608
36442
|
capName: "cover",
|
|
33609
36443
|
capScope: "device",
|
|
@@ -33736,6 +36570,12 @@ Object.freeze({
|
|
|
33736
36570
|
addonId: null,
|
|
33737
36571
|
access: "view"
|
|
33738
36572
|
},
|
|
36573
|
+
"dayNight.getStatus": {
|
|
36574
|
+
capName: "day-night",
|
|
36575
|
+
capScope: "device",
|
|
36576
|
+
addonId: null,
|
|
36577
|
+
access: "view"
|
|
36578
|
+
},
|
|
33739
36579
|
"dayNight.setSettings": {
|
|
33740
36580
|
capName: "day-night",
|
|
33741
36581
|
capScope: "device",
|
|
@@ -33826,6 +36666,30 @@ Object.freeze({
|
|
|
33826
36666
|
addonId: null,
|
|
33827
36667
|
access: "view"
|
|
33828
36668
|
},
|
|
36669
|
+
"detectionPipeline.applyDeviceSettingsPatch": {
|
|
36670
|
+
capName: "detection-pipeline",
|
|
36671
|
+
capScope: "device",
|
|
36672
|
+
addonId: null,
|
|
36673
|
+
access: "create"
|
|
36674
|
+
},
|
|
36675
|
+
"detectionPipeline.getDeviceLiveContribution": {
|
|
36676
|
+
capName: "detection-pipeline",
|
|
36677
|
+
capScope: "device",
|
|
36678
|
+
addonId: null,
|
|
36679
|
+
access: "view"
|
|
36680
|
+
},
|
|
36681
|
+
"detectionPipeline.getDeviceSettingsContribution": {
|
|
36682
|
+
capName: "detection-pipeline",
|
|
36683
|
+
capScope: "device",
|
|
36684
|
+
addonId: null,
|
|
36685
|
+
access: "view"
|
|
36686
|
+
},
|
|
36687
|
+
"deviceAdminLink.getAdminLink": {
|
|
36688
|
+
capName: "device-admin-link",
|
|
36689
|
+
capScope: "device",
|
|
36690
|
+
addonId: null,
|
|
36691
|
+
access: "view"
|
|
36692
|
+
},
|
|
33829
36693
|
"deviceAdoption.adopt": {
|
|
33830
36694
|
capName: "device-adoption",
|
|
33831
36695
|
capScope: "system",
|
|
@@ -33838,6 +36702,12 @@ Object.freeze({
|
|
|
33838
36702
|
addonId: null,
|
|
33839
36703
|
access: "view"
|
|
33840
36704
|
},
|
|
36705
|
+
"deviceAdoption.getStatus": {
|
|
36706
|
+
capName: "device-adoption",
|
|
36707
|
+
capScope: "system",
|
|
36708
|
+
addonId: null,
|
|
36709
|
+
access: "view"
|
|
36710
|
+
},
|
|
33841
36711
|
"deviceAdoption.listCandidateFilters": {
|
|
33842
36712
|
capName: "device-adoption",
|
|
33843
36713
|
capScope: "system",
|
|
@@ -33874,6 +36744,12 @@ Object.freeze({
|
|
|
33874
36744
|
addonId: null,
|
|
33875
36745
|
access: "create"
|
|
33876
36746
|
},
|
|
36747
|
+
"deviceDiscovery.getStatus": {
|
|
36748
|
+
capName: "device-discovery",
|
|
36749
|
+
capScope: "device",
|
|
36750
|
+
addonId: null,
|
|
36751
|
+
access: "view"
|
|
36752
|
+
},
|
|
33877
36753
|
"deviceDiscovery.listDiscovered": {
|
|
33878
36754
|
capName: "device-discovery",
|
|
33879
36755
|
capScope: "device",
|
|
@@ -33892,12 +36768,30 @@ Object.freeze({
|
|
|
33892
36768
|
addonId: null,
|
|
33893
36769
|
access: "create"
|
|
33894
36770
|
},
|
|
36771
|
+
"deviceExport.applyDeviceSettingsPatch": {
|
|
36772
|
+
capName: "device-export",
|
|
36773
|
+
capScope: "system",
|
|
36774
|
+
addonId: null,
|
|
36775
|
+
access: "create"
|
|
36776
|
+
},
|
|
33895
36777
|
"deviceExport.exposeDevice": {
|
|
33896
36778
|
capName: "device-export",
|
|
33897
36779
|
capScope: "system",
|
|
33898
36780
|
addonId: null,
|
|
33899
36781
|
access: "create"
|
|
33900
36782
|
},
|
|
36783
|
+
"deviceExport.getDeviceLiveContribution": {
|
|
36784
|
+
capName: "device-export",
|
|
36785
|
+
capScope: "system",
|
|
36786
|
+
addonId: null,
|
|
36787
|
+
access: "view"
|
|
36788
|
+
},
|
|
36789
|
+
"deviceExport.getDeviceSettingsContribution": {
|
|
36790
|
+
capName: "device-export",
|
|
36791
|
+
capScope: "system",
|
|
36792
|
+
addonId: null,
|
|
36793
|
+
access: "view"
|
|
36794
|
+
},
|
|
33901
36795
|
"deviceExport.getStatus": {
|
|
33902
36796
|
capName: "device-export",
|
|
33903
36797
|
capScope: "system",
|
|
@@ -33994,6 +36888,12 @@ Object.freeze({
|
|
|
33994
36888
|
addonId: null,
|
|
33995
36889
|
access: "create"
|
|
33996
36890
|
},
|
|
36891
|
+
"deviceManager.applyDeviceSettingsPatch": {
|
|
36892
|
+
capName: "device-manager",
|
|
36893
|
+
capScope: "system",
|
|
36894
|
+
addonId: null,
|
|
36895
|
+
access: "create"
|
|
36896
|
+
},
|
|
33997
36897
|
"deviceManager.applyInitialMeta": {
|
|
33998
36898
|
capName: "device-manager",
|
|
33999
36899
|
capScope: "system",
|
|
@@ -34096,6 +36996,12 @@ Object.freeze({
|
|
|
34096
36996
|
addonId: null,
|
|
34097
36997
|
access: "view"
|
|
34098
36998
|
},
|
|
36999
|
+
"deviceManager.getDeviceLiveContribution": {
|
|
37000
|
+
capName: "device-manager",
|
|
37001
|
+
capScope: "system",
|
|
37002
|
+
addonId: null,
|
|
37003
|
+
access: "view"
|
|
37004
|
+
},
|
|
34099
37005
|
"deviceManager.getDeviceLiveInfoAggregate": {
|
|
34100
37006
|
capName: "device-manager",
|
|
34101
37007
|
capScope: "system",
|
|
@@ -34108,6 +37014,12 @@ Object.freeze({
|
|
|
34108
37014
|
addonId: null,
|
|
34109
37015
|
access: "view"
|
|
34110
37016
|
},
|
|
37017
|
+
"deviceManager.getDeviceSettingsContribution": {
|
|
37018
|
+
capName: "device-manager",
|
|
37019
|
+
capScope: "system",
|
|
37020
|
+
addonId: null,
|
|
37021
|
+
access: "view"
|
|
37022
|
+
},
|
|
34111
37023
|
"deviceManager.getDeviceStatusAggregate": {
|
|
34112
37024
|
capName: "device-manager",
|
|
34113
37025
|
capScope: "system",
|
|
@@ -34600,6 +37512,36 @@ Object.freeze({
|
|
|
34600
37512
|
addonId: null,
|
|
34601
37513
|
access: "create"
|
|
34602
37514
|
},
|
|
37515
|
+
"deviceStatus.getStatus": {
|
|
37516
|
+
capName: "device-status",
|
|
37517
|
+
capScope: "device",
|
|
37518
|
+
addonId: null,
|
|
37519
|
+
access: "view"
|
|
37520
|
+
},
|
|
37521
|
+
"doorbell.applyDeviceSettingsPatch": {
|
|
37522
|
+
capName: "doorbell",
|
|
37523
|
+
capScope: "device",
|
|
37524
|
+
addonId: null,
|
|
37525
|
+
access: "create"
|
|
37526
|
+
},
|
|
37527
|
+
"doorbell.getDeviceLiveContribution": {
|
|
37528
|
+
capName: "doorbell",
|
|
37529
|
+
capScope: "device",
|
|
37530
|
+
addonId: null,
|
|
37531
|
+
access: "view"
|
|
37532
|
+
},
|
|
37533
|
+
"doorbell.getDeviceSettingsContribution": {
|
|
37534
|
+
capName: "doorbell",
|
|
37535
|
+
capScope: "device",
|
|
37536
|
+
addonId: null,
|
|
37537
|
+
access: "view"
|
|
37538
|
+
},
|
|
37539
|
+
"doorbell.getStatus": {
|
|
37540
|
+
capName: "doorbell",
|
|
37541
|
+
capScope: "device",
|
|
37542
|
+
addonId: null,
|
|
37543
|
+
access: "view"
|
|
37544
|
+
},
|
|
34603
37545
|
"embeddingEncoder.encode": {
|
|
34604
37546
|
capName: "embedding-encoder",
|
|
34605
37547
|
capScope: "system",
|
|
@@ -34618,6 +37560,18 @@ Object.freeze({
|
|
|
34618
37560
|
addonId: null,
|
|
34619
37561
|
access: "view"
|
|
34620
37562
|
},
|
|
37563
|
+
"enumSensor.getStatus": {
|
|
37564
|
+
capName: "enum-sensor",
|
|
37565
|
+
capScope: "device",
|
|
37566
|
+
addonId: null,
|
|
37567
|
+
access: "view"
|
|
37568
|
+
},
|
|
37569
|
+
"eventEmitter.getStatus": {
|
|
37570
|
+
capName: "event-emitter",
|
|
37571
|
+
capScope: "device",
|
|
37572
|
+
addonId: null,
|
|
37573
|
+
access: "view"
|
|
37574
|
+
},
|
|
34621
37575
|
"events.getEventClipUrl": {
|
|
34622
37576
|
capName: "events",
|
|
34623
37577
|
capScope: "device",
|
|
@@ -34738,6 +37692,12 @@ Object.freeze({
|
|
|
34738
37692
|
addonId: null,
|
|
34739
37693
|
access: "view"
|
|
34740
37694
|
},
|
|
37695
|
+
"fanControl.getStatus": {
|
|
37696
|
+
capName: "fan-control",
|
|
37697
|
+
capScope: "device",
|
|
37698
|
+
addonId: null,
|
|
37699
|
+
access: "view"
|
|
37700
|
+
},
|
|
34741
37701
|
"fanControl.setDirection": {
|
|
34742
37702
|
capName: "fan-control",
|
|
34743
37703
|
capScope: "device",
|
|
@@ -34762,6 +37722,12 @@ Object.freeze({
|
|
|
34762
37722
|
addonId: null,
|
|
34763
37723
|
access: "create"
|
|
34764
37724
|
},
|
|
37725
|
+
"featureProbe.getStatus": {
|
|
37726
|
+
capName: "feature-probe",
|
|
37727
|
+
capScope: "device",
|
|
37728
|
+
addonId: null,
|
|
37729
|
+
access: "view"
|
|
37730
|
+
},
|
|
34765
37731
|
"filesystemBrowse.browse": {
|
|
34766
37732
|
capName: "filesystem-browse",
|
|
34767
37733
|
capScope: "system",
|
|
@@ -34780,6 +37746,24 @@ Object.freeze({
|
|
|
34780
37746
|
addonId: null,
|
|
34781
37747
|
access: "view"
|
|
34782
37748
|
},
|
|
37749
|
+
"flood.getStatus": {
|
|
37750
|
+
capName: "flood",
|
|
37751
|
+
capScope: "device",
|
|
37752
|
+
addonId: null,
|
|
37753
|
+
access: "view"
|
|
37754
|
+
},
|
|
37755
|
+
"gas.getStatus": {
|
|
37756
|
+
capName: "gas",
|
|
37757
|
+
capScope: "device",
|
|
37758
|
+
addonId: null,
|
|
37759
|
+
access: "view"
|
|
37760
|
+
},
|
|
37761
|
+
"humidifier.getStatus": {
|
|
37762
|
+
capName: "humidifier",
|
|
37763
|
+
capScope: "device",
|
|
37764
|
+
addonId: null,
|
|
37765
|
+
access: "view"
|
|
37766
|
+
},
|
|
34783
37767
|
"humidifier.setMode": {
|
|
34784
37768
|
capName: "humidifier",
|
|
34785
37769
|
capScope: "device",
|
|
@@ -34798,12 +37782,30 @@ Object.freeze({
|
|
|
34798
37782
|
addonId: null,
|
|
34799
37783
|
access: "create"
|
|
34800
37784
|
},
|
|
37785
|
+
"humiditySensor.getStatus": {
|
|
37786
|
+
capName: "humidity-sensor",
|
|
37787
|
+
capScope: "device",
|
|
37788
|
+
addonId: null,
|
|
37789
|
+
access: "view"
|
|
37790
|
+
},
|
|
37791
|
+
"image.getStatus": {
|
|
37792
|
+
capName: "image",
|
|
37793
|
+
capScope: "device",
|
|
37794
|
+
addonId: null,
|
|
37795
|
+
access: "view"
|
|
37796
|
+
},
|
|
34801
37797
|
"imageSettings.getOptions": {
|
|
34802
37798
|
capName: "image-settings",
|
|
34803
37799
|
capScope: "device",
|
|
34804
37800
|
addonId: null,
|
|
34805
37801
|
access: "view"
|
|
34806
37802
|
},
|
|
37803
|
+
"imageSettings.getStatus": {
|
|
37804
|
+
capName: "image-settings",
|
|
37805
|
+
capScope: "device",
|
|
37806
|
+
addonId: null,
|
|
37807
|
+
access: "view"
|
|
37808
|
+
},
|
|
34807
37809
|
"imageSettings.setSettings": {
|
|
34808
37810
|
capName: "image-settings",
|
|
34809
37811
|
capScope: "device",
|
|
@@ -34876,6 +37878,12 @@ Object.freeze({
|
|
|
34876
37878
|
addonId: null,
|
|
34877
37879
|
access: "create"
|
|
34878
37880
|
},
|
|
37881
|
+
"intercom.getStatus": {
|
|
37882
|
+
capName: "intercom",
|
|
37883
|
+
capScope: "device",
|
|
37884
|
+
addonId: null,
|
|
37885
|
+
access: "view"
|
|
37886
|
+
},
|
|
34879
37887
|
"intercom.handleAnswer": {
|
|
34880
37888
|
capName: "intercom",
|
|
34881
37889
|
capScope: "device",
|
|
@@ -34912,6 +37920,12 @@ Object.freeze({
|
|
|
34912
37920
|
addonId: null,
|
|
34913
37921
|
access: "create"
|
|
34914
37922
|
},
|
|
37923
|
+
"lawnMowerControl.getStatus": {
|
|
37924
|
+
capName: "lawn-mower-control",
|
|
37925
|
+
capScope: "device",
|
|
37926
|
+
addonId: null,
|
|
37927
|
+
access: "view"
|
|
37928
|
+
},
|
|
34915
37929
|
"lawnMowerControl.pause": {
|
|
34916
37930
|
capName: "lawn-mower-control",
|
|
34917
37931
|
capScope: "device",
|
|
@@ -35194,6 +38208,12 @@ Object.freeze({
|
|
|
35194
38208
|
addonId: null,
|
|
35195
38209
|
access: "create"
|
|
35196
38210
|
},
|
|
38211
|
+
"lockControl.getStatus": {
|
|
38212
|
+
capName: "lock-control",
|
|
38213
|
+
capScope: "device",
|
|
38214
|
+
addonId: null,
|
|
38215
|
+
access: "view"
|
|
38216
|
+
},
|
|
35197
38217
|
"lockControl.lock": {
|
|
35198
38218
|
capName: "lock-control",
|
|
35199
38219
|
capScope: "device",
|
|
@@ -35242,6 +38262,12 @@ Object.freeze({
|
|
|
35242
38262
|
addonId: null,
|
|
35243
38263
|
access: "view"
|
|
35244
38264
|
},
|
|
38265
|
+
"mediaPlayer.getStatus": {
|
|
38266
|
+
capName: "media-player",
|
|
38267
|
+
capScope: "device",
|
|
38268
|
+
addonId: null,
|
|
38269
|
+
access: "view"
|
|
38270
|
+
},
|
|
35245
38271
|
"mediaPlayer.next": {
|
|
35246
38272
|
capName: "media-player",
|
|
35247
38273
|
capScope: "device",
|
|
@@ -35434,6 +38460,12 @@ Object.freeze({
|
|
|
35434
38460
|
addonId: null,
|
|
35435
38461
|
access: "create"
|
|
35436
38462
|
},
|
|
38463
|
+
"motion.getStatus": {
|
|
38464
|
+
capName: "motion",
|
|
38465
|
+
capScope: "device",
|
|
38466
|
+
addonId: null,
|
|
38467
|
+
access: "view"
|
|
38468
|
+
},
|
|
35437
38469
|
"motion.isDetected": {
|
|
35438
38470
|
capName: "motion",
|
|
35439
38471
|
capScope: "device",
|
|
@@ -35446,6 +38478,24 @@ Object.freeze({
|
|
|
35446
38478
|
addonId: null,
|
|
35447
38479
|
access: "create"
|
|
35448
38480
|
},
|
|
38481
|
+
"motionDetection.applyDeviceSettingsPatch": {
|
|
38482
|
+
capName: "motion-detection",
|
|
38483
|
+
capScope: "device",
|
|
38484
|
+
addonId: null,
|
|
38485
|
+
access: "create"
|
|
38486
|
+
},
|
|
38487
|
+
"motionDetection.getDeviceLiveContribution": {
|
|
38488
|
+
capName: "motion-detection",
|
|
38489
|
+
capScope: "device",
|
|
38490
|
+
addonId: null,
|
|
38491
|
+
access: "view"
|
|
38492
|
+
},
|
|
38493
|
+
"motionDetection.getDeviceSettingsContribution": {
|
|
38494
|
+
capName: "motion-detection",
|
|
38495
|
+
capScope: "device",
|
|
38496
|
+
addonId: null,
|
|
38497
|
+
access: "view"
|
|
38498
|
+
},
|
|
35449
38499
|
"motionDetection.removeCamera": {
|
|
35450
38500
|
capName: "motion-detection",
|
|
35451
38501
|
capScope: "device",
|
|
@@ -35458,6 +38508,12 @@ Object.freeze({
|
|
|
35458
38508
|
addonId: null,
|
|
35459
38509
|
access: "delete"
|
|
35460
38510
|
},
|
|
38511
|
+
"motionTrigger.getStatus": {
|
|
38512
|
+
capName: "motion-trigger",
|
|
38513
|
+
capScope: "device",
|
|
38514
|
+
addonId: null,
|
|
38515
|
+
access: "view"
|
|
38516
|
+
},
|
|
35461
38517
|
"motionTrigger.setMotionTrigger": {
|
|
35462
38518
|
capName: "motion-trigger",
|
|
35463
38519
|
capScope: "device",
|
|
@@ -35470,6 +38526,12 @@ Object.freeze({
|
|
|
35470
38526
|
addonId: null,
|
|
35471
38527
|
access: "view"
|
|
35472
38528
|
},
|
|
38529
|
+
"motionZones.getStatus": {
|
|
38530
|
+
capName: "motion-zones",
|
|
38531
|
+
capScope: "device",
|
|
38532
|
+
addonId: null,
|
|
38533
|
+
access: "view"
|
|
38534
|
+
},
|
|
35473
38535
|
"motionZones.setZone": {
|
|
35474
38536
|
capName: "motion-zones",
|
|
35475
38537
|
capScope: "device",
|
|
@@ -35530,6 +38592,12 @@ Object.freeze({
|
|
|
35530
38592
|
addonId: null,
|
|
35531
38593
|
access: "view"
|
|
35532
38594
|
},
|
|
38595
|
+
"nativeObjectDetection.getStatus": {
|
|
38596
|
+
capName: "native-object-detection",
|
|
38597
|
+
capScope: "device",
|
|
38598
|
+
addonId: null,
|
|
38599
|
+
access: "view"
|
|
38600
|
+
},
|
|
35533
38601
|
"nativeObjectDetection.setEnabled": {
|
|
35534
38602
|
capName: "native-object-detection",
|
|
35535
38603
|
capScope: "device",
|
|
@@ -35548,6 +38616,12 @@ Object.freeze({
|
|
|
35548
38616
|
addonId: null,
|
|
35549
38617
|
access: "view"
|
|
35550
38618
|
},
|
|
38619
|
+
"navigation.getStatus": {
|
|
38620
|
+
capName: "navigation",
|
|
38621
|
+
capScope: "device",
|
|
38622
|
+
addonId: null,
|
|
38623
|
+
access: "view"
|
|
38624
|
+
},
|
|
35551
38625
|
"navigation.goToPoint": {
|
|
35552
38626
|
capName: "navigation",
|
|
35553
38627
|
capScope: "device",
|
|
@@ -35632,6 +38706,12 @@ Object.freeze({
|
|
|
35632
38706
|
addonId: null,
|
|
35633
38707
|
access: "create"
|
|
35634
38708
|
},
|
|
38709
|
+
"networkLink.getStatus": {
|
|
38710
|
+
capName: "network-link",
|
|
38711
|
+
capScope: "device",
|
|
38712
|
+
addonId: null,
|
|
38713
|
+
access: "view"
|
|
38714
|
+
},
|
|
35635
38715
|
"networkQuality.getAllStats": {
|
|
35636
38716
|
capName: "network-quality",
|
|
35637
38717
|
capScope: "system",
|
|
@@ -35890,18 +38970,36 @@ Object.freeze({
|
|
|
35890
38970
|
addonId: null,
|
|
35891
38971
|
access: "create"
|
|
35892
38972
|
},
|
|
38973
|
+
"notifier.getStatus": {
|
|
38974
|
+
capName: "notifier",
|
|
38975
|
+
capScope: "device",
|
|
38976
|
+
addonId: null,
|
|
38977
|
+
access: "view"
|
|
38978
|
+
},
|
|
35893
38979
|
"notifier.send": {
|
|
35894
38980
|
capName: "notifier",
|
|
35895
38981
|
capScope: "device",
|
|
35896
38982
|
addonId: null,
|
|
35897
38983
|
access: "create"
|
|
35898
38984
|
},
|
|
38985
|
+
"numericSensor.getStatus": {
|
|
38986
|
+
capName: "numeric-sensor",
|
|
38987
|
+
capScope: "device",
|
|
38988
|
+
addonId: null,
|
|
38989
|
+
access: "view"
|
|
38990
|
+
},
|
|
35899
38991
|
"oauthIntegration.getDescriptor": {
|
|
35900
38992
|
capName: "oauth-integration",
|
|
35901
38993
|
capScope: "system",
|
|
35902
38994
|
addonId: null,
|
|
35903
38995
|
access: "view"
|
|
35904
38996
|
},
|
|
38997
|
+
"osd.getStatus": {
|
|
38998
|
+
capName: "osd",
|
|
38999
|
+
capScope: "device",
|
|
39000
|
+
addonId: null,
|
|
39001
|
+
access: "view"
|
|
39002
|
+
},
|
|
35905
39003
|
"osd.setOverlay": {
|
|
35906
39004
|
capName: "osd",
|
|
35907
39005
|
capScope: "device",
|
|
@@ -35974,6 +39072,12 @@ Object.freeze({
|
|
|
35974
39072
|
addonId: null,
|
|
35975
39073
|
access: "create"
|
|
35976
39074
|
},
|
|
39075
|
+
"petFeeder.getStatus": {
|
|
39076
|
+
capName: "pet-feeder",
|
|
39077
|
+
capScope: "device",
|
|
39078
|
+
addonId: null,
|
|
39079
|
+
access: "view"
|
|
39080
|
+
},
|
|
35977
39081
|
"petFeeder.markFoodReplenished": {
|
|
35978
39082
|
capName: "pet-feeder",
|
|
35979
39083
|
capScope: "device",
|
|
@@ -36016,6 +39120,12 @@ Object.freeze({
|
|
|
36016
39120
|
addonId: null,
|
|
36017
39121
|
access: "create"
|
|
36018
39122
|
},
|
|
39123
|
+
"pipelineAnalytics.applyDeviceSettingsPatch": {
|
|
39124
|
+
capName: "pipeline-analytics",
|
|
39125
|
+
capScope: "device",
|
|
39126
|
+
addonId: null,
|
|
39127
|
+
access: "create"
|
|
39128
|
+
},
|
|
36019
39129
|
"pipelineAnalytics.cancelRelocateMedia": {
|
|
36020
39130
|
capName: "pipeline-analytics",
|
|
36021
39131
|
capScope: "device",
|
|
@@ -36082,6 +39192,18 @@ Object.freeze({
|
|
|
36082
39192
|
addonId: null,
|
|
36083
39193
|
access: "view"
|
|
36084
39194
|
},
|
|
39195
|
+
"pipelineAnalytics.getDeviceLiveContribution": {
|
|
39196
|
+
capName: "pipeline-analytics",
|
|
39197
|
+
capScope: "device",
|
|
39198
|
+
addonId: null,
|
|
39199
|
+
access: "view"
|
|
39200
|
+
},
|
|
39201
|
+
"pipelineAnalytics.getDeviceSettingsContribution": {
|
|
39202
|
+
capName: "pipeline-analytics",
|
|
39203
|
+
capScope: "device",
|
|
39204
|
+
addonId: null,
|
|
39205
|
+
access: "view"
|
|
39206
|
+
},
|
|
36085
39207
|
"pipelineAnalytics.getEventDensity": {
|
|
36086
39208
|
capName: "pipeline-analytics",
|
|
36087
39209
|
capScope: "device",
|
|
@@ -36292,6 +39414,12 @@ Object.freeze({
|
|
|
36292
39414
|
addonId: null,
|
|
36293
39415
|
access: "view"
|
|
36294
39416
|
},
|
|
39417
|
+
"pipelineAnalytics.ownersWithMedia": {
|
|
39418
|
+
capName: "pipeline-analytics",
|
|
39419
|
+
capScope: "device",
|
|
39420
|
+
addonId: null,
|
|
39421
|
+
access: "view"
|
|
39422
|
+
},
|
|
36295
39423
|
"pipelineAnalytics.pauseForStorageMigration": {
|
|
36296
39424
|
capName: "pipeline-analytics",
|
|
36297
39425
|
capScope: "device",
|
|
@@ -36646,6 +39774,12 @@ Object.freeze({
|
|
|
36646
39774
|
addonId: null,
|
|
36647
39775
|
access: "view"
|
|
36648
39776
|
},
|
|
39777
|
+
"pipelineOrchestrator.applyDeviceSettingsPatch": {
|
|
39778
|
+
capName: "pipeline-orchestrator",
|
|
39779
|
+
capScope: "system",
|
|
39780
|
+
addonId: null,
|
|
39781
|
+
access: "create"
|
|
39782
|
+
},
|
|
36649
39783
|
"pipelineOrchestrator.assignAudio": {
|
|
36650
39784
|
capName: "pipeline-orchestrator",
|
|
36651
39785
|
capScope: "system",
|
|
@@ -36736,6 +39870,18 @@ Object.freeze({
|
|
|
36736
39870
|
addonId: null,
|
|
36737
39871
|
access: "view"
|
|
36738
39872
|
},
|
|
39873
|
+
"pipelineOrchestrator.getDeviceLiveContribution": {
|
|
39874
|
+
capName: "pipeline-orchestrator",
|
|
39875
|
+
capScope: "system",
|
|
39876
|
+
addonId: null,
|
|
39877
|
+
access: "view"
|
|
39878
|
+
},
|
|
39879
|
+
"pipelineOrchestrator.getDeviceSettingsContribution": {
|
|
39880
|
+
capName: "pipeline-orchestrator",
|
|
39881
|
+
capScope: "system",
|
|
39882
|
+
addonId: null,
|
|
39883
|
+
access: "view"
|
|
39884
|
+
},
|
|
36739
39885
|
"pipelineOrchestrator.getGlobalMetrics": {
|
|
36740
39886
|
capName: "pipeline-orchestrator",
|
|
36741
39887
|
capScope: "system",
|
|
@@ -37144,12 +40290,36 @@ Object.freeze({
|
|
|
37144
40290
|
addonId: null,
|
|
37145
40291
|
access: "view"
|
|
37146
40292
|
},
|
|
40293
|
+
"powerMeter.getStatus": {
|
|
40294
|
+
capName: "power-meter",
|
|
40295
|
+
capScope: "device",
|
|
40296
|
+
addonId: null,
|
|
40297
|
+
access: "view"
|
|
40298
|
+
},
|
|
40299
|
+
"presence.getStatus": {
|
|
40300
|
+
capName: "presence",
|
|
40301
|
+
capScope: "device",
|
|
40302
|
+
addonId: null,
|
|
40303
|
+
access: "view"
|
|
40304
|
+
},
|
|
40305
|
+
"pressureSensor.getStatus": {
|
|
40306
|
+
capName: "pressure-sensor",
|
|
40307
|
+
capScope: "device",
|
|
40308
|
+
addonId: null,
|
|
40309
|
+
access: "view"
|
|
40310
|
+
},
|
|
37147
40311
|
"privacyMask.getOptions": {
|
|
37148
40312
|
capName: "privacy-mask",
|
|
37149
40313
|
capScope: "device",
|
|
37150
40314
|
addonId: null,
|
|
37151
40315
|
access: "view"
|
|
37152
40316
|
},
|
|
40317
|
+
"privacyMask.getStatus": {
|
|
40318
|
+
capName: "privacy-mask",
|
|
40319
|
+
capScope: "device",
|
|
40320
|
+
addonId: null,
|
|
40321
|
+
access: "view"
|
|
40322
|
+
},
|
|
37153
40323
|
"privacyMask.setAudioEnabled": {
|
|
37154
40324
|
capName: "privacy-mask",
|
|
37155
40325
|
capScope: "device",
|
|
@@ -37210,6 +40380,12 @@ Object.freeze({
|
|
|
37210
40380
|
addonId: null,
|
|
37211
40381
|
access: "view"
|
|
37212
40382
|
},
|
|
40383
|
+
"ptz.getStatus": {
|
|
40384
|
+
capName: "ptz",
|
|
40385
|
+
capScope: "device",
|
|
40386
|
+
addonId: null,
|
|
40387
|
+
access: "view"
|
|
40388
|
+
},
|
|
37213
40389
|
"ptz.goHome": {
|
|
37214
40390
|
capName: "ptz",
|
|
37215
40391
|
capScope: "device",
|
|
@@ -37288,6 +40464,12 @@ Object.freeze({
|
|
|
37288
40464
|
addonId: null,
|
|
37289
40465
|
access: "create"
|
|
37290
40466
|
},
|
|
40467
|
+
"recording.applyDeviceSettingsPatch": {
|
|
40468
|
+
capName: "recording",
|
|
40469
|
+
capScope: "system",
|
|
40470
|
+
addonId: null,
|
|
40471
|
+
access: "create"
|
|
40472
|
+
},
|
|
37291
40473
|
"recording.cancelRelocateJob": {
|
|
37292
40474
|
capName: "recording",
|
|
37293
40475
|
capScope: "system",
|
|
@@ -37336,6 +40518,18 @@ Object.freeze({
|
|
|
37336
40518
|
addonId: null,
|
|
37337
40519
|
access: "view"
|
|
37338
40520
|
},
|
|
40521
|
+
"recording.getDeviceLiveContribution": {
|
|
40522
|
+
capName: "recording",
|
|
40523
|
+
capScope: "system",
|
|
40524
|
+
addonId: null,
|
|
40525
|
+
access: "view"
|
|
40526
|
+
},
|
|
40527
|
+
"recording.getDeviceSettingsContribution": {
|
|
40528
|
+
capName: "recording",
|
|
40529
|
+
capScope: "system",
|
|
40530
|
+
addonId: null,
|
|
40531
|
+
access: "view"
|
|
40532
|
+
},
|
|
37339
40533
|
"recording.getPlacement": {
|
|
37340
40534
|
capName: "recording",
|
|
37341
40535
|
capScope: "system",
|
|
@@ -37354,6 +40548,12 @@ Object.freeze({
|
|
|
37354
40548
|
addonId: null,
|
|
37355
40549
|
access: "view"
|
|
37356
40550
|
},
|
|
40551
|
+
"recording.getStatus": {
|
|
40552
|
+
capName: "recording",
|
|
40553
|
+
capScope: "system",
|
|
40554
|
+
addonId: null,
|
|
40555
|
+
access: "view"
|
|
40556
|
+
},
|
|
37357
40557
|
"recording.getStorageMigrationMoveStatus": {
|
|
37358
40558
|
capName: "recording",
|
|
37359
40559
|
capScope: "system",
|
|
@@ -37528,6 +40728,24 @@ Object.freeze({
|
|
|
37528
40728
|
addonId: null,
|
|
37529
40729
|
access: "view"
|
|
37530
40730
|
},
|
|
40731
|
+
"recordingOnboard.getOptions": {
|
|
40732
|
+
capName: "recording-onboard",
|
|
40733
|
+
capScope: "device",
|
|
40734
|
+
addonId: null,
|
|
40735
|
+
access: "view"
|
|
40736
|
+
},
|
|
40737
|
+
"recordingOnboard.getStatus": {
|
|
40738
|
+
capName: "recording-onboard",
|
|
40739
|
+
capScope: "device",
|
|
40740
|
+
addonId: null,
|
|
40741
|
+
access: "view"
|
|
40742
|
+
},
|
|
40743
|
+
"recordingOnboard.setSettings": {
|
|
40744
|
+
capName: "recording-onboard",
|
|
40745
|
+
capScope: "device",
|
|
40746
|
+
addonId: null,
|
|
40747
|
+
access: "create"
|
|
40748
|
+
},
|
|
37531
40749
|
"recordingSignal.getStatus": {
|
|
37532
40750
|
capName: "recording-signal",
|
|
37533
40751
|
capScope: "device",
|
|
@@ -37558,6 +40776,12 @@ Object.freeze({
|
|
|
37558
40776
|
addonId: null,
|
|
37559
40777
|
access: "delete"
|
|
37560
40778
|
},
|
|
40779
|
+
"sceneMonitor.getStatus": {
|
|
40780
|
+
capName: "scene-monitor",
|
|
40781
|
+
capScope: "device",
|
|
40782
|
+
addonId: null,
|
|
40783
|
+
access: "view"
|
|
40784
|
+
},
|
|
37561
40785
|
"sceneMonitor.listScenes": {
|
|
37562
40786
|
capName: "scene-monitor",
|
|
37563
40787
|
capScope: "device",
|
|
@@ -37588,6 +40812,12 @@ Object.freeze({
|
|
|
37588
40812
|
addonId: null,
|
|
37589
40813
|
access: "create"
|
|
37590
40814
|
},
|
|
40815
|
+
"scriptRunner.getStatus": {
|
|
40816
|
+
capName: "script-runner",
|
|
40817
|
+
capScope: "device",
|
|
40818
|
+
addonId: null,
|
|
40819
|
+
access: "view"
|
|
40820
|
+
},
|
|
37591
40821
|
"scriptRunner.run": {
|
|
37592
40822
|
capName: "script-runner",
|
|
37593
40823
|
capScope: "device",
|
|
@@ -37720,6 +40950,12 @@ Object.freeze({
|
|
|
37720
40950
|
addonId: null,
|
|
37721
40951
|
access: "create"
|
|
37722
40952
|
},
|
|
40953
|
+
"smoke.getStatus": {
|
|
40954
|
+
capName: "smoke",
|
|
40955
|
+
capScope: "device",
|
|
40956
|
+
addonId: null,
|
|
40957
|
+
access: "view"
|
|
40958
|
+
},
|
|
37723
40959
|
"smtpProvider.getStatus": {
|
|
37724
40960
|
capName: "smtp-provider",
|
|
37725
40961
|
capScope: "system",
|
|
@@ -37738,12 +40974,30 @@ Object.freeze({
|
|
|
37738
40974
|
addonId: null,
|
|
37739
40975
|
access: "view"
|
|
37740
40976
|
},
|
|
40977
|
+
"snapshot.applyDeviceSettingsPatch": {
|
|
40978
|
+
capName: "snapshot",
|
|
40979
|
+
capScope: "device",
|
|
40980
|
+
addonId: null,
|
|
40981
|
+
access: "create"
|
|
40982
|
+
},
|
|
37741
40983
|
"snapshot.getDebugState": {
|
|
37742
40984
|
capName: "snapshot",
|
|
37743
40985
|
capScope: "device",
|
|
37744
40986
|
addonId: null,
|
|
37745
40987
|
access: "view"
|
|
37746
40988
|
},
|
|
40989
|
+
"snapshot.getDeviceLiveContribution": {
|
|
40990
|
+
capName: "snapshot",
|
|
40991
|
+
capScope: "device",
|
|
40992
|
+
addonId: null,
|
|
40993
|
+
access: "view"
|
|
40994
|
+
},
|
|
40995
|
+
"snapshot.getDeviceSettingsContribution": {
|
|
40996
|
+
capName: "snapshot",
|
|
40997
|
+
capScope: "device",
|
|
40998
|
+
addonId: null,
|
|
40999
|
+
access: "view"
|
|
41000
|
+
},
|
|
37747
41001
|
"snapshot.getSnapshot": {
|
|
37748
41002
|
capName: "snapshot",
|
|
37749
41003
|
capScope: "device",
|
|
@@ -37762,6 +41016,12 @@ Object.freeze({
|
|
|
37762
41016
|
addonId: null,
|
|
37763
41017
|
access: "view"
|
|
37764
41018
|
},
|
|
41019
|
+
"snapshot.getStatus": {
|
|
41020
|
+
capName: "snapshot",
|
|
41021
|
+
capScope: "device",
|
|
41022
|
+
addonId: null,
|
|
41023
|
+
access: "view"
|
|
41024
|
+
},
|
|
37765
41025
|
"snapshot.invalidateCache": {
|
|
37766
41026
|
capName: "snapshot",
|
|
37767
41027
|
capScope: "device",
|
|
@@ -38098,6 +41358,12 @@ Object.freeze({
|
|
|
38098
41358
|
addonId: null,
|
|
38099
41359
|
access: "create"
|
|
38100
41360
|
},
|
|
41361
|
+
"streamBroker.applyDeviceSettingsPatch": {
|
|
41362
|
+
capName: "stream-broker",
|
|
41363
|
+
capScope: "system",
|
|
41364
|
+
addonId: null,
|
|
41365
|
+
access: "create"
|
|
41366
|
+
},
|
|
38101
41367
|
"streamBroker.assignProfile": {
|
|
38102
41368
|
capName: "stream-broker",
|
|
38103
41369
|
capScope: "system",
|
|
@@ -38134,6 +41400,18 @@ Object.freeze({
|
|
|
38134
41400
|
addonId: null,
|
|
38135
41401
|
access: "view"
|
|
38136
41402
|
},
|
|
41403
|
+
"streamBroker.getDeviceLiveContribution": {
|
|
41404
|
+
capName: "stream-broker",
|
|
41405
|
+
capScope: "system",
|
|
41406
|
+
addonId: null,
|
|
41407
|
+
access: "view"
|
|
41408
|
+
},
|
|
41409
|
+
"streamBroker.getDeviceSettingsContribution": {
|
|
41410
|
+
capName: "stream-broker",
|
|
41411
|
+
capScope: "system",
|
|
41412
|
+
addonId: null,
|
|
41413
|
+
access: "view"
|
|
41414
|
+
},
|
|
38137
41415
|
"streamBroker.getPreBufferInfo": {
|
|
38138
41416
|
capName: "stream-broker",
|
|
38139
41417
|
capScope: "system",
|
|
@@ -38332,6 +41610,12 @@ Object.freeze({
|
|
|
38332
41610
|
addonId: null,
|
|
38333
41611
|
access: "view"
|
|
38334
41612
|
},
|
|
41613
|
+
"streamParams.getStatus": {
|
|
41614
|
+
capName: "stream-params",
|
|
41615
|
+
capScope: "device",
|
|
41616
|
+
addonId: null,
|
|
41617
|
+
access: "view"
|
|
41618
|
+
},
|
|
38335
41619
|
"streamParams.setProfile": {
|
|
38336
41620
|
capName: "stream-params",
|
|
38337
41621
|
capScope: "device",
|
|
@@ -38344,6 +41628,12 @@ Object.freeze({
|
|
|
38344
41628
|
addonId: null,
|
|
38345
41629
|
access: "view"
|
|
38346
41630
|
},
|
|
41631
|
+
"switch.getStatus": {
|
|
41632
|
+
capName: "switch",
|
|
41633
|
+
capScope: "device",
|
|
41634
|
+
addonId: null,
|
|
41635
|
+
access: "view"
|
|
41636
|
+
},
|
|
38347
41637
|
"switch.setState": {
|
|
38348
41638
|
capName: "switch",
|
|
38349
41639
|
capScope: "device",
|
|
@@ -38440,6 +41730,18 @@ Object.freeze({
|
|
|
38440
41730
|
addonId: null,
|
|
38441
41731
|
access: "create"
|
|
38442
41732
|
},
|
|
41733
|
+
"tamper.getStatus": {
|
|
41734
|
+
capName: "tamper",
|
|
41735
|
+
capScope: "device",
|
|
41736
|
+
addonId: null,
|
|
41737
|
+
access: "view"
|
|
41738
|
+
},
|
|
41739
|
+
"temperatureSensor.getStatus": {
|
|
41740
|
+
capName: "temperature-sensor",
|
|
41741
|
+
capScope: "device",
|
|
41742
|
+
addonId: null,
|
|
41743
|
+
access: "view"
|
|
41744
|
+
},
|
|
38443
41745
|
"terminalSession.adoptLegacyMonitor": {
|
|
38444
41746
|
capName: "terminal-session",
|
|
38445
41747
|
capScope: "system",
|
|
@@ -38536,6 +41838,12 @@ Object.freeze({
|
|
|
38536
41838
|
addonId: null,
|
|
38537
41839
|
access: "view"
|
|
38538
41840
|
},
|
|
41841
|
+
"update.getStatus": {
|
|
41842
|
+
capName: "update",
|
|
41843
|
+
capScope: "device",
|
|
41844
|
+
addonId: null,
|
|
41845
|
+
access: "view"
|
|
41846
|
+
},
|
|
38539
41847
|
"update.installUpdate": {
|
|
38540
41848
|
capName: "update",
|
|
38541
41849
|
capScope: "device",
|
|
@@ -38758,6 +42066,12 @@ Object.freeze({
|
|
|
38758
42066
|
addonId: null,
|
|
38759
42067
|
access: "create"
|
|
38760
42068
|
},
|
|
42069
|
+
"vacuumControl.getStatus": {
|
|
42070
|
+
capName: "vacuum-control",
|
|
42071
|
+
capScope: "device",
|
|
42072
|
+
addonId: null,
|
|
42073
|
+
access: "view"
|
|
42074
|
+
},
|
|
38761
42075
|
"vacuumControl.locate": {
|
|
38762
42076
|
capName: "vacuum-control",
|
|
38763
42077
|
capScope: "device",
|
|
@@ -38800,6 +42114,12 @@ Object.freeze({
|
|
|
38800
42114
|
addonId: null,
|
|
38801
42115
|
access: "create"
|
|
38802
42116
|
},
|
|
42117
|
+
"valve.getStatus": {
|
|
42118
|
+
capName: "valve",
|
|
42119
|
+
capScope: "device",
|
|
42120
|
+
addonId: null,
|
|
42121
|
+
access: "view"
|
|
42122
|
+
},
|
|
38803
42123
|
"valve.open": {
|
|
38804
42124
|
capName: "valve",
|
|
38805
42125
|
capScope: "device",
|
|
@@ -38872,6 +42192,18 @@ Object.freeze({
|
|
|
38872
42192
|
addonId: null,
|
|
38873
42193
|
access: "create"
|
|
38874
42194
|
},
|
|
42195
|
+
"vibration.getStatus": {
|
|
42196
|
+
capName: "vibration",
|
|
42197
|
+
capScope: "device",
|
|
42198
|
+
addonId: null,
|
|
42199
|
+
access: "view"
|
|
42200
|
+
},
|
|
42201
|
+
"videoclips.dialClipStream": {
|
|
42202
|
+
capName: "videoclips",
|
|
42203
|
+
capScope: "device",
|
|
42204
|
+
addonId: null,
|
|
42205
|
+
access: "view"
|
|
42206
|
+
},
|
|
38875
42207
|
"videoclips.getClipPlayback": {
|
|
38876
42208
|
capName: "videoclips",
|
|
38877
42209
|
capScope: "device",
|
|
@@ -38884,6 +42216,18 @@ Object.freeze({
|
|
|
38884
42216
|
addonId: null,
|
|
38885
42217
|
access: "view"
|
|
38886
42218
|
},
|
|
42219
|
+
"videoclips.listSources": {
|
|
42220
|
+
capName: "videoclips",
|
|
42221
|
+
capScope: "device",
|
|
42222
|
+
addonId: null,
|
|
42223
|
+
access: "view"
|
|
42224
|
+
},
|
|
42225
|
+
"videoclips.readClipBytes": {
|
|
42226
|
+
capName: "videoclips",
|
|
42227
|
+
capScope: "device",
|
|
42228
|
+
addonId: null,
|
|
42229
|
+
access: "view"
|
|
42230
|
+
},
|
|
38887
42231
|
"viewerUi.getStaticDir": {
|
|
38888
42232
|
capName: "viewer-ui",
|
|
38889
42233
|
capScope: "system",
|
|
@@ -38896,6 +42240,12 @@ Object.freeze({
|
|
|
38896
42240
|
addonId: null,
|
|
38897
42241
|
access: "view"
|
|
38898
42242
|
},
|
|
42243
|
+
"waterHeater.getStatus": {
|
|
42244
|
+
capName: "water-heater",
|
|
42245
|
+
capScope: "device",
|
|
42246
|
+
addonId: null,
|
|
42247
|
+
access: "view"
|
|
42248
|
+
},
|
|
38899
42249
|
"waterHeater.setAway": {
|
|
38900
42250
|
capName: "water-heater",
|
|
38901
42251
|
capScope: "device",
|
|
@@ -38914,6 +42264,12 @@ Object.freeze({
|
|
|
38914
42264
|
addonId: null,
|
|
38915
42265
|
access: "create"
|
|
38916
42266
|
},
|
|
42267
|
+
"weather.getStatus": {
|
|
42268
|
+
capName: "weather",
|
|
42269
|
+
capScope: "device",
|
|
42270
|
+
addonId: null,
|
|
42271
|
+
access: "view"
|
|
42272
|
+
},
|
|
38917
42273
|
"webrtcSession.addIceCandidate": {
|
|
38918
42274
|
capName: "webrtc-session",
|
|
38919
42275
|
capScope: "device",
|
|
@@ -38938,6 +42294,12 @@ Object.freeze({
|
|
|
38938
42294
|
addonId: null,
|
|
38939
42295
|
access: "view"
|
|
38940
42296
|
},
|
|
42297
|
+
"webrtcSession.getSessionDebug": {
|
|
42298
|
+
capName: "webrtc-session",
|
|
42299
|
+
capScope: "device",
|
|
42300
|
+
addonId: null,
|
|
42301
|
+
access: "view"
|
|
42302
|
+
},
|
|
38941
42303
|
"webrtcSession.getSessionState": {
|
|
38942
42304
|
capName: "webrtc-session",
|
|
38943
42305
|
capScope: "device",
|
|
@@ -39270,6 +42632,11 @@ Object.freeze({
|
|
|
39270
42632
|
form: "single",
|
|
39271
42633
|
optional: true
|
|
39272
42634
|
}],
|
|
42635
|
+
"deviceAdminLink.getAdminLink": [{
|
|
42636
|
+
name: "deviceId",
|
|
42637
|
+
form: "single",
|
|
42638
|
+
optional: false
|
|
42639
|
+
}],
|
|
39273
42640
|
"deviceAdoption.release": [{
|
|
39274
42641
|
name: "camDeviceId",
|
|
39275
42642
|
form: "single",
|
|
@@ -40180,6 +43547,11 @@ Object.freeze({
|
|
|
40180
43547
|
form: "single",
|
|
40181
43548
|
optional: false
|
|
40182
43549
|
}],
|
|
43550
|
+
"pipelineAnalytics.ownersWithMedia": [{
|
|
43551
|
+
name: "deviceId",
|
|
43552
|
+
form: "single",
|
|
43553
|
+
optional: false
|
|
43554
|
+
}],
|
|
40183
43555
|
"pipelineAnalytics.proposeRetrainAnnotations": [{
|
|
40184
43556
|
name: "deviceId",
|
|
40185
43557
|
form: "single",
|
|
@@ -40650,6 +44022,16 @@ Object.freeze({
|
|
|
40650
44022
|
form: "single",
|
|
40651
44023
|
optional: true
|
|
40652
44024
|
}],
|
|
44025
|
+
"recordingOnboard.getOptions": [{
|
|
44026
|
+
name: "deviceId",
|
|
44027
|
+
form: "single",
|
|
44028
|
+
optional: false
|
|
44029
|
+
}],
|
|
44030
|
+
"recordingOnboard.setSettings": [{
|
|
44031
|
+
name: "deviceId",
|
|
44032
|
+
form: "single",
|
|
44033
|
+
optional: false
|
|
44034
|
+
}],
|
|
40653
44035
|
"recordingSignal.getStatus": [{
|
|
40654
44036
|
name: "deviceId",
|
|
40655
44037
|
form: "single",
|
|
@@ -40876,6 +44258,11 @@ Object.freeze({
|
|
|
40876
44258
|
form: "single",
|
|
40877
44259
|
optional: false
|
|
40878
44260
|
}],
|
|
44261
|
+
"videoclips.dialClipStream": [{
|
|
44262
|
+
name: "deviceId",
|
|
44263
|
+
form: "single",
|
|
44264
|
+
optional: false
|
|
44265
|
+
}],
|
|
40879
44266
|
"videoclips.getClipPlayback": [{
|
|
40880
44267
|
name: "deviceId",
|
|
40881
44268
|
form: "single",
|
|
@@ -40886,6 +44273,16 @@ Object.freeze({
|
|
|
40886
44273
|
form: "single",
|
|
40887
44274
|
optional: false
|
|
40888
44275
|
}],
|
|
44276
|
+
"videoclips.listSources": [{
|
|
44277
|
+
name: "deviceId",
|
|
44278
|
+
form: "single",
|
|
44279
|
+
optional: false
|
|
44280
|
+
}],
|
|
44281
|
+
"videoclips.readClipBytes": [{
|
|
44282
|
+
name: "deviceId",
|
|
44283
|
+
form: "single",
|
|
44284
|
+
optional: false
|
|
44285
|
+
}],
|
|
40889
44286
|
"waterHeater.setAway": [{
|
|
40890
44287
|
name: "deviceId",
|
|
40891
44288
|
form: "single",
|
|
@@ -40921,6 +44318,11 @@ Object.freeze({
|
|
|
40921
44318
|
form: "single",
|
|
40922
44319
|
optional: false
|
|
40923
44320
|
}],
|
|
44321
|
+
"webrtcSession.getSessionDebug": [{
|
|
44322
|
+
name: "deviceId",
|
|
44323
|
+
form: "single",
|
|
44324
|
+
optional: false
|
|
44325
|
+
}],
|
|
40924
44326
|
"webrtcSession.getSessionState": [{
|
|
40925
44327
|
name: "deviceId",
|
|
40926
44328
|
form: "single",
|
|
@@ -50596,6 +53998,37 @@ var OnvifCamera = class {
|
|
|
50596
53998
|
},
|
|
50597
53999
|
invalidateCache: async () => {}
|
|
50598
54000
|
});
|
|
54001
|
+
this.ctx.registerNativeCap(deviceAdminLinkCapability, { getAdminLink: async ({ deviceId }) => {
|
|
54002
|
+
if (deviceId !== this.id) throw new Error(`OnvifCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
|
|
54003
|
+
const host = this.config.get("host") ?? "";
|
|
54004
|
+
const port = this.config.get("port") ?? 80;
|
|
54005
|
+
if (port !== 80 && port !== 443) {
|
|
54006
|
+
this.ctx.logger.debug("onvif admin link unavailable: service port is not an HTTP port", {
|
|
54007
|
+
tags: { deviceId: this.id },
|
|
54008
|
+
meta: { port }
|
|
54009
|
+
});
|
|
54010
|
+
return null;
|
|
54011
|
+
}
|
|
54012
|
+
const built = buildDeviceAdminUrl({
|
|
54013
|
+
host,
|
|
54014
|
+
port,
|
|
54015
|
+
scheme: port === 443 ? "https" : "http",
|
|
54016
|
+
path: "/"
|
|
54017
|
+
});
|
|
54018
|
+
if (!built.ok) {
|
|
54019
|
+
this.ctx.logger.debug("onvif admin link unavailable", {
|
|
54020
|
+
tags: { deviceId: this.id },
|
|
54021
|
+
meta: { reason: built.reason }
|
|
54022
|
+
});
|
|
54023
|
+
return null;
|
|
54024
|
+
}
|
|
54025
|
+
return {
|
|
54026
|
+
url: built.url,
|
|
54027
|
+
label: "Web UI",
|
|
54028
|
+
target: "device",
|
|
54029
|
+
host
|
|
54030
|
+
};
|
|
54031
|
+
} });
|
|
50599
54032
|
if (this.hasPtz()) this.ctx.registerNativeCap(ptzCapability, {
|
|
50600
54033
|
move: async ({ deviceId, pan, tilt, zoom, speed }) => {
|
|
50601
54034
|
if (deviceId !== this.id) throw new Error(`OnvifCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
|