@camstack/addon-provider-amcrest 0.2.117 → 0.2.118
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 +3523 -28
- package/dist/addon.mjs +3523 -28
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -5368,7 +5368,7 @@ var ZodIssueCode = {
|
|
|
5368
5368
|
var ZodFirstPartyTypeKind;
|
|
5369
5369
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5370
5370
|
//#endregion
|
|
5371
|
-
//#region ../types/dist/sleep-
|
|
5371
|
+
//#region ../types/dist/sleep-i3eUVc-d.mjs
|
|
5372
5372
|
/**
|
|
5373
5373
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5374
5374
|
* window to float samples (D455).
|
|
@@ -6510,6 +6510,17 @@ var CamProfileSchema = _enum([
|
|
|
6510
6510
|
"mid",
|
|
6511
6511
|
"low"
|
|
6512
6512
|
]);
|
|
6513
|
+
/**
|
|
6514
|
+
* Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
|
|
6515
|
+
*
|
|
6516
|
+
* `auto` is a REAL value, not an absent one — it means "resolve it by the rule
|
|
6517
|
+
* that consumer owns". It lives beside `CamProfile` because a rung is the only
|
|
6518
|
+
* operator-facing way to name a stream: a cam-stream id is broker-internal and
|
|
6519
|
+
* moves when a slot is reassigned, so a stored id silently stops meaning what
|
|
6520
|
+
* the operator picked. Today's one user is the audio analyser, whose rule is
|
|
6521
|
+
* "the lowest AVAILABLE native rung" (D537).
|
|
6522
|
+
*/
|
|
6523
|
+
var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
|
|
6513
6524
|
var CamStreamKindSchema = _enum([
|
|
6514
6525
|
"pull-rtsp",
|
|
6515
6526
|
"pull-rtmp",
|
|
@@ -6928,6 +6939,18 @@ function systemMethod(input, output, options) {
|
|
|
6928
6939
|
systemOnly: true
|
|
6929
6940
|
};
|
|
6930
6941
|
}
|
|
6942
|
+
/**
|
|
6943
|
+
* A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
|
|
6944
|
+
* on `InferProvider`. The `providerOptional: true` literal is what
|
|
6945
|
+
* `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
|
|
6946
|
+
* for when this is the honest answer and when it is a soft stub.
|
|
6947
|
+
*/
|
|
6948
|
+
function optionalMethod(input, output, options) {
|
|
6949
|
+
return {
|
|
6950
|
+
...method(input, output, options),
|
|
6951
|
+
providerOptional: true
|
|
6952
|
+
};
|
|
6953
|
+
}
|
|
6931
6954
|
/** Shorthand to define an event schema */
|
|
6932
6955
|
function event(data) {
|
|
6933
6956
|
return { data };
|
|
@@ -7346,6 +7369,8 @@ var EVENT_OWNER_TYPES = [
|
|
|
7346
7369
|
* nothing failing until a caller asked.
|
|
7347
7370
|
*/
|
|
7348
7371
|
var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
|
|
7372
|
+
/** The same list as a Zod enum, for the cap input that carries it. */
|
|
7373
|
+
var MediaPresenceOwnerKindSchema = _enum([...EVENT_OWNER_TYPES, "track"]);
|
|
7349
7374
|
new Set(EVENT_OWNER_TYPES);
|
|
7350
7375
|
var EncodeProfileSchema = object({
|
|
7351
7376
|
video: object({
|
|
@@ -7718,6 +7743,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
7718
7743
|
"device-audio",
|
|
7719
7744
|
"broker-audio",
|
|
7720
7745
|
"audio-analysis",
|
|
7746
|
+
"event-saving",
|
|
7721
7747
|
"recording",
|
|
7722
7748
|
"notifications"
|
|
7723
7749
|
]);
|
|
@@ -7753,10 +7779,20 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
7753
7779
|
var CameraSwitchUnavailableReasonSchema = _enum([
|
|
7754
7780
|
"no-provider",
|
|
7755
7781
|
"source-unreachable",
|
|
7756
|
-
"not-configured",
|
|
7757
7782
|
"device-disabled"
|
|
7758
7783
|
]);
|
|
7759
7784
|
/**
|
|
7785
|
+
* Something true about a switch that is nonetheless OFFERED.
|
|
7786
|
+
*
|
|
7787
|
+
* The distinction from {@link CameraSwitchUnavailableReasonSchema} is the
|
|
7788
|
+
* whole point: a reason replaces the control, a warning travels WITH it. The
|
|
7789
|
+
* privacy mask taught us the difference — greying the one button that starts
|
|
7790
|
+
* the feature teaches the operator the feature is broken, while a live toggle
|
|
7791
|
+
* that flips nothing teaches them the switch is broken. Neither is true; the
|
|
7792
|
+
* honest surface is a working toggle that says what is missing.
|
|
7793
|
+
*/
|
|
7794
|
+
var CameraSwitchWarningSchema = _enum(["no-privacy-zones"]);
|
|
7795
|
+
/**
|
|
7760
7796
|
* One switch, resolved for one camera.
|
|
7761
7797
|
*
|
|
7762
7798
|
* `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
|
|
@@ -7775,6 +7811,11 @@ var CameraSwitchSchema = object({
|
|
|
7775
7811
|
/** False = do not render a control. `unavailableReason` says why. */
|
|
7776
7812
|
available: boolean(),
|
|
7777
7813
|
unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
|
|
7814
|
+
/**
|
|
7815
|
+
* Present on an AVAILABLE switch only. Rendered beside the live control;
|
|
7816
|
+
* never a substitute for one.
|
|
7817
|
+
*/
|
|
7818
|
+
warning: CameraSwitchWarningSchema.optional(),
|
|
7778
7819
|
/** Current state. Meaningless when `available` is false — read it as `true`. */
|
|
7779
7820
|
enabled: boolean(),
|
|
7780
7821
|
authority: CameraSwitchAuthoritySchema
|
|
@@ -8192,11 +8233,31 @@ var RecordingBandTriggersSchema = RecordingTriggersSchema;
|
|
|
8192
8233
|
* band engine's `applies` rule). `start`/`end` are `HH:MM`; an `end <= start`
|
|
8193
8234
|
* span wraps past midnight (handled by the band engine).
|
|
8194
8235
|
*/
|
|
8236
|
+
/**
|
|
8237
|
+
* Whether a band's footage carries sound.
|
|
8238
|
+
*
|
|
8239
|
+
* The recorder has always attached with `audio: 'aac'`, so every band of every
|
|
8240
|
+
* camera recorded sound whether anyone wanted it or not. The broker already
|
|
8241
|
+
* serves the other answer — `audio: 'none'` takes the video-only `/muted`
|
|
8242
|
+
* restream — so this was a choice the product could always have offered.
|
|
8243
|
+
*
|
|
8244
|
+
* It belongs on the BAND rather than the camera because one camera can want
|
|
8245
|
+
* both: silent across the shop floor during opening hours, sound overnight.
|
|
8246
|
+
* And it applies to BOTH modes — an events band firing on motion has the same
|
|
8247
|
+
* question to answer as a continuous one.
|
|
8248
|
+
*/
|
|
8249
|
+
var RecordingBandAudioSchema = _enum(["on", "muted"]);
|
|
8195
8250
|
var RecordingBandSchema = object({
|
|
8196
8251
|
days: array(RecordingWeekdaySchema),
|
|
8197
8252
|
start: string().regex(HHMM),
|
|
8198
8253
|
end: string().regex(HHMM),
|
|
8199
8254
|
mode: RecordingBandModeSchema,
|
|
8255
|
+
/**
|
|
8256
|
+
* Sound, or silence. OPTIONAL and absent-means-`on`: every recording
|
|
8257
|
+
* written before this existed carried sound, and a band whose key is
|
|
8258
|
+
* missing must keep meaning what it meant. See {@link RecordingBandAudioSchema}.
|
|
8259
|
+
*/
|
|
8260
|
+
audio: RecordingBandAudioSchema.optional(),
|
|
8200
8261
|
triggers: RecordingBandTriggersSchema.optional(),
|
|
8201
8262
|
/**
|
|
8202
8263
|
* RETIRED (D381). A band no longer carries a pre-buffer of its own.
|
|
@@ -13480,6 +13541,210 @@ method(object({ integrationId: string() }), object({ filters: array(AdoptionFilt
|
|
|
13480
13541
|
auth: "admin"
|
|
13481
13542
|
});
|
|
13482
13543
|
/**
|
|
13544
|
+
* device-admin-link — "this device has a management page of its own, and here
|
|
13545
|
+
* is its address".
|
|
13546
|
+
*
|
|
13547
|
+
* ## Why this is not a `deviceConfig` cap
|
|
13548
|
+
*
|
|
13549
|
+
* There is nothing to edit. A `deviceConfig` cap (D14) exists so the framework
|
|
13550
|
+
* can DERIVE a settings form from `getOptions` + `getStatus` and route a flat
|
|
13551
|
+
* patch back through a setter; it costs a `builderId` reducer in
|
|
13552
|
+
* `device-config-contribution.ts` and a `*-config-schema.ts` beside it, and it
|
|
13553
|
+
* renders a form section. This cap answers ONE question with ONE read and
|
|
13554
|
+
* renders a button. Nothing about it is a form, so it carries no `deviceConfig`
|
|
13555
|
+
* block, no `settings`, no `runtimeState` and no reducer — exactly like
|
|
13556
|
+
* `reboot`, the other pure-RPC device-native cap.
|
|
13557
|
+
*
|
|
13558
|
+
* ## Absent, and the difference between "no page" and "we cannot say"
|
|
13559
|
+
*
|
|
13560
|
+
* The two are answered at DIFFERENT layers, on purpose:
|
|
13561
|
+
*
|
|
13562
|
+
* - **"We cannot say"** → the provider never registers the cap for that
|
|
13563
|
+
* device. A VeSync humidifier, a Petkit feeder, a Dreame vacuum and a Dreo
|
|
13564
|
+
* fan are reached only through a vendor cloud; there is no address to hand
|
|
13565
|
+
* out and no page to open. A Tuya plug, a Wyze camera and a Gree air
|
|
13566
|
+
* conditioner DO have a LAN IP, and still have no HTTP management page
|
|
13567
|
+
* behind it. None of them register, so `deviceManager.getBindings` never
|
|
13568
|
+
* lists the cap and no surface asks.
|
|
13569
|
+
* - **"This device has no page, and I know that"** → the provider registers
|
|
13570
|
+
* and `getAdminLink` returns `null`. This is the answer for a device whose
|
|
13571
|
+
* sibling DOES have a page: a Reolink battery camera reached over UDP by
|
|
13572
|
+
* `uid` with a blank `host`, an Ecowitt gateway configured in `listener`
|
|
13573
|
+
* transport, a Home Assistant broker authenticated by supervisor token
|
|
13574
|
+
* (which carries no `baseUrl` at all).
|
|
13575
|
+
*
|
|
13576
|
+
* Both draw NOTHING. A button that opens a browser error is worse than no
|
|
13577
|
+
* button, and D62 is the same rule from the other side: an off switch is
|
|
13578
|
+
* reported off, never made to look broken. There is no third state where the
|
|
13579
|
+
* UI renders a disabled button "because the device might have a page".
|
|
13580
|
+
*
|
|
13581
|
+
* ## The URL never carries credentials
|
|
13582
|
+
*
|
|
13583
|
+
* Not in userinfo, not in a query string. Every provider builds through
|
|
13584
|
+
* `buildDeviceAdminUrl` (`device-admin-link-url.ts`), which takes host, port,
|
|
13585
|
+
* scheme and path as separate arguments — there is no parameter a secret could
|
|
13586
|
+
* arrive in — and re-checks its own output for the `scheme://user:pass@` shape
|
|
13587
|
+
* that `scripts/check-no-credential-urls-in-fixtures.ts` bans from recorded
|
|
13588
|
+
* output. `scripts/check-admin-link-builder-is-the-only-url-source.ts` is what
|
|
13589
|
+
* keeps providers from hand-rolling one anyway.
|
|
13590
|
+
*
|
|
13591
|
+
* This matters here more than anywhere else in the repo, because every provider
|
|
13592
|
+
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
13593
|
+
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
13594
|
+
* and `http://admin:hunter2@192.168.50.139/` is a URL a browser accepts. The
|
|
13595
|
+
* camera's own page will ask for its own login. That is correct, and pre-
|
|
13596
|
+
* filling it is the operator's business, not ours.
|
|
13597
|
+
*
|
|
13598
|
+
* ## It is a LAN fact
|
|
13599
|
+
*
|
|
13600
|
+
* The URL addresses the device where the NODE can see it. It is not proxied,
|
|
13601
|
+
* not made reachable from outside, and not sent anywhere. A surface renders it
|
|
13602
|
+
* as a link the operator's own browser follows, on the operator's own network,
|
|
13603
|
+
* or renders nothing.
|
|
13604
|
+
*/
|
|
13605
|
+
/**
|
|
13606
|
+
* Whose page is it. The distinction is for the OPERATOR, who needs to know
|
|
13607
|
+
* before clicking whether he is about to land on a camera's own web server or
|
|
13608
|
+
* inside Home Assistant.
|
|
13609
|
+
*/
|
|
13610
|
+
var AdminLinkTargetEnum = _enum(["device", "integration"]);
|
|
13611
|
+
var DeviceAdminLinkSchema = object({
|
|
13612
|
+
/**
|
|
13613
|
+
* Absolute `http(s)://` URL. Built by `buildDeviceAdminUrl` and therefore
|
|
13614
|
+
* free of userinfo and of any credential-shaped query key.
|
|
13615
|
+
*/
|
|
13616
|
+
url: string(),
|
|
13617
|
+
/**
|
|
13618
|
+
* What the surface calls it — "Web UI", "Home Assistant", "UniFi controller".
|
|
13619
|
+
* The PROVIDER names it, because only the provider knows what the page is;
|
|
13620
|
+
* a UI that invented the label from the addon id would call the Home
|
|
13621
|
+
* Assistant device page "Provider Homeassistant".
|
|
13622
|
+
*/
|
|
13623
|
+
label: string(),
|
|
13624
|
+
target: AdminLinkTargetEnum,
|
|
13625
|
+
/**
|
|
13626
|
+
* Host the URL points at, without scheme, port or path — for the tooltip, so
|
|
13627
|
+
* an operator can see WHERE the button goes before he follows it. Redundant
|
|
13628
|
+
* with `url` by construction; carried separately so no surface has to parse
|
|
13629
|
+
* a URL to show it.
|
|
13630
|
+
*/
|
|
13631
|
+
host: string()
|
|
13632
|
+
});
|
|
13633
|
+
var deviceAdminLinkCapability = {
|
|
13634
|
+
name: "device-admin-link",
|
|
13635
|
+
scope: "device",
|
|
13636
|
+
deviceNative: true,
|
|
13637
|
+
mode: "singleton",
|
|
13638
|
+
methods: {
|
|
13639
|
+
/**
|
|
13640
|
+
* The device's management page, or `null` when this device has none.
|
|
13641
|
+
*
|
|
13642
|
+
* `auth: 'admin'` deliberately. This is administration, not actuation —
|
|
13643
|
+
* the same bucket as `reboot` and `camera-credentials`, and explicitly NOT
|
|
13644
|
+
* the actuation set `scripts/check-actuation-not-admin.ts` protects (D403).
|
|
13645
|
+
* The URL is also a statement about the LAN, which a household member with
|
|
13646
|
+
* a `view` grant on a light has no reason to be handed.
|
|
13647
|
+
*
|
|
13648
|
+
* The surfaces gate on the QUERY, never on a role they guessed: a caller
|
|
13649
|
+
* without the right loses the query and draws nothing, which is the same
|
|
13650
|
+
* thing a device with no page draws. There is no path on which a button
|
|
13651
|
+
* appears and then fails — the D403 failure mode, from the other end.
|
|
13652
|
+
*/
|
|
13653
|
+
getAdminLink: method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" }) }
|
|
13654
|
+
};
|
|
13655
|
+
/**
|
|
13656
|
+
* Query keys that may never appear on an admin link. A credential smuggled as
|
|
13657
|
+
* `?password=` is the same leak as userinfo, in a form the userinfo check
|
|
13658
|
+
* cannot see: it survives copy-paste, referrer headers and proxy logs
|
|
13659
|
+
* identically.
|
|
13660
|
+
*/
|
|
13661
|
+
var CREDENTIAL_QUERY_KEYS = [
|
|
13662
|
+
"password",
|
|
13663
|
+
"passwd",
|
|
13664
|
+
"pwd",
|
|
13665
|
+
"pass",
|
|
13666
|
+
"user",
|
|
13667
|
+
"username",
|
|
13668
|
+
"usr",
|
|
13669
|
+
"login",
|
|
13670
|
+
"token",
|
|
13671
|
+
"access_token",
|
|
13672
|
+
"auth",
|
|
13673
|
+
"authorization",
|
|
13674
|
+
"apikey",
|
|
13675
|
+
"api_key",
|
|
13676
|
+
"secret",
|
|
13677
|
+
"credential",
|
|
13678
|
+
"credentials",
|
|
13679
|
+
"session",
|
|
13680
|
+
"sessionid",
|
|
13681
|
+
"key"
|
|
13682
|
+
];
|
|
13683
|
+
/**
|
|
13684
|
+
* The same userinfo shape `scripts/check-no-credential-urls-in-fixtures.ts`
|
|
13685
|
+
* scans recorded fixtures for, applied to what we are about to EMIT. Kept
|
|
13686
|
+
* structurally identical on purpose: a URL this function returns is a URL that
|
|
13687
|
+
* guard would pass.
|
|
13688
|
+
*/
|
|
13689
|
+
var CREDENTIAL_URL = /[a-z][a-z0-9+.-]*:\/\/[^/\s:@'"]+:[^/\s:@'"]+@/i;
|
|
13690
|
+
/** A host that is safe to place in an authority component verbatim. */
|
|
13691
|
+
var PLAIN_HOST = /^[a-zA-Z0-9._-]+$/;
|
|
13692
|
+
/** A bracketed IPv6 literal, the only other authority form we emit. */
|
|
13693
|
+
var IPV6_HOST = /^\[[0-9a-fA-F:.]+\]$/;
|
|
13694
|
+
function defaultPortFor(scheme) {
|
|
13695
|
+
return scheme === "https" ? 443 : 80;
|
|
13696
|
+
}
|
|
13697
|
+
/**
|
|
13698
|
+
* Build the admin-page URL, or refuse by name.
|
|
13699
|
+
*
|
|
13700
|
+
* The refusal is never thrown: a provider answering "no page for this device"
|
|
13701
|
+
* is an ordinary answer (`getAdminLink` → `null`), and a throw here would turn
|
|
13702
|
+
* a missing host on one device into a failed query for the whole surface.
|
|
13703
|
+
*/
|
|
13704
|
+
function buildDeviceAdminUrl(parts) {
|
|
13705
|
+
const host = parts.host.trim();
|
|
13706
|
+
if (host.length === 0) return {
|
|
13707
|
+
ok: false,
|
|
13708
|
+
reason: "empty-host"
|
|
13709
|
+
};
|
|
13710
|
+
if (host.includes("@")) return {
|
|
13711
|
+
ok: false,
|
|
13712
|
+
reason: "host-carries-userinfo"
|
|
13713
|
+
};
|
|
13714
|
+
if (!PLAIN_HOST.test(host) && !IPV6_HOST.test(host)) return {
|
|
13715
|
+
ok: false,
|
|
13716
|
+
reason: "host-not-plain"
|
|
13717
|
+
};
|
|
13718
|
+
if (parts.port !== null) {
|
|
13719
|
+
if (!Number.isInteger(parts.port) || parts.port < 1 || parts.port > 65535) return {
|
|
13720
|
+
ok: false,
|
|
13721
|
+
reason: "port-out-of-range"
|
|
13722
|
+
};
|
|
13723
|
+
}
|
|
13724
|
+
if (!parts.path.startsWith("/")) return {
|
|
13725
|
+
ok: false,
|
|
13726
|
+
reason: "path-not-absolute"
|
|
13727
|
+
};
|
|
13728
|
+
const query = parts.query ?? {};
|
|
13729
|
+
for (const key of Object.keys(query)) if (CREDENTIAL_QUERY_KEYS.includes(key.toLowerCase())) return {
|
|
13730
|
+
ok: false,
|
|
13731
|
+
reason: "credential-query-key"
|
|
13732
|
+
};
|
|
13733
|
+
const authority = parts.port === null || parts.port === defaultPortFor(parts.scheme) ? host : `${host}:${String(parts.port)}`;
|
|
13734
|
+
const search = new URLSearchParams();
|
|
13735
|
+
for (const [key, value] of Object.entries(query)) search.append(key, value);
|
|
13736
|
+
const suffix = search.size > 0 ? `?${search.toString()}` : "";
|
|
13737
|
+
const url = `${parts.scheme}://${authority}${parts.path}${suffix}`;
|
|
13738
|
+
if (CREDENTIAL_URL.test(url)) return {
|
|
13739
|
+
ok: false,
|
|
13740
|
+
reason: "userinfo-in-result"
|
|
13741
|
+
};
|
|
13742
|
+
return {
|
|
13743
|
+
ok: true,
|
|
13744
|
+
url
|
|
13745
|
+
};
|
|
13746
|
+
}
|
|
13747
|
+
/**
|
|
13483
13748
|
* `device-export` — collection cap for addons that export camstack
|
|
13484
13749
|
* devices to external ecosystems (HomeAssistant via MQTT discovery,
|
|
13485
13750
|
* HomeKit/HAP, Alexa Smart Home, …).
|
|
@@ -20246,6 +20511,7 @@ var MediaFileKindEnum = _enum([
|
|
|
20246
20511
|
"keyFrameSmall",
|
|
20247
20512
|
"thumbnailSmall",
|
|
20248
20513
|
"confirmationCrop",
|
|
20514
|
+
"debugCrop",
|
|
20249
20515
|
"sceneFrame"
|
|
20250
20516
|
]);
|
|
20251
20517
|
/**
|
|
@@ -21325,7 +21591,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
21325
21591
|
ownerType: EventOwnerTypeSchema,
|
|
21326
21592
|
eventId: number().int(),
|
|
21327
21593
|
deviceId: number()
|
|
21328
|
-
}), array(MediaFileInfoSchema).readonly()), method(
|
|
21594
|
+
}), array(MediaFileInfoSchema).readonly()), method(object({
|
|
21595
|
+
deviceId: number(),
|
|
21596
|
+
ownerKind: MediaPresenceOwnerKindSchema,
|
|
21597
|
+
ownerIds: array(string()).max(5e3)
|
|
21598
|
+
}), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
21329
21599
|
kind: "mutation",
|
|
21330
21600
|
auth: "admin"
|
|
21331
21601
|
}), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
|
|
@@ -21910,6 +22180,18 @@ var RunnerCameraConfigSchema = object({
|
|
|
21910
22180
|
motionStreamId: string(),
|
|
21911
22181
|
detectionStreamId: string(),
|
|
21912
22182
|
/**
|
|
22183
|
+
* Which rung this camera's audio analysis listens on — `auto` (the lowest
|
|
22184
|
+
* AVAILABLE native rung) or an explicit one.
|
|
22185
|
+
*
|
|
22186
|
+
* Not consumed by the runner: carried here so it travels with the camera's
|
|
22187
|
+
* config the way `audioMotionWindowMs` does, and so the orchestrator's
|
|
22188
|
+
* watchdog resolves the same stream the audio subscription did. OPTIONAL
|
|
22189
|
+
* with no default — absent means `auto`, which is what every camera written
|
|
22190
|
+
* before this field had, and an absent optional adds nothing to the
|
|
22191
|
+
* generated cap-input defaults (D537).
|
|
22192
|
+
*/
|
|
22193
|
+
audioStreamPick: AudioStreamPickSchema.optional(),
|
|
22194
|
+
/**
|
|
21913
22195
|
* Motion sources that can activate the pipeline. At least one must
|
|
21914
22196
|
* be present. Multiple sources OR-ed: any one firing arms the
|
|
21915
22197
|
* cooldown timer and transitions watching → active. The same
|
|
@@ -22980,10 +23262,9 @@ var ServerBootModeSchema = _enum([
|
|
|
22980
23262
|
* - `idle` / `checking` / `staging` — steady / in-flight registry work.
|
|
22981
23263
|
* - `pending-restart` — a version is staged and the node has NOT yet
|
|
22982
23264
|
* restarted onto it (still running the OLD version).
|
|
22983
|
-
* - `awaiting-confirmation` —
|
|
22984
|
-
*
|
|
22985
|
-
*
|
|
22986
|
-
* manually restarted, or the probation boot auto-rolls-back.
|
|
23265
|
+
* - `awaiting-confirmation` — RETIRED with the probation boot (single-copy
|
|
23266
|
+
* collapse, 2026-07-18). Kept in the enum so an older client still parses;
|
|
23267
|
+
* no provider emits it.
|
|
22987
23268
|
*/
|
|
22988
23269
|
var ServerUpdateStateSchema = _enum([
|
|
22989
23270
|
"idle",
|
|
@@ -23072,12 +23353,35 @@ var ServerRollbackInfoSchema = object({
|
|
|
23072
23353
|
var ServerPackageStatusSchema = object({
|
|
23073
23354
|
/** Root package name (`@camstack/server` on the hub). */
|
|
23074
23355
|
packageName: string(),
|
|
23075
|
-
/**
|
|
23356
|
+
/**
|
|
23357
|
+
* Version of the code THIS PROCESS loaded — captured once, at boot, from the
|
|
23358
|
+
* package.json of the copy the code was loaded from, and never re-read. A
|
|
23359
|
+
* copy replaced on disk under a live process must not move this number;
|
|
23360
|
+
* that divergence is what `installedVersion` is for. Before 2026-09-20 this
|
|
23361
|
+
* was a lazy disk read that would have followed such a swap silently.
|
|
23362
|
+
*/
|
|
23076
23363
|
runningVersion: string().nullable(),
|
|
23077
23364
|
/** Node.js runtime version the node's process runs on (`process.versions.node`). */
|
|
23078
23365
|
nodeRuntimeVersion: string().nullable(),
|
|
23079
23366
|
/** Active data-dir root version; null when booted from seed/workspace. */
|
|
23080
23367
|
activeVersion: string().nullable(),
|
|
23368
|
+
/**
|
|
23369
|
+
* Version of the root package in the data-root `current/` copy RIGHT NOW —
|
|
23370
|
+
* what the next boot would load if nothing is staged. Normally equal to
|
|
23371
|
+
* `runningVersion`; differs only when the copy on disk changed under the
|
|
23372
|
+
* live process. `null` when there is no data-root copy (baked seed or
|
|
23373
|
+
* workspace boot). Optional for version skew: an older provider omits it.
|
|
23374
|
+
*/
|
|
23375
|
+
installedVersion: string().nullable().optional(),
|
|
23376
|
+
/**
|
|
23377
|
+
* True when the code that would run after a restart is NOT the code running
|
|
23378
|
+
* now: a version is staged (`pendingVersion`), or `installedVersion` differs
|
|
23379
|
+
* from `runningVersion`. Derived on every read, never stored. This is the
|
|
23380
|
+
* one field that lets an operator or a script say "1.2.353 is installed and
|
|
23381
|
+
* will run after a restart" instead of reading it off a number that means
|
|
23382
|
+
* "installed". Optional for version skew.
|
|
23383
|
+
*/
|
|
23384
|
+
restartRequired: boolean().optional(),
|
|
23081
23385
|
/** N-1 version kept for rollback; null when no previous version exists. */
|
|
23082
23386
|
previousVersion: string().nullable(),
|
|
23083
23387
|
/** Version of the immutable baked seed closure (image fallback). */
|
|
@@ -23112,19 +23416,71 @@ var ServerPackageStatusSchema = object({
|
|
|
23112
23416
|
});
|
|
23113
23417
|
var ServerUpdateCheckResultSchema = object({
|
|
23114
23418
|
packageName: string(),
|
|
23419
|
+
/** Same meaning as on the status payload: what THIS process loaded. */
|
|
23115
23420
|
runningVersion: string().nullable(),
|
|
23116
23421
|
latestVersion: string().nullable(),
|
|
23422
|
+
/**
|
|
23423
|
+
* `latestVersion` is newer than `runningVersion` AND is not the version
|
|
23424
|
+
* already staged on this node. A staged-and-waiting version is not "an
|
|
23425
|
+
* update available" — applying it again is refused (`already-staged`), and
|
|
23426
|
+
* a button that announced "updating…" on this boolean then got refused
|
|
23427
|
+
* would be lying. It is reported through `pendingVersion` instead.
|
|
23428
|
+
*/
|
|
23117
23429
|
updateAvailable: boolean(),
|
|
23118
23430
|
checkedAtMs: number(),
|
|
23119
23431
|
/** Non-null when the registry lookup failed (offline, bad registry, …). */
|
|
23120
|
-
error: string().nullable()
|
|
23432
|
+
error: string().nullable(),
|
|
23433
|
+
/**
|
|
23434
|
+
* Version staged on this node and awaiting its restart, when one is. Before
|
|
23435
|
+
* 2026-09-20 a check answered `{runningVersion, updateAvailable:false}` for a
|
|
23436
|
+
* node whose staged closure had never booted, and the caller had no way to
|
|
23437
|
+
* see the difference. Optional for version skew.
|
|
23438
|
+
*/
|
|
23439
|
+
pendingVersion: string().nullable().optional(),
|
|
23440
|
+
/** See the status payload: staged, or installed-on-disk ≠ running. Optional for skew. */
|
|
23441
|
+
restartRequired: boolean().optional()
|
|
23121
23442
|
});
|
|
23443
|
+
/**
|
|
23444
|
+
* What an action DID — the discriminant `accepted` cannot carry. Before
|
|
23445
|
+
* 2026-09-20 `applyServerUpdate` answered `accepted:false` both for a refusal
|
|
23446
|
+
* where nothing was wrong (already current, already staged) and for a staging
|
|
23447
|
+
* install that died on `npm error code ECONNRESET`; the only difference was
|
|
23448
|
+
* prose in `message`, and an operator restarted a container on the strength of
|
|
23449
|
+
* it, booting the previous version.
|
|
23450
|
+
*
|
|
23451
|
+
* - `staged` — the closure is installed and validated; the node
|
|
23452
|
+
* restarts to apply it (`restarting:true`). NOTHING
|
|
23453
|
+
* runs the new code until that restart completes.
|
|
23454
|
+
* - `restart-scheduled` — a plain, version-preserving restart was accepted.
|
|
23455
|
+
* - `already-current` — the target IS the running version; nothing done.
|
|
23456
|
+
* - `refused` — dropped by a gate; `reason` names which.
|
|
23457
|
+
* - `failed` — attempted and did not land; `error` says why.
|
|
23458
|
+
*/
|
|
23459
|
+
var ServerUpdateOutcomeSchema = _enum([
|
|
23460
|
+
"staged",
|
|
23461
|
+
"restart-scheduled",
|
|
23462
|
+
"already-current",
|
|
23463
|
+
"refused",
|
|
23464
|
+
"failed"
|
|
23465
|
+
]);
|
|
23122
23466
|
var ServerUpdateActionResultSchema = object({
|
|
23123
23467
|
accepted: boolean(),
|
|
23124
23468
|
targetVersion: string().nullable(),
|
|
23125
23469
|
/** True when a graceful restart was scheduled to apply the change. */
|
|
23126
23470
|
restarting: boolean(),
|
|
23127
|
-
message: string()
|
|
23471
|
+
message: string(),
|
|
23472
|
+
/** See {@link ServerUpdateOutcomeSchema}. Optional for version skew. */
|
|
23473
|
+
outcome: ServerUpdateOutcomeSchema.optional(),
|
|
23474
|
+
/**
|
|
23475
|
+
* Stable, greppable cause for `refused` / `failed` (the engine's
|
|
23476
|
+
* `UpdateRefusalReason`, `staging-failed`, …). `null` on success.
|
|
23477
|
+
*/
|
|
23478
|
+
reason: string().nullable().optional(),
|
|
23479
|
+
/**
|
|
23480
|
+
* The failure itself, verbatim enough to act on (the npm output for a
|
|
23481
|
+
* staging install). Non-null only for `failed`.
|
|
23482
|
+
*/
|
|
23483
|
+
error: string().nullable().optional()
|
|
23128
23484
|
});
|
|
23129
23485
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
23130
23486
|
kind: "mutation",
|
|
@@ -24337,6 +24693,18 @@ method(VectorDeclareIndexInputSchema, _void(), {
|
|
|
24337
24693
|
kind: "mutation",
|
|
24338
24694
|
auth: "admin"
|
|
24339
24695
|
}), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
|
|
24696
|
+
_enum([
|
|
24697
|
+
"queue-full",
|
|
24698
|
+
"camera-backoff",
|
|
24699
|
+
"sleeping",
|
|
24700
|
+
"camera-refused",
|
|
24701
|
+
"no-keyframe",
|
|
24702
|
+
"no-catalog-row",
|
|
24703
|
+
"unsupported",
|
|
24704
|
+
"unknown-device",
|
|
24705
|
+
"uid-missing"
|
|
24706
|
+
]);
|
|
24707
|
+
_enum(["deferred", "final"]);
|
|
24340
24708
|
var ClipSchema = object({
|
|
24341
24709
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
24342
24710
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -24411,29 +24779,455 @@ var ClipSchema = object({
|
|
|
24411
24779
|
holes: array(object({
|
|
24412
24780
|
startMs: number(),
|
|
24413
24781
|
endMs: number()
|
|
24414
|
-
})).optional()
|
|
24782
|
+
})).optional(),
|
|
24783
|
+
/**
|
|
24784
|
+
* A URL that MAY produce this clip's still, asked only for rows actually on
|
|
24785
|
+
* screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
|
|
24786
|
+
* (a JPEG is already on disk), this one is an offer. The route answers 200
|
|
24787
|
+
* with the image, or 204 with `x-camstack-reason` when it could not mint one
|
|
24788
|
+
* — a surface latches that refusal to the instant rather than retrying.
|
|
24789
|
+
*
|
|
24790
|
+
* Never both: a clip with a vouched `thumbnail` needs no mint.
|
|
24791
|
+
*/
|
|
24792
|
+
thumbnailMint: string().optional(),
|
|
24793
|
+
/**
|
|
24794
|
+
* Why no still will be produced for this clip RIGHT NOW — set when the
|
|
24795
|
+
* provider already knows, so the surface draws the glyph and the reason
|
|
24796
|
+
* instead of firing a mint that cannot succeed.
|
|
24797
|
+
*
|
|
24798
|
+
* `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
|
|
24799
|
+
* `camera-refused` — the camera answered the CoverPreview with a refusal.
|
|
24800
|
+
* `no-keyframe` — the window holds no decodable I-frame (an event that sits
|
|
24801
|
+
* inside no file is the measured case).
|
|
24802
|
+
* `unsupported` — this source cannot mint stills at all.
|
|
24803
|
+
*/
|
|
24804
|
+
thumbnailUnavailable: object({ reason: _enum([
|
|
24805
|
+
"sleeping",
|
|
24806
|
+
"camera-refused",
|
|
24807
|
+
"no-keyframe",
|
|
24808
|
+
"unsupported"
|
|
24809
|
+
]) }).optional(),
|
|
24810
|
+
/**
|
|
24811
|
+
* When the catalog this row came from was last CONFIRMED against the device.
|
|
24812
|
+
* Absent means "this row was read live". A persisted catalog served while a
|
|
24813
|
+
* camera sleeps carries the age it really has — a cached list is never drawn
|
|
24814
|
+
* as current (D549 13).
|
|
24815
|
+
*/
|
|
24816
|
+
catalogAsOf: number().optional(),
|
|
24817
|
+
/**
|
|
24818
|
+
* The camera's OWN type strings for this clip, all of them, unmapped
|
|
24819
|
+
* (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
|
|
24820
|
+
* because a firmware inventing a type must not vanish: the mapping into our
|
|
24821
|
+
* filter vocabulary is lossy on purpose and this is the lossless copy
|
|
24822
|
+
* (D549 20).
|
|
24823
|
+
*/
|
|
24824
|
+
nativeTypes: array(string()).optional(),
|
|
24825
|
+
/**
|
|
24826
|
+
* What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
|
|
24827
|
+
* travels BESIDE a class, never instead of one, and the class filter ignores
|
|
24828
|
+
* it: "a person crossed a line" is still a person (D549 20).
|
|
24829
|
+
*/
|
|
24830
|
+
behaviours: array(string()).optional(),
|
|
24831
|
+
/**
|
|
24832
|
+
* The same recording as two files — the sub twin (what the row and its
|
|
24833
|
+
* thumbnail are) and its main twin, matched at listing time so a quality
|
|
24834
|
+
* change never re-searches the camera (D549 15). `getClipPlayback`'s
|
|
24835
|
+
* `profile` picks between them: `low | mid` → sub, `high` → main.
|
|
24836
|
+
*/
|
|
24837
|
+
streams: object({
|
|
24838
|
+
sub: object({
|
|
24839
|
+
id: string(),
|
|
24840
|
+
bytes: number().optional()
|
|
24841
|
+
}).optional(),
|
|
24842
|
+
main: object({
|
|
24843
|
+
id: string(),
|
|
24844
|
+
bytes: number().optional()
|
|
24845
|
+
}).optional()
|
|
24846
|
+
}).optional(),
|
|
24847
|
+
/** The camera says it holds a sub-stream copy of this recording. */
|
|
24848
|
+
supportSub: boolean().optional(),
|
|
24849
|
+
/**
|
|
24850
|
+
* Whether this row has bytes behind it. ABSENT means yes — every clip that
|
|
24851
|
+
* IS a file is playable, and only a source that lists EVENTS can produce a
|
|
24852
|
+
* row with nothing to play (the measured case: 1 of 29 hub events on 3628
|
|
24853
|
+
* fell inside no file). Such a row is shown, never dropped and never offered
|
|
24854
|
+
* as playable-then-failing (D549 19).
|
|
24855
|
+
*/
|
|
24856
|
+
playable: boolean().optional(),
|
|
24857
|
+
/** Why {@link playable} is false, verbatim (`no-file-for-window`). */
|
|
24858
|
+
unplayableReason: string().optional(),
|
|
24859
|
+
/**
|
|
24860
|
+
* This clip is STILL BEING WRITTEN, so {@link ClipSchema.timeRange}`.endMs`
|
|
24861
|
+
* is not its end.
|
|
24862
|
+
*
|
|
24863
|
+
* Absent — the common case — means the row is closed and its `endMs` is the
|
|
24864
|
+
* end of the recording. Present and `true` means the source told us the file
|
|
24865
|
+
* has no end yet, and the `endMs` we carry is whatever the camera's index
|
|
24866
|
+
* entry happened to hold: on a Reolink E1 Outdoor PoE (592, measured
|
|
24867
|
+
* 2026-09-20) the newest file `…_192927_000000_…_0.mp4` reported an `endTime`
|
|
24868
|
+
* of 19:29:56 and STILL reported it eight minutes later, while the file went
|
|
24869
|
+
* on growing. A surface that drew 29 s there was lying about a clip that
|
|
24870
|
+
* plays for minutes, on the one row the operator looks at first.
|
|
24871
|
+
*
|
|
24872
|
+
* `endMs` is deliberately still a number: it is the best bound anything has
|
|
24873
|
+
* for a mint window or a byte fetch, and every consumer already requires it.
|
|
24874
|
+
* This flag says what it is WORTH, and a duration is not drawn from it.
|
|
24875
|
+
*/
|
|
24876
|
+
inProgress: boolean().optional()
|
|
24415
24877
|
});
|
|
24416
24878
|
var ClipPlaybackSchema = object({
|
|
24417
|
-
/**
|
|
24879
|
+
/**
|
|
24880
|
+
* A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
|
|
24881
|
+
* says what kind — an HLS master playlist for a recording-derived clip, a
|
|
24882
|
+
* progressive MP4 for a native file the vendor addon muxed and serves with
|
|
24883
|
+
* `Range`. The URL is NOT a credential: both planes are registered
|
|
24884
|
+
* `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
|
|
24885
|
+
* the session cookie, so nothing is appended to it (D549 22).
|
|
24886
|
+
*/
|
|
24418
24887
|
playbackUrl: string(),
|
|
24888
|
+
/**
|
|
24889
|
+
* How to play {@link playbackUrl}. Absent means `hls` — the shape every
|
|
24890
|
+
* existing consumer already assumes. A player that branches on this is the
|
|
24891
|
+
* one change a native clip needs; a player that ignores it will hand an MP4
|
|
24892
|
+
* to hls.js and fail parsing it as a manifest.
|
|
24893
|
+
*/
|
|
24894
|
+
format: _enum(["hls", "mp4"]).optional(),
|
|
24895
|
+
/**
|
|
24896
|
+
* Which twin was actually served. A clip that holds only one stream answers
|
|
24897
|
+
* with the one it has, and the surface SAYS so — a missing main twin is
|
|
24898
|
+
* never served silently as if it were the asked-for quality (D549 15).
|
|
24899
|
+
*/
|
|
24900
|
+
served: CamProfileSchema.optional(),
|
|
24419
24901
|
/** Optional LAN/remote alternates for the same clip. */
|
|
24420
24902
|
playbackEndpoints: array(string()).optional(),
|
|
24421
24903
|
token: string().optional()
|
|
24422
24904
|
});
|
|
24905
|
+
/**
|
|
24906
|
+
* Why a source cannot answer right now — per SOURCE, never fleet-wide.
|
|
24907
|
+
*
|
|
24908
|
+
* `ok` is the only state whose clip list may be read as complete. The other
|
|
24909
|
+
* three exist because an empty list from a sleeping camera reads as "this
|
|
24910
|
+
* camera has no recordings", which is the defect this whole line of work is
|
|
24911
|
+
* about: a battery camera nobody woke (`sleeping`), a camera that could not be
|
|
24912
|
+
* reached at all (`unreachable`, also what a provider that THREW reports), a
|
|
24913
|
+
* camera whose SD card is not mounted (`no-storage`, measured on 640 —
|
|
24914
|
+
* `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
|
|
24915
|
+
* camera whose OWN index disagrees with its OWN calendar (`index-empty`,
|
|
24916
|
+
* measured on 618: the calendar marks 3–20 September, days 6–20 list zero
|
|
24917
|
+
* files on both streams and both filters, with 177 GB free). That last one is
|
|
24918
|
+
* a defect ON THE CAMERA, and the only honest thing a provider can do is say
|
|
24919
|
+
* which days it asked for and got nothing — "no clips" would be a lie about a
|
|
24920
|
+
* card that is full of them.
|
|
24921
|
+
*/
|
|
24922
|
+
/**
|
|
24923
|
+
* The operator's authorisation to WAKE a sleeping camera for one clip read.
|
|
24924
|
+
*
|
|
24925
|
+
* ONE value, absent by default, and it is a `force`-shaped operator signal in
|
|
24926
|
+
* exactly the sense `snapshot-wake-gate.ts` uses the word: *"`snapshot.getSnapshot`'s
|
|
24927
|
+
* `force` flag, and nothing else… a background caller must never set it…
|
|
24928
|
+
* Stale but honest beats woken"*. A scheduler, a retry, a reconcile and a
|
|
24929
|
+
* prefetch never set it; a surface sets it only behind the same confirm the
|
|
24930
|
+
* "Wake and refresh" gesture uses, and it is refused below 15 % battery
|
|
24931
|
+
* exactly as that gesture is.
|
|
24932
|
+
*
|
|
24933
|
+
* The gate is decided BEFORE `getApi()`, because on UDP the login IS the wake
|
|
24934
|
+
* (D549) — a read that opened a session and then checked would have woken the
|
|
24935
|
+
* camera to find out it was not allowed to.
|
|
24936
|
+
*
|
|
24937
|
+
* **One authorised yes is one wake.** `next-natural` — take the clip the next
|
|
24938
|
+
* time the camera is awake for its own reasons — is deliberately not a member:
|
|
24939
|
+
* it was proposed, it is free on the battery, and the operator declined it on
|
|
24940
|
+
* 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
|
|
24941
|
+
* D558 "Considered and not taken").
|
|
24942
|
+
*/
|
|
24943
|
+
var ClipWakeSchema = _enum(["authorised"]);
|
|
24944
|
+
/**
|
|
24945
|
+
* A clip's finished bytes, inline — the twin of `recordingExport.readExportBytes`.
|
|
24946
|
+
*
|
|
24947
|
+
* `bytes` is the DECODED length, so nobody infers it from the base64 length,
|
|
24948
|
+
* and `served` says which twin the caller actually got.
|
|
24949
|
+
*/
|
|
24950
|
+
var ClipBytesSchema = object({
|
|
24951
|
+
base64: string(),
|
|
24952
|
+
contentType: string(),
|
|
24953
|
+
/** Suggested filename, extension included. */
|
|
24954
|
+
name: string(),
|
|
24955
|
+
bytes: number().int().nonnegative(),
|
|
24956
|
+
/**
|
|
24957
|
+
* Which twin was actually served — the same contract
|
|
24958
|
+
* {@link ClipPlaybackSchema.served} carries, and REQUIRED here because the
|
|
24959
|
+
* export record persists it: a row read a week later must say the same thing
|
|
24960
|
+
* the panel said at the moment of the tap. A missing main twin is never
|
|
24961
|
+
* served silently as if it were the asked-for quality (D549 15).
|
|
24962
|
+
*/
|
|
24963
|
+
served: CamProfileSchema,
|
|
24964
|
+
/**
|
|
24965
|
+
* The DECODED duration of the delivered file, when the fetch measured one.
|
|
24966
|
+
*
|
|
24967
|
+
* A clip fetch verifies its own completion against the catalog row and
|
|
24968
|
+
* retries a materially short pass (D568); this is that measurement, carried
|
|
24969
|
+
* so a consumer can say the same thing rather than re-deriving it. Absent
|
|
24970
|
+
* when the producer did not measure — never zero, which would say the file
|
|
24971
|
+
* is empty.
|
|
24972
|
+
*/
|
|
24973
|
+
durationMs: number().positive().optional()
|
|
24974
|
+
});
|
|
24975
|
+
/**
|
|
24976
|
+
* Where a clip's STREAM can be dialled (D597) — the answer to
|
|
24977
|
+
* {@link videoclipsCapability.methods.dialClipStream}.
|
|
24978
|
+
*
|
|
24979
|
+
* `url` is plain `http://` on the provider's own token-gated listener, one
|
|
24980
|
+
* shot, short-lived. It is NOT the hub-proxied `/addon/<id>/clip-stream/…`
|
|
24981
|
+
* path: that plane takes only a user credential, which an addon does not
|
|
24982
|
+
* hold, and the framework offers no addon→addon byte transport — the one
|
|
24983
|
+
* precedent is the broker's own restream URLs (`getStreamWithCodec`), which
|
|
24984
|
+
* a consumer dials directly, and this is that precedent one addon over.
|
|
24985
|
+
*/
|
|
24986
|
+
/**
|
|
24987
|
+
* The audio a clip stream can be asked for, and the audio it carries.
|
|
24988
|
+
*
|
|
24989
|
+
* The camera stores AAC and no WebRTC session speaks it, so a stream that
|
|
24990
|
+
* carries sound carries it TRANSCODED, muxed as a second track beside the
|
|
24991
|
+
* video by the one ffmpeg the provider already runs (D600). `opus` is
|
|
24992
|
+
* 48 kHz mono, `pcmu` is 8 kHz µ-law — the two codecs a broker session
|
|
24993
|
+
* negotiates — and `none` is a stream asked for, or served, without sound.
|
|
24994
|
+
*/
|
|
24995
|
+
var ClipStreamAudioSchema = _enum([
|
|
24996
|
+
"opus",
|
|
24997
|
+
"pcmu",
|
|
24998
|
+
"none"
|
|
24999
|
+
]);
|
|
25000
|
+
/**
|
|
25001
|
+
* Why a stream that was ASKED for sound is not carrying it (D600).
|
|
25002
|
+
*
|
|
25003
|
+
* Only ever set beside `servedAudio: 'none'` after a non-`none` ask. Absent
|
|
25004
|
+
* means the ask was honoured, or there was no ask.
|
|
25005
|
+
*
|
|
25006
|
+
* - `not-requested` — the caller asked for `none`.
|
|
25007
|
+
* - `unsupported-ask` — the provider cannot produce that codec.
|
|
25008
|
+
*
|
|
25009
|
+
* Whether the RECORDING has sound at all is not knowable here: the camera
|
|
25010
|
+
* declares no audio track before its first frame arrives. That answer is in
|
|
25011
|
+
* the stream itself — an fMP4 with no audio track — and the broker reads it
|
|
25012
|
+
* there. A dial that says `opus` promises the codec, never the content.
|
|
25013
|
+
*/
|
|
25014
|
+
var ClipStreamAudioReasonSchema = _enum(["not-requested", "unsupported-ask"]);
|
|
25015
|
+
var ClipStreamDialSchema = object({
|
|
25016
|
+
url: string().min(1),
|
|
25017
|
+
/** After this the token is gone and the URL answers 404 `dial-expired`. */
|
|
25018
|
+
expiresAtMs: number(),
|
|
25019
|
+
/** What the stream IS — a consumer that cannot honour it must not dial. */
|
|
25020
|
+
transport: literal("forward-only"),
|
|
25021
|
+
/**
|
|
25022
|
+
* Which twin the stream ACTUALLY carries — not what was asked for.
|
|
25023
|
+
*
|
|
25024
|
+
* The stream serves every profile (D599), and a recording whose main twin
|
|
25025
|
+
* does not exist is streamed as its sub copy, which this field reports as
|
|
25026
|
+
* `low`. That is the whole reason the dial no longer refuses `high`:
|
|
25027
|
+
* stating what was served is what makes accepting honest, where a silent
|
|
25028
|
+
* substitution would be the 33x lie D576 forbids. Stated rather than
|
|
25029
|
+
* assumed, the way {@link ClipPlaybackSchema.served} is (D549 15).
|
|
25030
|
+
*/
|
|
25031
|
+
served: CamProfileSchema,
|
|
25032
|
+
/**
|
|
25033
|
+
* Which audio the stream will ACTUALLY carry — `served`'s companion for
|
|
25034
|
+
* sound, and the same reasoning (D599, D600).
|
|
25035
|
+
*
|
|
25036
|
+
* The broker asks for the codec its WebRTC session negotiated, and the
|
|
25037
|
+
* provider either muxes that codec or carries none. Stating it is what
|
|
25038
|
+
* lets the broker wire the matching sink before the first byte, and what
|
|
25039
|
+
* keeps a session from being handed a codec it cannot send — the audio
|
|
25040
|
+
* shape of the substitution `served` exists to prevent.
|
|
25041
|
+
*
|
|
25042
|
+
* It is a promise about the CODEC, not about the content: a recording with
|
|
25043
|
+
* no microphone track answers `opus` here and then arrives with no audio
|
|
25044
|
+
* track at all. That second answer is in the stream, and the broker reads
|
|
25045
|
+
* it from the `moov`.
|
|
25046
|
+
*/
|
|
25047
|
+
servedAudio: ClipStreamAudioSchema,
|
|
25048
|
+
/** Why `servedAudio` is `none` although sound was asked for. */
|
|
25049
|
+
audioReason: ClipStreamAudioReasonSchema.optional()
|
|
25050
|
+
});
|
|
25051
|
+
var ClipSourceAvailabilitySchema = object({
|
|
25052
|
+
state: _enum([
|
|
25053
|
+
"ok",
|
|
25054
|
+
"sleeping",
|
|
25055
|
+
"unreachable",
|
|
25056
|
+
"no-storage",
|
|
25057
|
+
"index-empty"
|
|
25058
|
+
]),
|
|
25059
|
+
/** Free text, shown verbatim. Names the camera's own refusal when there is one. */
|
|
25060
|
+
reason: string().optional(),
|
|
25061
|
+
/** When this source's catalog was last CONFIRMED. A cached list is never
|
|
25062
|
+
* drawn as current: the surface shows the age whenever it is older than the
|
|
25063
|
+
* refresh interval. */
|
|
25064
|
+
catalogAsOf: number().optional()
|
|
25065
|
+
});
|
|
25066
|
+
/**
|
|
25067
|
+
* One SOURCE of clips for a camera — a row of the picker, and the namespace
|
|
25068
|
+
* every clip id from it is prefixed with (`analytics`,
|
|
25069
|
+
* `native:reolink:onboard`, `hksv`, …).
|
|
25070
|
+
*
|
|
25071
|
+
* A provider lists the sources IT serves for that device, and answers for each
|
|
25072
|
+
* of them whether it can answer at all. A provider with nothing to offer on a
|
|
25073
|
+
* camera returns `[]` — it is not that camera's business.
|
|
25074
|
+
*/
|
|
25075
|
+
var ClipSourceSchema = object({
|
|
25076
|
+
/** The value this source stamps on {@link ClipSchema.source}, and the prefix
|
|
25077
|
+
* of every clip id it mints. `getClipPlayback` routes on it. */
|
|
25078
|
+
source: string(),
|
|
25079
|
+
/** Operator-facing name of the source ("CamStack recordings", "SD card"). */
|
|
25080
|
+
label: string(),
|
|
25081
|
+
/**
|
|
25082
|
+
* The addon that SERVES this row.
|
|
25083
|
+
*
|
|
25084
|
+
* A surface cannot otherwise resolve a source to who answers it, and the
|
|
25085
|
+
* alternative — a `native:reolink:* → provider-reolink` table inside the
|
|
25086
|
+
* widget — is a second authority on provider identity living in the one
|
|
25087
|
+
* package with no business knowing it, wrong the day a third source appears
|
|
25088
|
+
* (D557). One addon may serve SEVERAL sources, which is why the per-device
|
|
25089
|
+
* switch is keyed by SOURCE and not by this (D555) — `addon-provider-reolink`
|
|
25090
|
+
* served a hub child two of them until the two views were measured to be one
|
|
25091
|
+
* store read twice (D555) and then read ONE way (D565).
|
|
25092
|
+
*
|
|
25093
|
+
* Optional for version skew only. The collection dispatcher stamps it from
|
|
25094
|
+
* the registry, so a row that travelled through the fan-out carries the
|
|
25095
|
+
* authoritative id whatever the provider filled in.
|
|
25096
|
+
*/
|
|
25097
|
+
addonId: string().optional(),
|
|
25098
|
+
availability: ClipSourceAvailabilitySchema
|
|
25099
|
+
});
|
|
24423
25100
|
DeviceType.Camera, method(object({
|
|
24424
25101
|
deviceId: number(),
|
|
24425
25102
|
since: number(),
|
|
24426
25103
|
until: number(),
|
|
24427
|
-
limit: number().int().positive().optional()
|
|
25104
|
+
limit: number().int().positive().optional(),
|
|
25105
|
+
/**
|
|
25106
|
+
* WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
|
|
25107
|
+
* carries, never a source id and never a list. **Required** (D554 amended).
|
|
25108
|
+
*
|
|
25109
|
+
* A provider the device is not bound to is refused by name rather than
|
|
25110
|
+
* answered by another one (D552's `rejectUnresolvedAddonPin` rule).
|
|
25111
|
+
*
|
|
25112
|
+
* It was optional, documented as "absent means the device's BOUND
|
|
25113
|
+
* provider, which is CamStack on every camera". No code implemented
|
|
25114
|
+
* that. Measured on the live hub 2026-09-20 — device 592, bound to
|
|
25115
|
+
* `recorder` AND `provider-reolink` — a bare call with `limit: 3`
|
|
25116
|
+
* answered SIX rows, three from each source, merged newest-first:
|
|
25117
|
+
* `device-collection-dispatch.ts` simply left the fan-out un-narrowed,
|
|
25118
|
+
* so absence bought the union this method exists not to be, and
|
|
25119
|
+
* `limit` meant `limit × sources`.
|
|
25120
|
+
*
|
|
25121
|
+
* There is nothing to restore the default to. `getBindings` answers a
|
|
25122
|
+
* collection cap with a DERIVED, PLURAL set (D554 amended, step 0);
|
|
25123
|
+
* `setWrapperActive` — the singleton authority that could name one —
|
|
25124
|
+
* throws for a collection cap by design. Naming CamStack here instead
|
|
25125
|
+
* would privilege one addon by id inside a surface whose premise is
|
|
25126
|
+
* that sources are peers, and would be wrong on the first camera with
|
|
25127
|
+
* no recorder binding. So absence is REFUSED, in the schema, where the
|
|
25128
|
+
* generated types make it unomittable rather than merely discouraged.
|
|
25129
|
+
*
|
|
25130
|
+
* The default belongs to the SURFACE, which has the `listSources` rows
|
|
25131
|
+
* and can say which one it picked (D569 § 1–3; the viewer already
|
|
25132
|
+
* always sends this).
|
|
25133
|
+
*
|
|
25134
|
+
* It replaced `sources?: string[]`, a VIEW filter over source ids that
|
|
25135
|
+
* assumed the answer was a fan-out over everything a camera has. The
|
|
25136
|
+
* operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
|
|
25137
|
+
* solo"* — so the list is asked of one provider at a time and there is
|
|
25138
|
+
* nothing to filter out of it.
|
|
25139
|
+
*/
|
|
25140
|
+
provider: string().min(1)
|
|
24428
25141
|
}), array(ClipSchema).readonly(), {
|
|
24429
25142
|
kind: "query",
|
|
24430
|
-
auth: "
|
|
25143
|
+
auth: "protected"
|
|
25144
|
+
}), method(object({ deviceId: number() }), array(ClipSourceSchema).readonly(), {
|
|
25145
|
+
kind: "query",
|
|
25146
|
+
auth: "protected"
|
|
24431
25147
|
}), method(object({
|
|
24432
25148
|
deviceId: number(),
|
|
24433
|
-
clipId: string()
|
|
25149
|
+
clipId: string(),
|
|
25150
|
+
/**
|
|
25151
|
+
* Which twin to serve, on the ONE quality scale the system already has
|
|
25152
|
+
* (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
|
|
25153
|
+
* twin; both ids are already on the row so this never re-searches the
|
|
25154
|
+
* camera. Absent means the provider's own default (the sub file, which
|
|
25155
|
+
* every source is measured to hold).
|
|
25156
|
+
*
|
|
25157
|
+
* `auto` is deliberately NOT accepted here: a stored file has no
|
|
25158
|
+
* broker session, so the adaptive tier cannot be resolved for it. The
|
|
25159
|
+
* viewer resolves `auto` to a profile the same way live does, before
|
|
25160
|
+
* it calls (D549 15).
|
|
25161
|
+
*/
|
|
25162
|
+
profile: CamProfileSchema.optional()
|
|
24434
25163
|
}), ClipPlaybackSchema, {
|
|
24435
25164
|
kind: "query",
|
|
24436
|
-
auth: "
|
|
25165
|
+
auth: "protected"
|
|
25166
|
+
}), optionalMethod(object({
|
|
25167
|
+
deviceId: number(),
|
|
25168
|
+
clipId: string().min(1),
|
|
25169
|
+
/**
|
|
25170
|
+
* WHICH provider holds the bytes — the `addonId` a
|
|
25171
|
+
* {@link ClipSourceSchema} row carries. **Required**, for the reason
|
|
25172
|
+
* `listClips` states one method above and for a second one this method
|
|
25173
|
+
* learned the hard way.
|
|
25174
|
+
*
|
|
25175
|
+
* The caller is the stream broker, and the broker lives in
|
|
25176
|
+
* `addon-pipeline` — the same addon that registers the RECORDER's
|
|
25177
|
+
* videoclips provider. With no provider named, the in-process
|
|
25178
|
+
* resolution handed it its own sibling, which implements this method
|
|
25179
|
+
* solely to explain that an analytics visit has no file behind it. So
|
|
25180
|
+
* every onboard clip answered with that sentence, a flash of video and
|
|
25181
|
+
* nothing more, while the SAME call from outside the addon reached the
|
|
25182
|
+
* right provider and returned the bytes (measured on 3628,
|
|
25183
|
+
* 2026-09-21).
|
|
25184
|
+
*/
|
|
25185
|
+
provider: string().min(1),
|
|
25186
|
+
/**
|
|
25187
|
+
* Which twin to fetch, on the mapping D549 15 already fixed:
|
|
25188
|
+
* `low | mid` → the sub file, `high` → the main twin. `auto` is not
|
|
25189
|
+
* accepted here for the same reason it is not accepted by
|
|
25190
|
+
* `getClipPlayback` — a stored file has no broker session, so the
|
|
25191
|
+
* adaptive tier cannot be resolved for it.
|
|
25192
|
+
*/
|
|
25193
|
+
profile: CamProfileSchema.optional(),
|
|
25194
|
+
/**
|
|
25195
|
+
* The CALLER's byte bound, so an over-size clip is refused before it is
|
|
25196
|
+
* read and encoded rather than after. Capped by
|
|
25197
|
+
* {@link VIDEOCLIPS_MAX_READ_BYTES} whatever is passed; absent means
|
|
25198
|
+
* that ceiling.
|
|
25199
|
+
*/
|
|
25200
|
+
maxBytes: number().int().positive().optional(),
|
|
25201
|
+
/**
|
|
25202
|
+
* The operator's authorisation to wake a sleeping camera for this
|
|
25203
|
+
* read. Absent — the default — means a sleeping standalone battery
|
|
25204
|
+
* camera is REFUSED by name, before any session is opened.
|
|
25205
|
+
*/
|
|
25206
|
+
wake: ClipWakeSchema.optional()
|
|
25207
|
+
}), ClipBytesSchema, {
|
|
25208
|
+
kind: "query",
|
|
25209
|
+
auth: "protected"
|
|
25210
|
+
}), optionalMethod(object({
|
|
25211
|
+
deviceId: number(),
|
|
25212
|
+
clipId: string().min(1),
|
|
25213
|
+
/** WHICH provider holds the camera — see `readClipBytes.provider`. */
|
|
25214
|
+
provider: string().min(1),
|
|
25215
|
+
/** Which twin — `high` → the main file, everything else the sub (D599). */
|
|
25216
|
+
profile: CamProfileSchema.optional(),
|
|
25217
|
+
/**
|
|
25218
|
+
* Which audio codec the caller's session negotiated, so the provider
|
|
25219
|
+
* can mux it (D600). The ask travels on the DIAL because the mux is
|
|
25220
|
+
* spawned once, before the first byte, and cannot gain a track later.
|
|
25221
|
+
*
|
|
25222
|
+
* ABSENT IS `none`, never a guess: a caller that does not state an
|
|
25223
|
+
* ask is served the video-only stream that shipped first, so a broker
|
|
25224
|
+
* older than this field keeps working unchanged (D382 — an unstated
|
|
25225
|
+
* ask is zero).
|
|
25226
|
+
*/
|
|
25227
|
+
audio: ClipStreamAudioSchema.optional()
|
|
25228
|
+
}), ClipStreamDialSchema, {
|
|
25229
|
+
kind: "query",
|
|
25230
|
+
auth: "protected"
|
|
24437
25231
|
});
|
|
24438
25232
|
/**
|
|
24439
25233
|
* Optional client-side hints sent at session creation to help the provider
|
|
@@ -24505,6 +25299,104 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
24505
25299
|
/** Bitrate in kbps (null for adaptive). */
|
|
24506
25300
|
bitrateKbps: number().nullable()
|
|
24507
25301
|
});
|
|
25302
|
+
/**
|
|
25303
|
+
* The last move the adaptive ladder made on this session, and the numbers it
|
|
25304
|
+
* rested on. Mirrors `LadderDecision` in the stream-broker's
|
|
25305
|
+
* `adaptive-controller.ts` — the log line's evidence, made readable.
|
|
25306
|
+
*
|
|
25307
|
+
* Nullability is load-bearing: `lossRatio: null` means the loss tracker had
|
|
25308
|
+
* no window to report, which is NOT a link with zero loss.
|
|
25309
|
+
*/
|
|
25310
|
+
var WebrtcLadderDecisionSchema = object({
|
|
25311
|
+
/** `loss` — RTCP / client-reported packet loss; `stall` — the picture
|
|
25312
|
+
* stopped moving; `healthy` — a promotion. */
|
|
25313
|
+
cause: _enum([
|
|
25314
|
+
"loss",
|
|
25315
|
+
"stall",
|
|
25316
|
+
"healthy"
|
|
25317
|
+
]),
|
|
25318
|
+
/** The rung the ladder moved TO. Null for a move that changed no rung
|
|
25319
|
+
* (an MTU reduction, a forced transcode-down at the bottom). */
|
|
25320
|
+
tier: CamProfileSchema.nullable(),
|
|
25321
|
+
/** Whether that move forced the egress transcode-down rung. */
|
|
25322
|
+
forcedTranscodeDown: boolean(),
|
|
25323
|
+
/** Loss ratio over the tracker's window at the moment of the decision. */
|
|
25324
|
+
lossRatio: number().nullable(),
|
|
25325
|
+
/** The client's reported downlink at that moment, in kbps. */
|
|
25326
|
+
clientKbps: number().nullable(),
|
|
25327
|
+
/** How long the stall detector had been seeing nothing arrive. */
|
|
25328
|
+
stalledForMs: number().nullable(),
|
|
25329
|
+
/** Whether the broker WAS sending while that was measured — the half that
|
|
25330
|
+
* separates "the camera stopped" from "the link stopped". */
|
|
25331
|
+
serverSending: boolean().nullable(),
|
|
25332
|
+
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
25333
|
+
* must not read like one from this second. */
|
|
25334
|
+
at: number()
|
|
25335
|
+
});
|
|
25336
|
+
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
25337
|
+
var WebrtcTierFailureSchema = object({
|
|
25338
|
+
tier: CamProfileSchema,
|
|
25339
|
+
failures: number().int().nonnegative()
|
|
25340
|
+
});
|
|
25341
|
+
/**
|
|
25342
|
+
* What the SERVER decided for one WebRTC session — the half the client cannot
|
|
25343
|
+
* measure. See `getSessionDebug`.
|
|
25344
|
+
*/
|
|
25345
|
+
var WebrtcSessionDebugSchema = object({
|
|
25346
|
+
sessionId: string(),
|
|
25347
|
+
/** Whether the session still exists here. Everything below is null when
|
|
25348
|
+
* it does not — a dead session has no measurements, not zero ones. */
|
|
25349
|
+
alive: boolean(),
|
|
25350
|
+
/** Epoch ms this snapshot was taken, so the overlay can show its age. */
|
|
25351
|
+
observedAt: number(),
|
|
25352
|
+
/** The broker actually feeding this session (`<deviceId>/<camStreamId>`). */
|
|
25353
|
+
brokerId: string().nullable(),
|
|
25354
|
+
camStreamId: string().nullable(),
|
|
25355
|
+
/** `derived` = this hub BUILT the rung being served (a transcode/scale of
|
|
25356
|
+
* another stream); `native` = the camera's own encode. */
|
|
25357
|
+
sourceKind: _enum([
|
|
25358
|
+
"native",
|
|
25359
|
+
"derived",
|
|
25360
|
+
"unknown"
|
|
25361
|
+
]),
|
|
25362
|
+
/** Whether the ladder is running at all (an `adaptive` target). */
|
|
25363
|
+
adaptive: boolean(),
|
|
25364
|
+
/** The rung `auto` ACTUALLY resolved to right now. Null when the broker
|
|
25365
|
+
* is bound to no profile slot (a manually-activated stream). */
|
|
25366
|
+
servedTier: CamProfileSchema.nullable(),
|
|
25367
|
+
/** Which node ingests the source. Null when unresolved. */
|
|
25368
|
+
ingestNodeId: string().nullable(),
|
|
25369
|
+
/** Whether that node is a remote one (the media crosses a node boundary
|
|
25370
|
+
* before it reaches this session). Null when unresolved. */
|
|
25371
|
+
ingestIsRemote: boolean().nullable(),
|
|
25372
|
+
/** Codec off the source, and the one negotiated with this client. */
|
|
25373
|
+
sourceCodec: string().nullable(),
|
|
25374
|
+
negotiatedCodec: string().nullable(),
|
|
25375
|
+
/** Whether the EGRESS leg re-encodes for this client or forwards the
|
|
25376
|
+
* source bytes untouched. */
|
|
25377
|
+
egressLeg: _enum([
|
|
25378
|
+
"copy",
|
|
25379
|
+
"transcode",
|
|
25380
|
+
"unknown"
|
|
25381
|
+
]),
|
|
25382
|
+
/** What the broker itself reports about the rung it is serving. */
|
|
25383
|
+
brokerStatus: string().nullable(),
|
|
25384
|
+
brokerCodec: string().nullable(),
|
|
25385
|
+
brokerBitrateKbps: number().nullable(),
|
|
25386
|
+
brokerInputFps: number().nullable(),
|
|
25387
|
+
brokerUptimeMs: number().nullable(),
|
|
25388
|
+
/** RTP packets this session has sent — the server's own "am I sending?". */
|
|
25389
|
+
rtpPacketsSent: number().nullable(),
|
|
25390
|
+
/** D42: the track may be carrying the recorded feeder, not live. The ladder
|
|
25391
|
+
* is suspended there, so a frozen picture in `recorded` mode means
|
|
25392
|
+
* something entirely different. */
|
|
25393
|
+
trackMode: _enum(["live", "recorded"]).nullable(),
|
|
25394
|
+
lastDecision: WebrtcLadderDecisionSchema.nullable(),
|
|
25395
|
+
tierFailures: array(WebrtcTierFailureSchema),
|
|
25396
|
+
/** Successful promotions in this viewer session (drives the progressive
|
|
25397
|
+
* healthy timer). Null when no ladder is running. */
|
|
25398
|
+
upgradeCount: number().nullable()
|
|
25399
|
+
});
|
|
24508
25400
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
24509
25401
|
deviceId: number().int().nonnegative(),
|
|
24510
25402
|
target: WebrtcStreamTargetSchema,
|
|
@@ -24669,7 +25561,10 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
|
|
|
24669
25561
|
* the same answer as "it ended": either way nothing is holding it up.
|
|
24670
25562
|
*/
|
|
24671
25563
|
alive: boolean()
|
|
24672
|
-
}))
|
|
25564
|
+
})), method(object({
|
|
25565
|
+
deviceId: number().int().nonnegative(),
|
|
25566
|
+
sessionId: string()
|
|
25567
|
+
}), WebrtcSessionDebugSchema, { kind: "query" });
|
|
24673
25568
|
object({
|
|
24674
25569
|
/** All accessory children of the parent. */
|
|
24675
25570
|
childDeviceIds: array(number()).readonly(),
|
|
@@ -26495,6 +27390,346 @@ var dayNightCapability = {
|
|
|
26495
27390
|
volatileStateFields: ["lastFetchedAt"]
|
|
26496
27391
|
};
|
|
26497
27392
|
/**
|
|
27393
|
+
* Vendor-neutral **onboard** recording + storage cap — what the CAMERA
|
|
27394
|
+
* writes to the CAMERA's own card, on the camera's own schedule.
|
|
27395
|
+
*
|
|
27396
|
+
* This is NOT `recording.cap.ts`. That one is CamStack's recorder: our
|
|
27397
|
+
* footage ledger, our storage locations, our retention. This one has a
|
|
27398
|
+
* different authority — the camera's firmware — and per D62 it stores
|
|
27399
|
+
* nothing of its own. Every value here is read from the camera and every
|
|
27400
|
+
* write goes back to the camera; there is no CamStack-side mirror that
|
|
27401
|
+
* could disagree with the device.
|
|
27402
|
+
*
|
|
27403
|
+
* ## One shape, two firmwares
|
|
27404
|
+
*
|
|
27405
|
+
* Measured 2026-09-22 against the live fleet:
|
|
27406
|
+
*
|
|
27407
|
+
* | fact | Hikvision (ISAPI) | Reolink (Baichuan) |
|
|
27408
|
+
* | --- | --- | --- |
|
|
27409
|
+
* | storage | `ContentMgmt/Storage` `<hdd>` rows: status, capacity, freeSpace (MB) | `getHddInfoList` (cmd 102): `mount`, `format`, `capacity` GB + `capacityM` MB remainder |
|
|
27410
|
+
* | tracks | several (101 **and** 103 on both 1436 and 3833), each with its own schedule | one per channel |
|
|
27411
|
+
* | schedule | per track, 7 `ScheduleAction` blocks: DayOfWeek + TimeOfDay range + ONE `ActionRecordingMode` | per trigger type, a 168-char weekly HOUR mask |
|
|
27412
|
+
* | triggers | `CMR`, `MOTION` | `Normal`, `MD`, `people`, `vehicle`, `dog_cat`, `crossline`, `intrude`, `loitering` |
|
|
27413
|
+
* | pre-record | `PreRecordTimeSeconds` | `preRecordTime` |
|
|
27414
|
+
* | post-record | `PostRecordTimeSeconds` | `recordDelayTime` |
|
|
27415
|
+
* | overwrite | per track `LoopEnable` | `cycle`, with `cycleList` enumerating the accepted values |
|
|
27416
|
+
* | segment length | not exposed on V5.7.1 | `packageTime` (minutes) |
|
|
27417
|
+
*
|
|
27418
|
+
* The two schedule models look different and are the same thing in
|
|
27419
|
+
* different coordinates: both answer "for this trigger, during which
|
|
27420
|
+
* weekly windows does the camera record". {@link RecordWindow} is that
|
|
27421
|
+
* question in one shape — Hikvision's ranges map straight onto it,
|
|
27422
|
+
* Reolink's mask expands into hour-aligned windows.
|
|
27423
|
+
*
|
|
27424
|
+
* ## Union, not intersection
|
|
27425
|
+
*
|
|
27426
|
+
* **The same fields exist on every camera.** What differs per device is
|
|
27427
|
+
* which VALUES that device accepts, and that is what {@link
|
|
27428
|
+
* RecordingOnboardOptions} reports — a `{ readable, writable, reason }`
|
|
27429
|
+
* per field plus the schedule's own limits. A control a camera cannot
|
|
27430
|
+
* honour is rendered DISABLED WITH ITS REASON, never missing and never
|
|
27431
|
+
* dead: disabled must not look like broken.
|
|
27432
|
+
*
|
|
27433
|
+
* ## Refusal by name
|
|
27434
|
+
*
|
|
27435
|
+
* A write a camera cannot honour is refused with a sentence the operator
|
|
27436
|
+
* can read — never accepted and dropped. Both providers refuse through
|
|
27437
|
+
* {@link describeOnboardRefusal}, so the vocabulary is one function and
|
|
27438
|
+
* one test, not two hand-written vendor opinions.
|
|
27439
|
+
*
|
|
27440
|
+
* Follows the D14 `deviceConfig` archetype (see `stream-params.cap.ts`):
|
|
27441
|
+
* `getOptions` advertises per-camera availability, `getStatus` (auto-
|
|
27442
|
+
* injected from `status`) reports the live values, and a single
|
|
27443
|
+
* `setSettings` mutation applies a partial change. No hand-written
|
|
27444
|
+
* settings-contribution methods.
|
|
27445
|
+
*/
|
|
27446
|
+
/**
|
|
27447
|
+
* What makes the camera start recording during a window.
|
|
27448
|
+
*
|
|
27449
|
+
* The union of both vendors' vocabularies. `continuous` is Hikvision's
|
|
27450
|
+
* `CMR` and Reolink's `Normal`; `motion` is `MOTION` / `MD`. The
|
|
27451
|
+
* object-class triggers are Reolink-only today and the smart-event ones
|
|
27452
|
+
* (`lineCrossing`, `intrusion`, `loitering`) are Reolink-only on the
|
|
27453
|
+
* firmwares measured — a camera that cannot record on a trigger simply
|
|
27454
|
+
* does not list it in `options.schedule.triggers`, and a window naming
|
|
27455
|
+
* it is REFUSED, not dropped.
|
|
27456
|
+
*/
|
|
27457
|
+
var RecordTriggerSchema = _enum([
|
|
27458
|
+
"continuous",
|
|
27459
|
+
"motion",
|
|
27460
|
+
"person",
|
|
27461
|
+
"vehicle",
|
|
27462
|
+
"animal",
|
|
27463
|
+
"lineCrossing",
|
|
27464
|
+
"intrusion",
|
|
27465
|
+
"loitering",
|
|
27466
|
+
"alarmInput"
|
|
27467
|
+
]);
|
|
27468
|
+
/**
|
|
27469
|
+
* One weekly recording window: "on `day`, from `startMinute` to
|
|
27470
|
+
* `endMinute`, record on `trigger`".
|
|
27471
|
+
*
|
|
27472
|
+
* `day` is 0 = Monday … 6 = Sunday (ISO order, which is also the order
|
|
27473
|
+
* both firmwares enumerate). Minutes are local camera time since
|
|
27474
|
+
* midnight; `endMinute` may be 1440, meaning end of day — that is
|
|
27475
|
+
* Hikvision's literal `24:00` and Reolink's 24th mask slot, and
|
|
27476
|
+
* collapsing it to 0 would turn a whole-day window into an empty one.
|
|
27477
|
+
*/
|
|
27478
|
+
var RecordWindowSchema = object({
|
|
27479
|
+
trigger: RecordTriggerSchema,
|
|
27480
|
+
day: number().int().min(0).max(6),
|
|
27481
|
+
startMinute: number().int().min(0).max(1439),
|
|
27482
|
+
endMinute: number().int().min(1).max(1440)
|
|
27483
|
+
});
|
|
27484
|
+
/** Status of one physical volume, as the camera itself describes it. */
|
|
27485
|
+
var OnboardStorageVolumeSchema = object({
|
|
27486
|
+
/** The camera's own id for the volume (`hdd/id`, Reolink `HddInfo/number`). */
|
|
27487
|
+
id: string(),
|
|
27488
|
+
/** The camera's own name for it, when it gives one (`hddName`). */
|
|
27489
|
+
label: string().optional(),
|
|
27490
|
+
status: _enum([
|
|
27491
|
+
"ok",
|
|
27492
|
+
"unformatted",
|
|
27493
|
+
"error",
|
|
27494
|
+
"offline",
|
|
27495
|
+
"unknown"
|
|
27496
|
+
]),
|
|
27497
|
+
/**
|
|
27498
|
+
* Total size in MB, or **null when the camera did not say**.
|
|
27499
|
+
*
|
|
27500
|
+
* Never 0 for an unreadable value: a measurement that failed is not a
|
|
27501
|
+
* measurement (D393), and a card whose size is unknown must not be
|
|
27502
|
+
* rendered as a card of size zero.
|
|
27503
|
+
*/
|
|
27504
|
+
capacityMb: number().nullable(),
|
|
27505
|
+
/**
|
|
27506
|
+
* Free space in MB, or null when unknown.
|
|
27507
|
+
*
|
|
27508
|
+
* **Not a proxy for "has footage".** Measured 2026-09-22: 1436 and
|
|
27509
|
+
* 1439 both report exactly 11776 MB free — the fixed reserve a looping
|
|
27510
|
+
* card converges on once it has wrapped. At loop steady state the
|
|
27511
|
+
* number is identical whether the camera recorded yesterday or stopped
|
|
27512
|
+
* a month ago.
|
|
27513
|
+
*/
|
|
27514
|
+
freeMb: number().nullable(),
|
|
27515
|
+
/** True when the camera reports the volume writable (`property` RW). */
|
|
27516
|
+
writable: boolean().optional()
|
|
27517
|
+
});
|
|
27518
|
+
/**
|
|
27519
|
+
* What the camera is doing with its own storage, right now.
|
|
27520
|
+
*
|
|
27521
|
+
* Every scalar is nullable and **null means the camera did not answer**,
|
|
27522
|
+
* never a default. A form that seeds `0` from an unanswered read invites
|
|
27523
|
+
* the operator to save that 0 back onto the camera.
|
|
27524
|
+
*/
|
|
27525
|
+
var RecordingOnboardStatusSchema = object({
|
|
27526
|
+
storage: discriminatedUnion("kind", [
|
|
27527
|
+
object({
|
|
27528
|
+
kind: literal("present"),
|
|
27529
|
+
volumes: array(OnboardStorageVolumeSchema)
|
|
27530
|
+
}),
|
|
27531
|
+
object({
|
|
27532
|
+
kind: literal("absent"),
|
|
27533
|
+
reason: string()
|
|
27534
|
+
}),
|
|
27535
|
+
object({
|
|
27536
|
+
kind: literal("unknown"),
|
|
27537
|
+
reason: string()
|
|
27538
|
+
})
|
|
27539
|
+
]),
|
|
27540
|
+
tracks: array(object({
|
|
27541
|
+
id: string(),
|
|
27542
|
+
enabled: boolean(),
|
|
27543
|
+
isVideo: boolean(),
|
|
27544
|
+
/** From the camera's own track description. Null when it does not say. */
|
|
27545
|
+
codec: string().nullable(),
|
|
27546
|
+
resolution: string().nullable(),
|
|
27547
|
+
/** Per-track overwrite flag, where the firmware keeps it per track. */
|
|
27548
|
+
overwriteWhenFull: boolean().nullable()
|
|
27549
|
+
})),
|
|
27550
|
+
/**
|
|
27551
|
+
* The track the write path targets — the enabled VIDEO one. Null when
|
|
27552
|
+
* no track could be identified, which is itself a refusal reason.
|
|
27553
|
+
*/
|
|
27554
|
+
primaryTrackId: string().nullable(),
|
|
27555
|
+
/** Master "record to the card at all" switch. */
|
|
27556
|
+
enabled: boolean().nullable(),
|
|
27557
|
+
overwriteWhenFull: boolean().nullable(),
|
|
27558
|
+
preRecordSec: number().nullable(),
|
|
27559
|
+
postRecordSec: number().nullable(),
|
|
27560
|
+
/** Length of one recorded file, in minutes. */
|
|
27561
|
+
segmentMinutes: number().nullable(),
|
|
27562
|
+
/** The primary track's weekly windows, flattened. */
|
|
27563
|
+
windows: array(RecordWindowSchema),
|
|
27564
|
+
/**
|
|
27565
|
+
* How many windows the camera described that CamStack could NOT read —
|
|
27566
|
+
* an unrecognised trigger, an unparseable clock, a weekday it does not
|
|
27567
|
+
* name.
|
|
27568
|
+
*
|
|
27569
|
+
* A dropped window is work the reader threw away, and a schedule that
|
|
27570
|
+
* silently shows fewer rows than the camera holds is how an operator
|
|
27571
|
+
* saves back a schedule shorter than the one they were looking at
|
|
27572
|
+
* (D391). Non-zero means the window list is INCOMPLETE and a write
|
|
27573
|
+
* that replaces it would delete what was not shown — which is why a
|
|
27574
|
+
* provider reporting a non-zero count also reports the schedule as not
|
|
27575
|
+
* writable.
|
|
27576
|
+
*/
|
|
27577
|
+
unreadableWindows: number(),
|
|
27578
|
+
/**
|
|
27579
|
+
* The camera is scheduled to record and has NO usable storage.
|
|
27580
|
+
*
|
|
27581
|
+
* A first-class fact because it is the fleet's most common silent
|
|
27582
|
+
* defect: measured 2026-09-22, 1441 and 3831 are both motion-recording
|
|
27583
|
+
* to a card that is not there. Neither the schedule nor the storage
|
|
27584
|
+
* read says anything wrong on its own; only the pair does.
|
|
27585
|
+
*/
|
|
27586
|
+
recordingToNowhere: boolean(),
|
|
27587
|
+
lastFetchedAt: number()
|
|
27588
|
+
});
|
|
27589
|
+
/** Numeric range descriptor — `{ min, max, step }` per the getOptions convention. */
|
|
27590
|
+
var RangeSchema = object({
|
|
27591
|
+
min: number(),
|
|
27592
|
+
max: number(),
|
|
27593
|
+
step: number()
|
|
27594
|
+
});
|
|
27595
|
+
/**
|
|
27596
|
+
* The values a camera actually takes for a numeric field, when they are a SET
|
|
27597
|
+
* rather than a range.
|
|
27598
|
+
*
|
|
27599
|
+
* `{min,max,step}` cannot say what these two firmwares do. Measured on 1436
|
|
27600
|
+
* (I91DN) on 2026-09-22 by writing each value and reading it back:
|
|
27601
|
+
*
|
|
27602
|
+
* - pre-record: `0, 5, 10, 15, 20, 25, 30` and `2147483647` (INT32_MAX, the
|
|
27603
|
+
* camera's "no limit" — `-1` and `4294967295` both land on it);
|
|
27604
|
+
* - post-record: `5, 10, 30, 60, 120, 300, 600`.
|
|
27605
|
+
*
|
|
27606
|
+
* Neither is expressible as a step: the first has a sentinel two billion away
|
|
27607
|
+
* from its neighbours, the second doubles and then jumps. A range that tried
|
|
27608
|
+
* would forbid values the camera takes AND permit values it silently replaces
|
|
27609
|
+
* with 5 — wrong in both directions at once.
|
|
27610
|
+
*
|
|
27611
|
+
* `sentinel` names the member that is not a duration, so a surface can render
|
|
27612
|
+
* "no limit" instead of `2147483647` seconds.
|
|
27613
|
+
*/
|
|
27614
|
+
var AllowedValuesSchema = object({
|
|
27615
|
+
values: array(number()).min(1),
|
|
27616
|
+
sentinel: object({
|
|
27617
|
+
value: number(),
|
|
27618
|
+
meaning: _enum(["no-limit", "disabled"])
|
|
27619
|
+
}).optional()
|
|
27620
|
+
});
|
|
27621
|
+
/**
|
|
27622
|
+
* Per-field availability on ONE camera.
|
|
27623
|
+
*
|
|
27624
|
+
* The field exists on every camera — this says whether this one can be
|
|
27625
|
+
* read and whether it can be written, and `reason` says why not when
|
|
27626
|
+
* either is false. The UI renders the control DISABLED with the reason
|
|
27627
|
+
* rather than hiding it, so a limitation is legible instead of looking
|
|
27628
|
+
* like a missing feature.
|
|
27629
|
+
*/
|
|
27630
|
+
var OnboardFieldSupportSchema = object({
|
|
27631
|
+
readable: boolean(),
|
|
27632
|
+
writable: boolean(),
|
|
27633
|
+
/** Required whenever `readable` or `writable` is false. */
|
|
27634
|
+
reason: string().optional()
|
|
27635
|
+
});
|
|
27636
|
+
/** What this camera's schedule model can express. */
|
|
27637
|
+
var OnboardScheduleSupportSchema = object({
|
|
27638
|
+
support: OnboardFieldSupportSchema,
|
|
27639
|
+
/**
|
|
27640
|
+
* The smallest time step the camera can express, in minutes.
|
|
27641
|
+
*
|
|
27642
|
+
* Hikvision takes arbitrary minutes (`00:05:00`–`23:57:00` observed on
|
|
27643
|
+
* 1436's track 103). Reolink's schedule is a 7×24 HOUR mask, so 60. A
|
|
27644
|
+
* window whose edges are not a multiple of this is REFUSED rather than
|
|
27645
|
+
* quietly rounded — rounding is how an operator's 06:30 becomes 06:00
|
|
27646
|
+
* and nothing says so.
|
|
27647
|
+
*/
|
|
27648
|
+
granularityMinutes: number(),
|
|
27649
|
+
/** Triggers this camera can record on. A window naming another is refused. */
|
|
27650
|
+
triggers: array(RecordTriggerSchema),
|
|
27651
|
+
/**
|
|
27652
|
+
* False when the camera stores ONE trigger per time range, so two
|
|
27653
|
+
* windows overlapping on the same day cannot carry different triggers.
|
|
27654
|
+
* True on Reolink, whose mask is per-trigger and independent.
|
|
27655
|
+
*/
|
|
27656
|
+
supportsOverlappingTriggers: boolean()
|
|
27657
|
+
});
|
|
27658
|
+
var RecordingOnboardOptionsSchema = object({
|
|
27659
|
+
enabled: OnboardFieldSupportSchema,
|
|
27660
|
+
overwriteWhenFull: OnboardFieldSupportSchema,
|
|
27661
|
+
preRecordSec: OnboardFieldSupportSchema,
|
|
27662
|
+
preRecordSecRange: RangeSchema.optional(),
|
|
27663
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
27664
|
+
preRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
27665
|
+
postRecordSec: OnboardFieldSupportSchema,
|
|
27666
|
+
postRecordSecRange: RangeSchema.optional(),
|
|
27667
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
27668
|
+
postRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
27669
|
+
segmentMinutes: OnboardFieldSupportSchema,
|
|
27670
|
+
segmentMinutesRange: RangeSchema.optional(),
|
|
27671
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
27672
|
+
segmentMinutesAllowed: AllowedValuesSchema.optional(),
|
|
27673
|
+
schedule: OnboardScheduleSupportSchema
|
|
27674
|
+
});
|
|
27675
|
+
/**
|
|
27676
|
+
* A partial change. Every field optional.
|
|
27677
|
+
*
|
|
27678
|
+
* Unlike the other `deviceConfig` caps, a provider here does **NOT**
|
|
27679
|
+
* silently ignore a field it cannot support — it refuses, by name,
|
|
27680
|
+
* through {@link describeOnboardRefusal}. Silence on a recording setting
|
|
27681
|
+
* is the failure D62 exists to prevent: the operator believes the camera
|
|
27682
|
+
* is recording the way the form says, and it is not.
|
|
27683
|
+
*/
|
|
27684
|
+
var RecordingOnboardPatchSchema = object({
|
|
27685
|
+
enabled: boolean().optional(),
|
|
27686
|
+
overwriteWhenFull: boolean().optional(),
|
|
27687
|
+
preRecordSec: number().optional(),
|
|
27688
|
+
postRecordSec: number().optional(),
|
|
27689
|
+
segmentMinutes: number().optional(),
|
|
27690
|
+
/** The complete new window set for the primary track — not a delta. */
|
|
27691
|
+
windows: array(RecordWindowSchema).optional()
|
|
27692
|
+
});
|
|
27693
|
+
var recordingOnboardCapability = {
|
|
27694
|
+
name: "recording-onboard",
|
|
27695
|
+
scope: "device",
|
|
27696
|
+
deviceNative: true,
|
|
27697
|
+
mode: "singleton",
|
|
27698
|
+
deviceTypes: [DeviceType.Camera],
|
|
27699
|
+
deviceConfig: { ui: {
|
|
27700
|
+
kind: "derived-form",
|
|
27701
|
+
builderId: "recording-onboard",
|
|
27702
|
+
tab: "recording"
|
|
27703
|
+
} },
|
|
27704
|
+
methods: {
|
|
27705
|
+
getOptions: method(object({ deviceId: number() }), RecordingOnboardOptionsSchema),
|
|
27706
|
+
setSettings: method(object({
|
|
27707
|
+
deviceId: number(),
|
|
27708
|
+
settings: RecordingOnboardPatchSchema
|
|
27709
|
+
}), _void(), {
|
|
27710
|
+
kind: "mutation",
|
|
27711
|
+
auth: "admin"
|
|
27712
|
+
})
|
|
27713
|
+
},
|
|
27714
|
+
status: {
|
|
27715
|
+
schema: RecordingOnboardStatusSchema,
|
|
27716
|
+
kind: "poll"
|
|
27717
|
+
},
|
|
27718
|
+
runtimeState: RecordingOnboardStatusSchema,
|
|
27719
|
+
/**
|
|
27720
|
+
* Runtime-state durability: **restored** — operator-set camera-side
|
|
27721
|
+
* recording config; mutation-driven, and the storage half is the last
|
|
27722
|
+
* thing the camera said about its own card.
|
|
27723
|
+
*
|
|
27724
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
27725
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
27726
|
+
*/
|
|
27727
|
+
durability: "restored",
|
|
27728
|
+
/** Clock fields: written, but excluded from the compare that decides
|
|
27729
|
+
* whether persisting is worth a SQLite commit. */
|
|
27730
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
27731
|
+
};
|
|
27732
|
+
/**
|
|
26498
27733
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
26499
27734
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
26500
27735
|
* cap-keyed slice for the basic device flags every consumer expects to
|
|
@@ -29093,16 +30328,37 @@ var GridProfileOfferSchema = object({
|
|
|
29093
30328
|
]),
|
|
29094
30329
|
offered: boolean(),
|
|
29095
30330
|
/** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
|
|
29096
|
-
missingSources: array(number().int().positive())
|
|
30331
|
+
missingSources: array(number().int().positive()),
|
|
30332
|
+
/**
|
|
30333
|
+
* The canvas this profile composes onto, `WxH`, or empty when it is not
|
|
30334
|
+
* offered. DERIVED from the cells and the sources' own size at this profile —
|
|
30335
|
+
* it is reported because nothing else in the system would ever say what the
|
|
30336
|
+
* grid came out as, and because it is the number an operator would otherwise
|
|
30337
|
+
* expect to type.
|
|
30338
|
+
*/
|
|
30339
|
+
canvas: string(),
|
|
30340
|
+
/**
|
|
30341
|
+
* Whether this profile is PUBLISHED, of the ones the grid could serve.
|
|
30342
|
+
*
|
|
30343
|
+
* A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
|
|
30344
|
+
* a 4K canvas built from 4K decodes — something to opt into, not something a
|
|
30345
|
+
* viewer's adaptive should be handed by climbing to the top rung it can see.
|
|
30346
|
+
* Default is `mid` + `low`.
|
|
30347
|
+
*/
|
|
30348
|
+
published: boolean()
|
|
29097
30349
|
});
|
|
29098
30350
|
var GridLayoutViewSchema = object({
|
|
29099
30351
|
/** The persisted grid row this camera was declared from. */
|
|
29100
30352
|
instanceId: string(),
|
|
29101
30353
|
deviceId: number().int().nonnegative(),
|
|
29102
30354
|
name: string(),
|
|
29103
|
-
/**
|
|
29104
|
-
|
|
29105
|
-
|
|
30355
|
+
/**
|
|
30356
|
+
* NO canvas size. A grid's resolution is not authored: each profile derives
|
|
30357
|
+
* its own from the cells and its sources' dimensions. The two numbers that
|
|
30358
|
+
* used to be here were a text field that silently decided both how much the
|
|
30359
|
+
* composite cost and how sharp it was — see `profiles[].canvas` for what it
|
|
30360
|
+
* came out as.
|
|
30361
|
+
*/
|
|
29106
30362
|
fps: number().int(),
|
|
29107
30363
|
cells: array(GridLayoutCellSchema),
|
|
29108
30364
|
/** What the catalog will publish, and what it refuses to. Read-only. */
|
|
@@ -29111,9 +30367,13 @@ var GridLayoutViewSchema = object({
|
|
|
29111
30367
|
var GridLayoutPatchSchema = object({
|
|
29112
30368
|
deviceId: number().int().nonnegative(),
|
|
29113
30369
|
name: string().min(1).max(160).optional(),
|
|
29114
|
-
width: number().int().min(160).max(7680).optional(),
|
|
29115
|
-
height: number().int().min(120).max(4320).optional(),
|
|
29116
30370
|
fps: number().int().min(1).max(60).optional(),
|
|
30371
|
+
/** Which profiles to publish. See `GridProfileOffer.published`. */
|
|
30372
|
+
publishedProfiles: array(_enum([
|
|
30373
|
+
"high",
|
|
30374
|
+
"mid",
|
|
30375
|
+
"low"
|
|
30376
|
+
])).max(3).optional(),
|
|
29117
30377
|
/**
|
|
29118
30378
|
* The whole cell list at once. A per-cell patch would need an ordering the
|
|
29119
30379
|
* editor does not have, and a half-applied layout is a picture nobody asked
|
|
@@ -31554,6 +32814,25 @@ var ptzAutotrackCapability = {
|
|
|
31554
32814
|
scope: "device",
|
|
31555
32815
|
deviceNative: true,
|
|
31556
32816
|
mode: "singleton",
|
|
32817
|
+
/**
|
|
32818
|
+
* Two kinds of provider behind one name, the same shape `snapshot` and
|
|
32819
|
+
* `doorbell` use:
|
|
32820
|
+
*
|
|
32821
|
+
* - **Native**: the camera's own firmware autotrack, registered per device by
|
|
32822
|
+
* the driver when the probe says it exists (Reolink: yes; Hikvision:
|
|
32823
|
+
* model-dependent).
|
|
32824
|
+
* - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
|
|
32825
|
+
* which follows a subject with `ptz.move` pulses driven by the detection
|
|
32826
|
+
* pipeline. `defaultActive: false` — the operator binds it per camera in
|
|
32827
|
+
* the bindings UI, exactly as `virtual-doorbell` is bound.
|
|
32828
|
+
*
|
|
32829
|
+
* The binding chooses WHICH implementation answers; `setEnabled` stays the
|
|
32830
|
+
* one switch for the function either way. A camera never has both driving its
|
|
32831
|
+
* head, which is the whole reason this is not a second cap with a second
|
|
32832
|
+
* toggle (D62, D520).
|
|
32833
|
+
*/
|
|
32834
|
+
kind: "wrapper",
|
|
32835
|
+
defaultActive: false,
|
|
31557
32836
|
deviceTypes: [DeviceType.Camera],
|
|
31558
32837
|
deviceConfig: { ui: {
|
|
31559
32838
|
kind: "widget",
|
|
@@ -32036,7 +33315,7 @@ method(object({
|
|
|
32036
33315
|
auth: "admin"
|
|
32037
33316
|
}), method(object({ deviceId: number() }), RecordingConfigSchema, {
|
|
32038
33317
|
kind: "query",
|
|
32039
|
-
auth: "
|
|
33318
|
+
auth: "protected"
|
|
32040
33319
|
}), method(object({
|
|
32041
33320
|
deviceId: number(),
|
|
32042
33321
|
profile: string(),
|
|
@@ -32266,13 +33545,101 @@ var ExportStateSchema = _enum([
|
|
|
32266
33545
|
"expired",
|
|
32267
33546
|
"deleted"
|
|
32268
33547
|
]);
|
|
32269
|
-
/**
|
|
33548
|
+
/**
|
|
33549
|
+
* WHAT an export is — the authority, as opposed to the four top-level fields
|
|
33550
|
+
* the Library sorts and labels on (D558 § 2.2).
|
|
33551
|
+
*
|
|
33552
|
+
* Read it through {@link exportSubjectOf}, never off the record directly: the
|
|
33553
|
+
* field is optional for the history rows written before it existed, and that
|
|
33554
|
+
* absence has exactly one interpreter.
|
|
33555
|
+
*/
|
|
33556
|
+
var ExportSubjectSchema = discriminatedUnion("kind", [object({
|
|
33557
|
+
kind: literal("footage"),
|
|
33558
|
+
deviceId: number(),
|
|
33559
|
+
profiles: array(string()).min(1),
|
|
33560
|
+
fromMs: number(),
|
|
33561
|
+
toMs: number()
|
|
33562
|
+
}), object({
|
|
33563
|
+
kind: literal("clip"),
|
|
33564
|
+
deviceId: number(),
|
|
33565
|
+
provider: string().min(1),
|
|
33566
|
+
source: string().min(1),
|
|
33567
|
+
sourceLabel: string().min(1),
|
|
33568
|
+
clipId: string().min(1),
|
|
33569
|
+
/**
|
|
33570
|
+
* WHERE IN THE CATALOG to confirm this clip — the day window the surface was
|
|
33571
|
+
* already listing when the operator picked the segment.
|
|
33572
|
+
*
|
|
33573
|
+
* It is **not** a time range control and it never becomes one: the record's
|
|
33574
|
+
* `fromMs`/`toMs` come from the catalog ROW and from nothing a caller
|
|
33575
|
+
* supplied (D558 § 5.4.3), and a window that does not contain the clip is a
|
|
33576
|
+
* `catalog-miss`, not a silently wider search. It exists because
|
|
33577
|
+
* `videoclips.listClips` takes `since`/`until` and has no by-handle twin:
|
|
33578
|
+
* the catalog check D558 asks for is literally that call, and a call needs a
|
|
33579
|
+
* window. The surface has one — `ClipsBrowser`'s `dayWindow`, one local
|
|
33580
|
+
* wall-clock day, which is also the only width measured to be cheap (one
|
|
33581
|
+
* day on 592 lists 171 clips in 712 ms, a busy day 546 in 2.0 s; twenty days
|
|
33582
|
+
* of one child's events took 3.6–5.4 s).
|
|
33583
|
+
*/
|
|
33584
|
+
catalogWindow: object({
|
|
33585
|
+
sinceMs: number(),
|
|
33586
|
+
untilMs: number()
|
|
33587
|
+
}),
|
|
33588
|
+
profile: _enum([
|
|
33589
|
+
"high",
|
|
33590
|
+
"mid",
|
|
33591
|
+
"low"
|
|
33592
|
+
]),
|
|
33593
|
+
/**
|
|
33594
|
+
* The operator's authorisation to wake a sleeping camera for this export.
|
|
33595
|
+
* ONE definition, in the cap that owns the clip read ({@link ClipWakeSchema}),
|
|
33596
|
+
* because the gate that honours it is the clip provider's sleep gate — a
|
|
33597
|
+
* second enum here would be a second contract. Absent by default; never
|
|
33598
|
+
* settable by a scheduler or a retry.
|
|
33599
|
+
*
|
|
33600
|
+
* **One authorised yes is ONE wake.** A failed clip export is retried only by
|
|
33601
|
+
* an operator act that asks again; a queued job that outlived its wake fails
|
|
33602
|
+
* with a reason rather than waking on its turn; and this subject carries
|
|
33603
|
+
* exactly one profile precisely so one tap is never two fetches (D558 § 5.2).
|
|
33604
|
+
*/
|
|
33605
|
+
wake: ClipWakeSchema.optional()
|
|
33606
|
+
})]);
|
|
33607
|
+
/**
|
|
33608
|
+
* One export job / history row.
|
|
33609
|
+
*
|
|
33610
|
+
* **`subject` is the AUTHORITY on what was exported. `deviceId`, `profile`,
|
|
33611
|
+
* `fromMs` and `toMs` are its PROJECTION** — kept top-level because the whole
|
|
33612
|
+
* Library sorts and labels on them (`library-items.ts` orders an export by
|
|
33613
|
+
* `fromMs`; `export-format.ts` draws `rangeLabel` from the pair), and a row
|
|
33614
|
+
* that did not fill them would sort under the epoch and render a blank range.
|
|
33615
|
+
* Write to the subject and read from the projection and the two will disagree;
|
|
33616
|
+
* the projection is derived at creation and never edited afterwards.
|
|
33617
|
+
*
|
|
33618
|
+
* And they mean DIFFERENT FACTS for the two kinds, which is the part a reader
|
|
33619
|
+
* who knows only the recording export will get wrong:
|
|
33620
|
+
*
|
|
33621
|
+
* | field | `kind:'footage'` | `kind:'clip'` |
|
|
33622
|
+
* | --- | --- | --- |
|
|
33623
|
+
* | `fromMs`/`toMs` | the stretch the operator ASKED for | the camera's own boundaries, always `clip.timeRange`, never anything a caller supplied |
|
|
33624
|
+
* | `profile` | the stream rendered | the twin actually SERVED (`subject.profile` is the one asked for) |
|
|
33625
|
+
*
|
|
33626
|
+
* Same type, different fact — the shape this repo keeps getting wrong (D385's
|
|
33627
|
+
* two authorities, D224's second copy).
|
|
33628
|
+
*/
|
|
32270
33629
|
var ExportRecordSchema = object({
|
|
32271
33630
|
id: string(),
|
|
32272
33631
|
deviceId: number(),
|
|
32273
33632
|
profile: string(),
|
|
32274
33633
|
fromMs: number(),
|
|
32275
33634
|
toMs: number(),
|
|
33635
|
+
/**
|
|
33636
|
+
* What this export IS. Optional ONLY for the rows written before D558: the
|
|
33637
|
+
* store parses every row through this schema on every read, so a required
|
|
33638
|
+
* field would make the export AUDIT — which is the whole reason rows survive
|
|
33639
|
+
* file deletion — unreadable in one release. Absence means `footage`, and
|
|
33640
|
+
* {@link exportSubjectOf} is the one place that says so.
|
|
33641
|
+
*/
|
|
33642
|
+
subject: ExportSubjectSchema.optional(),
|
|
32276
33643
|
options: ExportOptionsSchema,
|
|
32277
33644
|
state: ExportStateSchema,
|
|
32278
33645
|
/** 0–100 while rendering; null otherwise. */
|
|
@@ -32289,6 +33656,18 @@ var ExportRecordSchema = object({
|
|
|
32289
33656
|
/** Failure reason when state is 'failed'; null otherwise. */
|
|
32290
33657
|
error: string().nullable()
|
|
32291
33658
|
});
|
|
33659
|
+
_enum([
|
|
33660
|
+
"catalog-miss",
|
|
33661
|
+
"catalog-unreachable",
|
|
33662
|
+
"no-file-for-window",
|
|
33663
|
+
"clip-in-progress",
|
|
33664
|
+
"unsupported-option",
|
|
33665
|
+
"sleeping",
|
|
33666
|
+
"camera-refused",
|
|
33667
|
+
"fetch-failed",
|
|
33668
|
+
"too-large-to-transfer",
|
|
33669
|
+
"wake-expired"
|
|
33670
|
+
]);
|
|
32292
33671
|
/** Candidate download URLs (LAN first, then operator extra hosts). */
|
|
32293
33672
|
var ExportDownloadSchema = object({
|
|
32294
33673
|
url: string(),
|
|
@@ -32307,16 +33686,50 @@ var ExportBytesSchema = object({
|
|
|
32307
33686
|
name: string(),
|
|
32308
33687
|
bytes: number().int().nonnegative()
|
|
32309
33688
|
});
|
|
33689
|
+
/** Canonical `profiles[]`, falling back to the legacy singular `profile`. */
|
|
33690
|
+
function resolveExportProfiles(input) {
|
|
33691
|
+
if (input.profiles !== void 0 && input.profiles.length > 0) return [...input.profiles];
|
|
33692
|
+
if (typeof input.profile === "string" && input.profile.length > 0) return [input.profile];
|
|
33693
|
+
return [];
|
|
33694
|
+
}
|
|
32310
33695
|
method(object({
|
|
32311
33696
|
deviceId: number(),
|
|
32312
33697
|
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
32313
33698
|
profile: string().optional(),
|
|
32314
33699
|
profiles: array(string()).min(1).optional(),
|
|
32315
|
-
|
|
32316
|
-
|
|
33700
|
+
/** Footage only — a clip's boundaries are the camera's. */
|
|
33701
|
+
fromMs: number().optional(),
|
|
33702
|
+
toMs: number().optional(),
|
|
33703
|
+
/** What to export. Absent means the legacy flat footage request. */
|
|
33704
|
+
subject: ExportSubjectSchema.optional(),
|
|
32317
33705
|
options: ExportOptionsSchema
|
|
32318
33706
|
}).superRefine((v, ctx) => {
|
|
32319
|
-
if (
|
|
33707
|
+
if (v.subject?.kind === "clip") {
|
|
33708
|
+
if (v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
33709
|
+
code: ZodIssueCode.custom,
|
|
33710
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId}) — the top-level field is what per-device scope enforcement reads`,
|
|
33711
|
+
path: ["subject", "deviceId"]
|
|
33712
|
+
});
|
|
33713
|
+
if (v.fromMs !== void 0 || v.toMs !== void 0) ctx.addIssue({
|
|
33714
|
+
code: ZodIssueCode.custom,
|
|
33715
|
+
message: "a clip export asks for no time range: the camera chose the boundaries and they are read from the catalog row",
|
|
33716
|
+
path: ["fromMs"]
|
|
33717
|
+
});
|
|
33718
|
+
return;
|
|
33719
|
+
}
|
|
33720
|
+
if (v.subject?.kind === "footage" && v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
33721
|
+
code: ZodIssueCode.custom,
|
|
33722
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId})`,
|
|
33723
|
+
path: ["subject", "deviceId"]
|
|
33724
|
+
});
|
|
33725
|
+
const fromMs = v.subject?.kind === "footage" ? v.subject.fromMs : v.fromMs;
|
|
33726
|
+
const toMs = v.subject?.kind === "footage" ? v.subject.toMs : v.toMs;
|
|
33727
|
+
if (typeof fromMs !== "number" || typeof toMs !== "number") ctx.addIssue({
|
|
33728
|
+
code: ZodIssueCode.custom,
|
|
33729
|
+
message: "a footage export needs fromMs and toMs",
|
|
33730
|
+
path: ["fromMs"]
|
|
33731
|
+
});
|
|
33732
|
+
if ((v.subject?.kind === "footage" ? [...v.subject.profiles] : resolveExportProfiles(v)).length < 1) ctx.addIssue({
|
|
32320
33733
|
code: ZodIssueCode.custom,
|
|
32321
33734
|
message: "pass profiles[] (min 1) or legacy profile",
|
|
32322
33735
|
path: ["profiles"]
|
|
@@ -35326,6 +36739,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
35326
36739
|
pressureSensor: pressureSensorCapability,
|
|
35327
36740
|
privacyMask: privacyMaskCapability,
|
|
35328
36741
|
ptzAutotrack: ptzAutotrackCapability,
|
|
36742
|
+
recordingOnboard: recordingOnboardCapability,
|
|
35329
36743
|
recordingSignal: recordingSignalCapability,
|
|
35330
36744
|
sceneMonitor: sceneMonitorCapability,
|
|
35331
36745
|
scriptRunner: scriptRunnerCapability,
|
|
@@ -36487,6 +37901,1329 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
36487
37901
|
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;
|
|
36488
37902
|
new Set(Object.values(DeviceType));
|
|
36489
37903
|
DeviceFeature.BatteryOperated;
|
|
37904
|
+
/**
|
|
37905
|
+
* The vocabulary, in the order a picker offers it.
|
|
37906
|
+
*
|
|
37907
|
+
* Adding a key here is not enough to make it pickable: both glyph tables must
|
|
37908
|
+
* gain it too, and their tests fail until they do. That is the intended
|
|
37909
|
+
* friction — it is the only thing that keeps the two surfaces honest.
|
|
37910
|
+
*/
|
|
37911
|
+
var DISPLAY_ICON_VOCABULARY = [
|
|
37912
|
+
{
|
|
37913
|
+
key: "camera",
|
|
37914
|
+
group: "device",
|
|
37915
|
+
synonyms: [
|
|
37916
|
+
"telecamera",
|
|
37917
|
+
"videocamera",
|
|
37918
|
+
"camera",
|
|
37919
|
+
"cam"
|
|
37920
|
+
]
|
|
37921
|
+
},
|
|
37922
|
+
{
|
|
37923
|
+
key: "network",
|
|
37924
|
+
group: "device",
|
|
37925
|
+
synonyms: [
|
|
37926
|
+
"rete",
|
|
37927
|
+
"network",
|
|
37928
|
+
"lan"
|
|
37929
|
+
]
|
|
37930
|
+
},
|
|
37931
|
+
{
|
|
37932
|
+
key: "toggle-left",
|
|
37933
|
+
group: "device",
|
|
37934
|
+
synonyms: [
|
|
37935
|
+
"interruttore",
|
|
37936
|
+
"switch",
|
|
37937
|
+
"toggle"
|
|
37938
|
+
]
|
|
37939
|
+
},
|
|
37940
|
+
{
|
|
37941
|
+
key: "toggle-right",
|
|
37942
|
+
group: "device",
|
|
37943
|
+
synonyms: [
|
|
37944
|
+
"interruttore",
|
|
37945
|
+
"switch",
|
|
37946
|
+
"toggle",
|
|
37947
|
+
"acceso"
|
|
37948
|
+
]
|
|
37949
|
+
},
|
|
37950
|
+
{
|
|
37951
|
+
key: "circle-dot",
|
|
37952
|
+
group: "device",
|
|
37953
|
+
synonyms: [
|
|
37954
|
+
"pulsante",
|
|
37955
|
+
"bottone",
|
|
37956
|
+
"button",
|
|
37957
|
+
"dot"
|
|
37958
|
+
]
|
|
37959
|
+
},
|
|
37960
|
+
{
|
|
37961
|
+
key: "refresh-cw",
|
|
37962
|
+
group: "device",
|
|
37963
|
+
synonyms: [
|
|
37964
|
+
"aggiorna",
|
|
37965
|
+
"ricarica",
|
|
37966
|
+
"refresh",
|
|
37967
|
+
"reload",
|
|
37968
|
+
"ciclo"
|
|
37969
|
+
]
|
|
37970
|
+
},
|
|
37971
|
+
{
|
|
37972
|
+
key: "box",
|
|
37973
|
+
group: "device",
|
|
37974
|
+
synonyms: [
|
|
37975
|
+
"scatola",
|
|
37976
|
+
"box",
|
|
37977
|
+
"contenitore"
|
|
37978
|
+
]
|
|
37979
|
+
},
|
|
37980
|
+
{
|
|
37981
|
+
key: "scroll-text",
|
|
37982
|
+
group: "device",
|
|
37983
|
+
synonyms: [
|
|
37984
|
+
"registro",
|
|
37985
|
+
"log",
|
|
37986
|
+
"testo"
|
|
37987
|
+
]
|
|
37988
|
+
},
|
|
37989
|
+
{
|
|
37990
|
+
key: "workflow",
|
|
37991
|
+
group: "device",
|
|
37992
|
+
synonyms: [
|
|
37993
|
+
"automazione",
|
|
37994
|
+
"flusso",
|
|
37995
|
+
"workflow",
|
|
37996
|
+
"scenario"
|
|
37997
|
+
]
|
|
37998
|
+
},
|
|
37999
|
+
{
|
|
38000
|
+
key: "lock",
|
|
38001
|
+
group: "device",
|
|
38002
|
+
synonyms: [
|
|
38003
|
+
"serratura",
|
|
38004
|
+
"chiave",
|
|
38005
|
+
"lucchetto",
|
|
38006
|
+
"chiuso",
|
|
38007
|
+
"locked"
|
|
38008
|
+
]
|
|
38009
|
+
},
|
|
38010
|
+
{
|
|
38011
|
+
key: "lock-open",
|
|
38012
|
+
group: "device",
|
|
38013
|
+
synonyms: [
|
|
38014
|
+
"serratura",
|
|
38015
|
+
"aperta",
|
|
38016
|
+
"sbloccato",
|
|
38017
|
+
"unlocked"
|
|
38018
|
+
]
|
|
38019
|
+
},
|
|
38020
|
+
{
|
|
38021
|
+
key: "blinds",
|
|
38022
|
+
group: "device",
|
|
38023
|
+
synonyms: [
|
|
38024
|
+
"tapparella",
|
|
38025
|
+
"persiana",
|
|
38026
|
+
"serranda",
|
|
38027
|
+
"avvolgibile",
|
|
38028
|
+
"shutter",
|
|
38029
|
+
"blind",
|
|
38030
|
+
"veneziana"
|
|
38031
|
+
]
|
|
38032
|
+
},
|
|
38033
|
+
{
|
|
38034
|
+
key: "air-vent",
|
|
38035
|
+
group: "device",
|
|
38036
|
+
synonyms: [
|
|
38037
|
+
"bocchetta",
|
|
38038
|
+
"aria",
|
|
38039
|
+
"ventilazione",
|
|
38040
|
+
"condizionatore",
|
|
38041
|
+
"vent"
|
|
38042
|
+
]
|
|
38043
|
+
},
|
|
38044
|
+
{
|
|
38045
|
+
key: "monitor-play",
|
|
38046
|
+
group: "device",
|
|
38047
|
+
synonyms: [
|
|
38048
|
+
"schermo",
|
|
38049
|
+
"riproduzione",
|
|
38050
|
+
"player",
|
|
38051
|
+
"display"
|
|
38052
|
+
]
|
|
38053
|
+
},
|
|
38054
|
+
{
|
|
38055
|
+
key: "sliders-horizontal",
|
|
38056
|
+
group: "device",
|
|
38057
|
+
synonyms: [
|
|
38058
|
+
"regolazione",
|
|
38059
|
+
"cursori",
|
|
38060
|
+
"impostazioni",
|
|
38061
|
+
"slider"
|
|
38062
|
+
]
|
|
38063
|
+
},
|
|
38064
|
+
{
|
|
38065
|
+
key: "map-pin",
|
|
38066
|
+
group: "device",
|
|
38067
|
+
synonyms: [
|
|
38068
|
+
"posizione",
|
|
38069
|
+
"luogo",
|
|
38070
|
+
"mappa",
|
|
38071
|
+
"pin"
|
|
38072
|
+
]
|
|
38073
|
+
},
|
|
38074
|
+
{
|
|
38075
|
+
key: "bot",
|
|
38076
|
+
group: "device",
|
|
38077
|
+
synonyms: [
|
|
38078
|
+
"robot",
|
|
38079
|
+
"aspirapolvere",
|
|
38080
|
+
"bot"
|
|
38081
|
+
]
|
|
38082
|
+
},
|
|
38083
|
+
{
|
|
38084
|
+
key: "tractor",
|
|
38085
|
+
group: "device",
|
|
38086
|
+
synonyms: [
|
|
38087
|
+
"trattore",
|
|
38088
|
+
"tosaerba",
|
|
38089
|
+
"giardino"
|
|
38090
|
+
]
|
|
38091
|
+
},
|
|
38092
|
+
{
|
|
38093
|
+
key: "image",
|
|
38094
|
+
group: "device",
|
|
38095
|
+
synonyms: [
|
|
38096
|
+
"immagine",
|
|
38097
|
+
"foto",
|
|
38098
|
+
"snapshot"
|
|
38099
|
+
]
|
|
38100
|
+
},
|
|
38101
|
+
{
|
|
38102
|
+
key: "bone",
|
|
38103
|
+
group: "device",
|
|
38104
|
+
synonyms: ["osso", "cane"]
|
|
38105
|
+
},
|
|
38106
|
+
{
|
|
38107
|
+
key: "door-closed",
|
|
38108
|
+
group: "device",
|
|
38109
|
+
synonyms: [
|
|
38110
|
+
"porta",
|
|
38111
|
+
"chiusa",
|
|
38112
|
+
"contatto"
|
|
38113
|
+
]
|
|
38114
|
+
},
|
|
38115
|
+
{
|
|
38116
|
+
key: "door-open",
|
|
38117
|
+
group: "device",
|
|
38118
|
+
synonyms: [
|
|
38119
|
+
"porta",
|
|
38120
|
+
"aperta",
|
|
38121
|
+
"contatto",
|
|
38122
|
+
"ingresso",
|
|
38123
|
+
"uscita"
|
|
38124
|
+
]
|
|
38125
|
+
},
|
|
38126
|
+
{
|
|
38127
|
+
key: "crosshair",
|
|
38128
|
+
group: "device",
|
|
38129
|
+
synonyms: [
|
|
38130
|
+
"mirino",
|
|
38131
|
+
"puntamento",
|
|
38132
|
+
"centro"
|
|
38133
|
+
]
|
|
38134
|
+
},
|
|
38135
|
+
{
|
|
38136
|
+
key: "eye",
|
|
38137
|
+
group: "device",
|
|
38138
|
+
synonyms: [
|
|
38139
|
+
"occhio",
|
|
38140
|
+
"visibile",
|
|
38141
|
+
"visto"
|
|
38142
|
+
]
|
|
38143
|
+
},
|
|
38144
|
+
{
|
|
38145
|
+
key: "eye-off",
|
|
38146
|
+
group: "device",
|
|
38147
|
+
synonyms: [
|
|
38148
|
+
"privacy",
|
|
38149
|
+
"nascosto",
|
|
38150
|
+
"oscurato",
|
|
38151
|
+
"mascherato",
|
|
38152
|
+
"invisibile"
|
|
38153
|
+
]
|
|
38154
|
+
},
|
|
38155
|
+
{
|
|
38156
|
+
key: "smartphone",
|
|
38157
|
+
group: "device",
|
|
38158
|
+
synonyms: [
|
|
38159
|
+
"telefono",
|
|
38160
|
+
"cellulare",
|
|
38161
|
+
"smartphone"
|
|
38162
|
+
]
|
|
38163
|
+
},
|
|
38164
|
+
{
|
|
38165
|
+
key: "phone",
|
|
38166
|
+
group: "device",
|
|
38167
|
+
synonyms: ["telefono", "chiamata"]
|
|
38168
|
+
},
|
|
38169
|
+
{
|
|
38170
|
+
key: "tablet",
|
|
38171
|
+
group: "device",
|
|
38172
|
+
synonyms: ["tablet", "ipad"]
|
|
38173
|
+
},
|
|
38174
|
+
{
|
|
38175
|
+
key: "laptop",
|
|
38176
|
+
group: "device",
|
|
38177
|
+
synonyms: [
|
|
38178
|
+
"portatile",
|
|
38179
|
+
"laptop",
|
|
38180
|
+
"notebook"
|
|
38181
|
+
]
|
|
38182
|
+
},
|
|
38183
|
+
{
|
|
38184
|
+
key: "monitor",
|
|
38185
|
+
group: "device",
|
|
38186
|
+
synonyms: ["monitor", "schermo"]
|
|
38187
|
+
},
|
|
38188
|
+
{
|
|
38189
|
+
key: "tv",
|
|
38190
|
+
group: "device",
|
|
38191
|
+
synonyms: ["televisore", "tv"]
|
|
38192
|
+
},
|
|
38193
|
+
{
|
|
38194
|
+
key: "speaker",
|
|
38195
|
+
group: "device",
|
|
38196
|
+
synonyms: [
|
|
38197
|
+
"altoparlante",
|
|
38198
|
+
"cassa",
|
|
38199
|
+
"speaker"
|
|
38200
|
+
]
|
|
38201
|
+
},
|
|
38202
|
+
{
|
|
38203
|
+
key: "printer",
|
|
38204
|
+
group: "device",
|
|
38205
|
+
synonyms: ["stampante"]
|
|
38206
|
+
},
|
|
38207
|
+
{
|
|
38208
|
+
key: "projector",
|
|
38209
|
+
group: "device",
|
|
38210
|
+
synonyms: ["proiettore"]
|
|
38211
|
+
},
|
|
38212
|
+
{
|
|
38213
|
+
key: "gamepad-2",
|
|
38214
|
+
group: "device",
|
|
38215
|
+
synonyms: [
|
|
38216
|
+
"console",
|
|
38217
|
+
"gioco",
|
|
38218
|
+
"joystick"
|
|
38219
|
+
]
|
|
38220
|
+
},
|
|
38221
|
+
{
|
|
38222
|
+
key: "radio",
|
|
38223
|
+
group: "device",
|
|
38224
|
+
synonyms: ["radio"]
|
|
38225
|
+
},
|
|
38226
|
+
{
|
|
38227
|
+
key: "router",
|
|
38228
|
+
group: "device",
|
|
38229
|
+
synonyms: ["router", "modem"]
|
|
38230
|
+
},
|
|
38231
|
+
{
|
|
38232
|
+
key: "bluetooth",
|
|
38233
|
+
group: "device",
|
|
38234
|
+
synonyms: ["bluetooth"]
|
|
38235
|
+
},
|
|
38236
|
+
{
|
|
38237
|
+
key: "cpu",
|
|
38238
|
+
group: "device",
|
|
38239
|
+
synonyms: [
|
|
38240
|
+
"processore",
|
|
38241
|
+
"cpu",
|
|
38242
|
+
"chip",
|
|
38243
|
+
"dispositivo"
|
|
38244
|
+
]
|
|
38245
|
+
},
|
|
38246
|
+
{
|
|
38247
|
+
key: "hard-drive",
|
|
38248
|
+
group: "device",
|
|
38249
|
+
synonyms: [
|
|
38250
|
+
"disco",
|
|
38251
|
+
"hdd",
|
|
38252
|
+
"archiviazione",
|
|
38253
|
+
"storage"
|
|
38254
|
+
]
|
|
38255
|
+
},
|
|
38256
|
+
{
|
|
38257
|
+
key: "server",
|
|
38258
|
+
group: "device",
|
|
38259
|
+
synonyms: ["server", "nas"]
|
|
38260
|
+
},
|
|
38261
|
+
{
|
|
38262
|
+
key: "mic",
|
|
38263
|
+
group: "device",
|
|
38264
|
+
synonyms: ["microfono", "audio"]
|
|
38265
|
+
},
|
|
38266
|
+
{
|
|
38267
|
+
key: "mic-off",
|
|
38268
|
+
group: "device",
|
|
38269
|
+
synonyms: [
|
|
38270
|
+
"microfono",
|
|
38271
|
+
"spento",
|
|
38272
|
+
"muto"
|
|
38273
|
+
]
|
|
38274
|
+
},
|
|
38275
|
+
{
|
|
38276
|
+
key: "video",
|
|
38277
|
+
group: "device",
|
|
38278
|
+
synonyms: ["video"]
|
|
38279
|
+
},
|
|
38280
|
+
{
|
|
38281
|
+
key: "video-off",
|
|
38282
|
+
group: "device",
|
|
38283
|
+
synonyms: ["video", "spento"]
|
|
38284
|
+
},
|
|
38285
|
+
{
|
|
38286
|
+
key: "music",
|
|
38287
|
+
group: "device",
|
|
38288
|
+
synonyms: ["musica"]
|
|
38289
|
+
},
|
|
38290
|
+
{
|
|
38291
|
+
key: "plug",
|
|
38292
|
+
group: "device",
|
|
38293
|
+
synonyms: [
|
|
38294
|
+
"presa",
|
|
38295
|
+
"spina",
|
|
38296
|
+
"plug"
|
|
38297
|
+
]
|
|
38298
|
+
},
|
|
38299
|
+
{
|
|
38300
|
+
key: "plug-zap",
|
|
38301
|
+
group: "device",
|
|
38302
|
+
synonyms: ["presa", "alimentata"]
|
|
38303
|
+
},
|
|
38304
|
+
{
|
|
38305
|
+
key: "power",
|
|
38306
|
+
group: "device",
|
|
38307
|
+
synonyms: [
|
|
38308
|
+
"accensione",
|
|
38309
|
+
"alimentazione",
|
|
38310
|
+
"power"
|
|
38311
|
+
]
|
|
38312
|
+
},
|
|
38313
|
+
{
|
|
38314
|
+
key: "power-off",
|
|
38315
|
+
group: "device",
|
|
38316
|
+
synonyms: ["spegnimento", "spento"]
|
|
38317
|
+
},
|
|
38318
|
+
{
|
|
38319
|
+
key: "cable",
|
|
38320
|
+
group: "device",
|
|
38321
|
+
synonyms: ["cavo"]
|
|
38322
|
+
},
|
|
38323
|
+
{
|
|
38324
|
+
key: "battery",
|
|
38325
|
+
group: "device",
|
|
38326
|
+
synonyms: ["batteria"]
|
|
38327
|
+
},
|
|
38328
|
+
{
|
|
38329
|
+
key: "battery-charging",
|
|
38330
|
+
group: "device",
|
|
38331
|
+
synonyms: [
|
|
38332
|
+
"batteria",
|
|
38333
|
+
"carica",
|
|
38334
|
+
"ricarica"
|
|
38335
|
+
]
|
|
38336
|
+
},
|
|
38337
|
+
{
|
|
38338
|
+
key: "battery-full",
|
|
38339
|
+
group: "device",
|
|
38340
|
+
synonyms: [
|
|
38341
|
+
"batteria",
|
|
38342
|
+
"carica",
|
|
38343
|
+
"piena"
|
|
38344
|
+
]
|
|
38345
|
+
},
|
|
38346
|
+
{
|
|
38347
|
+
key: "battery-low",
|
|
38348
|
+
group: "device",
|
|
38349
|
+
synonyms: ["batteria", "scarica"]
|
|
38350
|
+
},
|
|
38351
|
+
{
|
|
38352
|
+
key: "key",
|
|
38353
|
+
group: "device",
|
|
38354
|
+
synonyms: ["chiave"]
|
|
38355
|
+
},
|
|
38356
|
+
{
|
|
38357
|
+
key: "key-round",
|
|
38358
|
+
group: "device",
|
|
38359
|
+
synonyms: ["chiave", "tonda"]
|
|
38360
|
+
},
|
|
38361
|
+
{
|
|
38362
|
+
key: "mailbox",
|
|
38363
|
+
group: "device",
|
|
38364
|
+
synonyms: [
|
|
38365
|
+
"cassetta",
|
|
38366
|
+
"postale",
|
|
38367
|
+
"posta"
|
|
38368
|
+
]
|
|
38369
|
+
},
|
|
38370
|
+
{
|
|
38371
|
+
key: "package",
|
|
38372
|
+
group: "device",
|
|
38373
|
+
synonyms: ["pacco", "consegna"]
|
|
38374
|
+
},
|
|
38375
|
+
{
|
|
38376
|
+
key: "shield",
|
|
38377
|
+
group: "device",
|
|
38378
|
+
synonyms: [
|
|
38379
|
+
"scudo",
|
|
38380
|
+
"protezione",
|
|
38381
|
+
"sicurezza",
|
|
38382
|
+
"allarme"
|
|
38383
|
+
]
|
|
38384
|
+
},
|
|
38385
|
+
{
|
|
38386
|
+
key: "shield-check",
|
|
38387
|
+
group: "device",
|
|
38388
|
+
synonyms: [
|
|
38389
|
+
"protetto",
|
|
38390
|
+
"sicuro",
|
|
38391
|
+
"armato"
|
|
38392
|
+
]
|
|
38393
|
+
},
|
|
38394
|
+
{
|
|
38395
|
+
key: "shield-off",
|
|
38396
|
+
group: "device",
|
|
38397
|
+
synonyms: [
|
|
38398
|
+
"protezione",
|
|
38399
|
+
"disattivata",
|
|
38400
|
+
"disarmato"
|
|
38401
|
+
]
|
|
38402
|
+
},
|
|
38403
|
+
{
|
|
38404
|
+
key: "briefcase",
|
|
38405
|
+
group: "device",
|
|
38406
|
+
synonyms: [
|
|
38407
|
+
"ufficio",
|
|
38408
|
+
"lavoro",
|
|
38409
|
+
"valigetta",
|
|
38410
|
+
"studio"
|
|
38411
|
+
]
|
|
38412
|
+
},
|
|
38413
|
+
{
|
|
38414
|
+
key: "lightbulb",
|
|
38415
|
+
group: "lighting",
|
|
38416
|
+
synonyms: [
|
|
38417
|
+
"luce",
|
|
38418
|
+
"lampadina",
|
|
38419
|
+
"lampada",
|
|
38420
|
+
"light"
|
|
38421
|
+
]
|
|
38422
|
+
},
|
|
38423
|
+
{
|
|
38424
|
+
key: "lamp",
|
|
38425
|
+
group: "lighting",
|
|
38426
|
+
synonyms: ["lampada", "abatjour"]
|
|
38427
|
+
},
|
|
38428
|
+
{
|
|
38429
|
+
key: "lamp-ceiling",
|
|
38430
|
+
group: "lighting",
|
|
38431
|
+
synonyms: [
|
|
38432
|
+
"plafoniera",
|
|
38433
|
+
"lampadario",
|
|
38434
|
+
"luce",
|
|
38435
|
+
"soffitto"
|
|
38436
|
+
]
|
|
38437
|
+
},
|
|
38438
|
+
{
|
|
38439
|
+
key: "lamp-desk",
|
|
38440
|
+
group: "lighting",
|
|
38441
|
+
synonyms: [
|
|
38442
|
+
"lampada",
|
|
38443
|
+
"scrivania",
|
|
38444
|
+
"abatjour"
|
|
38445
|
+
]
|
|
38446
|
+
},
|
|
38447
|
+
{
|
|
38448
|
+
key: "lamp-floor",
|
|
38449
|
+
group: "lighting",
|
|
38450
|
+
synonyms: [
|
|
38451
|
+
"piantana",
|
|
38452
|
+
"lampada",
|
|
38453
|
+
"terra"
|
|
38454
|
+
]
|
|
38455
|
+
},
|
|
38456
|
+
{
|
|
38457
|
+
key: "flashlight",
|
|
38458
|
+
group: "lighting",
|
|
38459
|
+
synonyms: [
|
|
38460
|
+
"faretto",
|
|
38461
|
+
"torcia",
|
|
38462
|
+
"spot",
|
|
38463
|
+
"faro"
|
|
38464
|
+
]
|
|
38465
|
+
},
|
|
38466
|
+
{
|
|
38467
|
+
key: "heater",
|
|
38468
|
+
group: "lighting",
|
|
38469
|
+
synonyms: [
|
|
38470
|
+
"riscaldamento",
|
|
38471
|
+
"termosifone",
|
|
38472
|
+
"stufa",
|
|
38473
|
+
"calore"
|
|
38474
|
+
]
|
|
38475
|
+
},
|
|
38476
|
+
{
|
|
38477
|
+
key: "snowflake",
|
|
38478
|
+
group: "lighting",
|
|
38479
|
+
synonyms: [
|
|
38480
|
+
"freddo",
|
|
38481
|
+
"raffrescamento",
|
|
38482
|
+
"aria",
|
|
38483
|
+
"condizionata",
|
|
38484
|
+
"neve"
|
|
38485
|
+
]
|
|
38486
|
+
},
|
|
38487
|
+
{
|
|
38488
|
+
key: "thermometer-sun",
|
|
38489
|
+
group: "lighting",
|
|
38490
|
+
synonyms: [
|
|
38491
|
+
"caldo",
|
|
38492
|
+
"temperatura",
|
|
38493
|
+
"alta"
|
|
38494
|
+
]
|
|
38495
|
+
},
|
|
38496
|
+
{
|
|
38497
|
+
key: "thermometer-snowflake",
|
|
38498
|
+
group: "lighting",
|
|
38499
|
+
synonyms: [
|
|
38500
|
+
"freddo",
|
|
38501
|
+
"temperatura",
|
|
38502
|
+
"bassa"
|
|
38503
|
+
]
|
|
38504
|
+
},
|
|
38505
|
+
{
|
|
38506
|
+
key: "refrigerator",
|
|
38507
|
+
group: "lighting",
|
|
38508
|
+
synonyms: ["frigorifero", "frigo"]
|
|
38509
|
+
},
|
|
38510
|
+
{
|
|
38511
|
+
key: "microwave",
|
|
38512
|
+
group: "lighting",
|
|
38513
|
+
synonyms: ["microonde", "forno"]
|
|
38514
|
+
},
|
|
38515
|
+
{
|
|
38516
|
+
key: "washing-machine",
|
|
38517
|
+
group: "lighting",
|
|
38518
|
+
synonyms: [
|
|
38519
|
+
"lavatrice",
|
|
38520
|
+
"lavastoviglie",
|
|
38521
|
+
"asciugatrice"
|
|
38522
|
+
]
|
|
38523
|
+
},
|
|
38524
|
+
{
|
|
38525
|
+
key: "cooking-pot",
|
|
38526
|
+
group: "lighting",
|
|
38527
|
+
synonyms: [
|
|
38528
|
+
"pentola",
|
|
38529
|
+
"cucina",
|
|
38530
|
+
"fornello"
|
|
38531
|
+
]
|
|
38532
|
+
},
|
|
38533
|
+
{
|
|
38534
|
+
key: "utensils",
|
|
38535
|
+
group: "lighting",
|
|
38536
|
+
synonyms: [
|
|
38537
|
+
"cucina",
|
|
38538
|
+
"posate",
|
|
38539
|
+
"sala",
|
|
38540
|
+
"pranzo"
|
|
38541
|
+
]
|
|
38542
|
+
},
|
|
38543
|
+
{
|
|
38544
|
+
key: "thermometer",
|
|
38545
|
+
group: "sensing",
|
|
38546
|
+
synonyms: [
|
|
38547
|
+
"temperatura",
|
|
38548
|
+
"termometro",
|
|
38549
|
+
"termostato"
|
|
38550
|
+
]
|
|
38551
|
+
},
|
|
38552
|
+
{
|
|
38553
|
+
key: "droplets",
|
|
38554
|
+
group: "sensing",
|
|
38555
|
+
synonyms: [
|
|
38556
|
+
"umidita",
|
|
38557
|
+
"acqua",
|
|
38558
|
+
"allagamento",
|
|
38559
|
+
"perdita"
|
|
38560
|
+
]
|
|
38561
|
+
},
|
|
38562
|
+
{
|
|
38563
|
+
key: "droplet",
|
|
38564
|
+
group: "sensing",
|
|
38565
|
+
synonyms: [
|
|
38566
|
+
"goccia",
|
|
38567
|
+
"acqua",
|
|
38568
|
+
"perdita",
|
|
38569
|
+
"allagamento"
|
|
38570
|
+
]
|
|
38571
|
+
},
|
|
38572
|
+
{
|
|
38573
|
+
key: "gauge",
|
|
38574
|
+
group: "sensing",
|
|
38575
|
+
synonyms: [
|
|
38576
|
+
"misura",
|
|
38577
|
+
"livello",
|
|
38578
|
+
"pressione",
|
|
38579
|
+
"indicatore"
|
|
38580
|
+
]
|
|
38581
|
+
},
|
|
38582
|
+
{
|
|
38583
|
+
key: "gauge2",
|
|
38584
|
+
group: "sensing",
|
|
38585
|
+
synonyms: [
|
|
38586
|
+
"misura",
|
|
38587
|
+
"livello",
|
|
38588
|
+
"pressione",
|
|
38589
|
+
"indicatore"
|
|
38590
|
+
],
|
|
38591
|
+
aliasOf: "gauge"
|
|
38592
|
+
},
|
|
38593
|
+
{
|
|
38594
|
+
key: "sun",
|
|
38595
|
+
group: "sensing",
|
|
38596
|
+
synonyms: [
|
|
38597
|
+
"sole",
|
|
38598
|
+
"luminosita",
|
|
38599
|
+
"giorno",
|
|
38600
|
+
"esterno"
|
|
38601
|
+
]
|
|
38602
|
+
},
|
|
38603
|
+
{
|
|
38604
|
+
key: "sun-medium",
|
|
38605
|
+
group: "sensing",
|
|
38606
|
+
synonyms: ["sole", "luminosita"]
|
|
38607
|
+
},
|
|
38608
|
+
{
|
|
38609
|
+
key: "sunrise",
|
|
38610
|
+
group: "sensing",
|
|
38611
|
+
synonyms: ["alba"]
|
|
38612
|
+
},
|
|
38613
|
+
{
|
|
38614
|
+
key: "moon",
|
|
38615
|
+
group: "sensing",
|
|
38616
|
+
synonyms: [
|
|
38617
|
+
"notte",
|
|
38618
|
+
"luna",
|
|
38619
|
+
"notturna",
|
|
38620
|
+
"nightvision"
|
|
38621
|
+
]
|
|
38622
|
+
},
|
|
38623
|
+
{
|
|
38624
|
+
key: "wind",
|
|
38625
|
+
group: "sensing",
|
|
38626
|
+
synonyms: [
|
|
38627
|
+
"vento",
|
|
38628
|
+
"aria",
|
|
38629
|
+
"gas",
|
|
38630
|
+
"monossido",
|
|
38631
|
+
"co"
|
|
38632
|
+
]
|
|
38633
|
+
},
|
|
38634
|
+
{
|
|
38635
|
+
key: "cloud",
|
|
38636
|
+
group: "sensing",
|
|
38637
|
+
synonyms: ["nuvola", "nuvoloso"]
|
|
38638
|
+
},
|
|
38639
|
+
{
|
|
38640
|
+
key: "cloud-rain",
|
|
38641
|
+
group: "sensing",
|
|
38642
|
+
synonyms: ["pioggia"]
|
|
38643
|
+
},
|
|
38644
|
+
{
|
|
38645
|
+
key: "cloud-sun",
|
|
38646
|
+
group: "sensing",
|
|
38647
|
+
synonyms: [
|
|
38648
|
+
"variabile",
|
|
38649
|
+
"sole",
|
|
38650
|
+
"nuvole"
|
|
38651
|
+
]
|
|
38652
|
+
},
|
|
38653
|
+
{
|
|
38654
|
+
key: "waves",
|
|
38655
|
+
group: "sensing",
|
|
38656
|
+
synonyms: [
|
|
38657
|
+
"onde",
|
|
38658
|
+
"acqua",
|
|
38659
|
+
"vibrazione"
|
|
38660
|
+
]
|
|
38661
|
+
},
|
|
38662
|
+
{
|
|
38663
|
+
key: "sprout",
|
|
38664
|
+
group: "sensing",
|
|
38665
|
+
synonyms: [
|
|
38666
|
+
"irrigazione",
|
|
38667
|
+
"pianta",
|
|
38668
|
+
"germoglio",
|
|
38669
|
+
"orto"
|
|
38670
|
+
]
|
|
38671
|
+
},
|
|
38672
|
+
{
|
|
38673
|
+
key: "leaf",
|
|
38674
|
+
group: "sensing",
|
|
38675
|
+
synonyms: [
|
|
38676
|
+
"foglia",
|
|
38677
|
+
"pianta",
|
|
38678
|
+
"verde"
|
|
38679
|
+
]
|
|
38680
|
+
},
|
|
38681
|
+
{
|
|
38682
|
+
key: "flower",
|
|
38683
|
+
group: "sensing",
|
|
38684
|
+
synonyms: ["fiore", "pianta"]
|
|
38685
|
+
},
|
|
38686
|
+
{
|
|
38687
|
+
key: "zap",
|
|
38688
|
+
group: "sensing",
|
|
38689
|
+
synonyms: [
|
|
38690
|
+
"energia",
|
|
38691
|
+
"corrente",
|
|
38692
|
+
"consumo",
|
|
38693
|
+
"watt",
|
|
38694
|
+
"elettricita"
|
|
38695
|
+
]
|
|
38696
|
+
},
|
|
38697
|
+
{
|
|
38698
|
+
key: "flame",
|
|
38699
|
+
group: "sensing",
|
|
38700
|
+
synonyms: [
|
|
38701
|
+
"fiamma",
|
|
38702
|
+
"gas",
|
|
38703
|
+
"fuoco",
|
|
38704
|
+
"incendio"
|
|
38705
|
+
]
|
|
38706
|
+
},
|
|
38707
|
+
{
|
|
38708
|
+
key: "bell",
|
|
38709
|
+
group: "sensing",
|
|
38710
|
+
synonyms: [
|
|
38711
|
+
"campanello",
|
|
38712
|
+
"suoneria",
|
|
38713
|
+
"notifica",
|
|
38714
|
+
"bell",
|
|
38715
|
+
"chime"
|
|
38716
|
+
]
|
|
38717
|
+
},
|
|
38718
|
+
{
|
|
38719
|
+
key: "bell-ring",
|
|
38720
|
+
group: "sensing",
|
|
38721
|
+
synonyms: [
|
|
38722
|
+
"campanello",
|
|
38723
|
+
"suona",
|
|
38724
|
+
"doorbell",
|
|
38725
|
+
"notifica"
|
|
38726
|
+
]
|
|
38727
|
+
},
|
|
38728
|
+
{
|
|
38729
|
+
key: "bell-off",
|
|
38730
|
+
group: "sensing",
|
|
38731
|
+
synonyms: [
|
|
38732
|
+
"silenzioso",
|
|
38733
|
+
"notifiche",
|
|
38734
|
+
"disattivate",
|
|
38735
|
+
"muto"
|
|
38736
|
+
]
|
|
38737
|
+
},
|
|
38738
|
+
{
|
|
38739
|
+
key: "siren",
|
|
38740
|
+
group: "sensing",
|
|
38741
|
+
synonyms: ["sirena", "allarme"]
|
|
38742
|
+
},
|
|
38743
|
+
{
|
|
38744
|
+
key: "radar",
|
|
38745
|
+
group: "sensing",
|
|
38746
|
+
synonyms: [
|
|
38747
|
+
"movimento",
|
|
38748
|
+
"presenza",
|
|
38749
|
+
"pir",
|
|
38750
|
+
"rilevamento",
|
|
38751
|
+
"radar"
|
|
38752
|
+
]
|
|
38753
|
+
},
|
|
38754
|
+
{
|
|
38755
|
+
key: "activity",
|
|
38756
|
+
group: "sensing",
|
|
38757
|
+
synonyms: [
|
|
38758
|
+
"attivita",
|
|
38759
|
+
"sensore",
|
|
38760
|
+
"generico"
|
|
38761
|
+
]
|
|
38762
|
+
},
|
|
38763
|
+
{
|
|
38764
|
+
key: "hash",
|
|
38765
|
+
group: "sensing",
|
|
38766
|
+
synonyms: [
|
|
38767
|
+
"numero",
|
|
38768
|
+
"contatore",
|
|
38769
|
+
"valore"
|
|
38770
|
+
]
|
|
38771
|
+
},
|
|
38772
|
+
{
|
|
38773
|
+
key: "clock",
|
|
38774
|
+
group: "sensing",
|
|
38775
|
+
synonyms: [
|
|
38776
|
+
"orologio",
|
|
38777
|
+
"ora",
|
|
38778
|
+
"tempo"
|
|
38779
|
+
]
|
|
38780
|
+
},
|
|
38781
|
+
{
|
|
38782
|
+
key: "timer",
|
|
38783
|
+
group: "sensing",
|
|
38784
|
+
synonyms: [
|
|
38785
|
+
"timer",
|
|
38786
|
+
"conto",
|
|
38787
|
+
"rovescia"
|
|
38788
|
+
]
|
|
38789
|
+
},
|
|
38790
|
+
{
|
|
38791
|
+
key: "calendar",
|
|
38792
|
+
group: "sensing",
|
|
38793
|
+
synonyms: [
|
|
38794
|
+
"calendario",
|
|
38795
|
+
"data",
|
|
38796
|
+
"pianificazione"
|
|
38797
|
+
]
|
|
38798
|
+
},
|
|
38799
|
+
{
|
|
38800
|
+
key: "pipette",
|
|
38801
|
+
group: "sensing",
|
|
38802
|
+
synonyms: [
|
|
38803
|
+
"qualita",
|
|
38804
|
+
"aria",
|
|
38805
|
+
"misura",
|
|
38806
|
+
"campione"
|
|
38807
|
+
]
|
|
38808
|
+
},
|
|
38809
|
+
{
|
|
38810
|
+
key: "fan",
|
|
38811
|
+
group: "sensing",
|
|
38812
|
+
synonyms: [
|
|
38813
|
+
"ventilatore",
|
|
38814
|
+
"ventola",
|
|
38815
|
+
"aria"
|
|
38816
|
+
]
|
|
38817
|
+
},
|
|
38818
|
+
{
|
|
38819
|
+
key: "bath",
|
|
38820
|
+
group: "sensing",
|
|
38821
|
+
synonyms: [
|
|
38822
|
+
"bagno",
|
|
38823
|
+
"vasca",
|
|
38824
|
+
"doccia"
|
|
38825
|
+
]
|
|
38826
|
+
},
|
|
38827
|
+
{
|
|
38828
|
+
key: "footprints",
|
|
38829
|
+
group: "sensing",
|
|
38830
|
+
synonyms: [
|
|
38831
|
+
"movimento",
|
|
38832
|
+
"passi",
|
|
38833
|
+
"presenza",
|
|
38834
|
+
"motion"
|
|
38835
|
+
]
|
|
38836
|
+
},
|
|
38837
|
+
{
|
|
38838
|
+
key: "alarm-smoke",
|
|
38839
|
+
group: "sensing",
|
|
38840
|
+
synonyms: [
|
|
38841
|
+
"fumo",
|
|
38842
|
+
"antincendio",
|
|
38843
|
+
"rilevatore"
|
|
38844
|
+
]
|
|
38845
|
+
},
|
|
38846
|
+
{
|
|
38847
|
+
key: "shield-alert",
|
|
38848
|
+
group: "sensing",
|
|
38849
|
+
synonyms: [
|
|
38850
|
+
"manomissione",
|
|
38851
|
+
"allarme",
|
|
38852
|
+
"tamper",
|
|
38853
|
+
"avviso"
|
|
38854
|
+
]
|
|
38855
|
+
},
|
|
38856
|
+
{
|
|
38857
|
+
key: "vibrate",
|
|
38858
|
+
group: "sensing",
|
|
38859
|
+
synonyms: [
|
|
38860
|
+
"vibrazione",
|
|
38861
|
+
"urto",
|
|
38862
|
+
"tamper"
|
|
38863
|
+
]
|
|
38864
|
+
},
|
|
38865
|
+
{
|
|
38866
|
+
key: "wifi",
|
|
38867
|
+
group: "sensing",
|
|
38868
|
+
synonyms: [
|
|
38869
|
+
"wifi",
|
|
38870
|
+
"rete",
|
|
38871
|
+
"segnale"
|
|
38872
|
+
]
|
|
38873
|
+
},
|
|
38874
|
+
{
|
|
38875
|
+
key: "wifi-off",
|
|
38876
|
+
group: "sensing",
|
|
38877
|
+
synonyms: [
|
|
38878
|
+
"wifi",
|
|
38879
|
+
"assente",
|
|
38880
|
+
"offline",
|
|
38881
|
+
"disconnesso"
|
|
38882
|
+
]
|
|
38883
|
+
},
|
|
38884
|
+
{
|
|
38885
|
+
key: "volume-2",
|
|
38886
|
+
group: "sensing",
|
|
38887
|
+
synonyms: [
|
|
38888
|
+
"volume",
|
|
38889
|
+
"audio",
|
|
38890
|
+
"altoparlante",
|
|
38891
|
+
"suono"
|
|
38892
|
+
]
|
|
38893
|
+
},
|
|
38894
|
+
{
|
|
38895
|
+
key: "volume-x",
|
|
38896
|
+
group: "sensing",
|
|
38897
|
+
synonyms: [
|
|
38898
|
+
"muto",
|
|
38899
|
+
"silenziato",
|
|
38900
|
+
"volume",
|
|
38901
|
+
"spento"
|
|
38902
|
+
]
|
|
38903
|
+
},
|
|
38904
|
+
{
|
|
38905
|
+
key: "heart",
|
|
38906
|
+
group: "sensing",
|
|
38907
|
+
synonyms: [
|
|
38908
|
+
"cuore",
|
|
38909
|
+
"preferito",
|
|
38910
|
+
"salute"
|
|
38911
|
+
]
|
|
38912
|
+
},
|
|
38913
|
+
{
|
|
38914
|
+
key: "heart-pulse",
|
|
38915
|
+
group: "sensing",
|
|
38916
|
+
synonyms: [
|
|
38917
|
+
"battito",
|
|
38918
|
+
"salute",
|
|
38919
|
+
"monitoraggio"
|
|
38920
|
+
]
|
|
38921
|
+
},
|
|
38922
|
+
{
|
|
38923
|
+
key: "star",
|
|
38924
|
+
group: "sensing",
|
|
38925
|
+
synonyms: [
|
|
38926
|
+
"preferito",
|
|
38927
|
+
"stella",
|
|
38928
|
+
"importante"
|
|
38929
|
+
]
|
|
38930
|
+
},
|
|
38931
|
+
{
|
|
38932
|
+
key: "sparkles",
|
|
38933
|
+
group: "sensing",
|
|
38934
|
+
synonyms: [
|
|
38935
|
+
"automatico",
|
|
38936
|
+
"intelligente",
|
|
38937
|
+
"ai",
|
|
38938
|
+
"scena"
|
|
38939
|
+
]
|
|
38940
|
+
},
|
|
38941
|
+
{
|
|
38942
|
+
key: "house",
|
|
38943
|
+
group: "place",
|
|
38944
|
+
synonyms: [
|
|
38945
|
+
"casa",
|
|
38946
|
+
"abitazione",
|
|
38947
|
+
"home"
|
|
38948
|
+
]
|
|
38949
|
+
},
|
|
38950
|
+
{
|
|
38951
|
+
key: "sofa",
|
|
38952
|
+
group: "place",
|
|
38953
|
+
synonyms: [
|
|
38954
|
+
"salone",
|
|
38955
|
+
"soggiorno",
|
|
38956
|
+
"divano",
|
|
38957
|
+
"living"
|
|
38958
|
+
]
|
|
38959
|
+
},
|
|
38960
|
+
{
|
|
38961
|
+
key: "bed-double",
|
|
38962
|
+
group: "place",
|
|
38963
|
+
synonyms: [
|
|
38964
|
+
"camera",
|
|
38965
|
+
"letto",
|
|
38966
|
+
"matrimoniale"
|
|
38967
|
+
]
|
|
38968
|
+
},
|
|
38969
|
+
{
|
|
38970
|
+
key: "bed",
|
|
38971
|
+
group: "place",
|
|
38972
|
+
synonyms: [
|
|
38973
|
+
"cameretta",
|
|
38974
|
+
"letto",
|
|
38975
|
+
"singolo"
|
|
38976
|
+
]
|
|
38977
|
+
},
|
|
38978
|
+
{
|
|
38979
|
+
key: "warehouse",
|
|
38980
|
+
group: "place",
|
|
38981
|
+
synonyms: [
|
|
38982
|
+
"magazzino",
|
|
38983
|
+
"capanno",
|
|
38984
|
+
"deposito",
|
|
38985
|
+
"cantina"
|
|
38986
|
+
]
|
|
38987
|
+
},
|
|
38988
|
+
{
|
|
38989
|
+
key: "trees",
|
|
38990
|
+
group: "place",
|
|
38991
|
+
synonyms: [
|
|
38992
|
+
"giardino",
|
|
38993
|
+
"alberi",
|
|
38994
|
+
"esterno",
|
|
38995
|
+
"parco"
|
|
38996
|
+
]
|
|
38997
|
+
},
|
|
38998
|
+
{
|
|
38999
|
+
key: "tree-pine",
|
|
39000
|
+
group: "place",
|
|
39001
|
+
synonyms: [
|
|
39002
|
+
"albero",
|
|
39003
|
+
"pino",
|
|
39004
|
+
"giardino"
|
|
39005
|
+
]
|
|
39006
|
+
},
|
|
39007
|
+
{
|
|
39008
|
+
key: "fence",
|
|
39009
|
+
group: "place",
|
|
39010
|
+
synonyms: [
|
|
39011
|
+
"recinto",
|
|
39012
|
+
"cancello",
|
|
39013
|
+
"staccionata",
|
|
39014
|
+
"perimetro"
|
|
39015
|
+
]
|
|
39016
|
+
},
|
|
39017
|
+
{
|
|
39018
|
+
key: "store",
|
|
39019
|
+
group: "place",
|
|
39020
|
+
synonyms: [
|
|
39021
|
+
"negozio",
|
|
39022
|
+
"ingresso",
|
|
39023
|
+
"vetrina"
|
|
39024
|
+
]
|
|
39025
|
+
},
|
|
39026
|
+
{
|
|
39027
|
+
key: "building",
|
|
39028
|
+
group: "place",
|
|
39029
|
+
synonyms: ["edificio", "palazzo"]
|
|
39030
|
+
},
|
|
39031
|
+
{
|
|
39032
|
+
key: "building-2",
|
|
39033
|
+
group: "place",
|
|
39034
|
+
synonyms: [
|
|
39035
|
+
"edificio",
|
|
39036
|
+
"uffici",
|
|
39037
|
+
"condominio"
|
|
39038
|
+
]
|
|
39039
|
+
},
|
|
39040
|
+
{
|
|
39041
|
+
key: "mountain",
|
|
39042
|
+
group: "place",
|
|
39043
|
+
synonyms: [
|
|
39044
|
+
"montagna",
|
|
39045
|
+
"esterno",
|
|
39046
|
+
"panorama"
|
|
39047
|
+
]
|
|
39048
|
+
},
|
|
39049
|
+
{
|
|
39050
|
+
key: "umbrella",
|
|
39051
|
+
group: "place",
|
|
39052
|
+
synonyms: [
|
|
39053
|
+
"tenda",
|
|
39054
|
+
"markise",
|
|
39055
|
+
"ombrellone",
|
|
39056
|
+
"pergola",
|
|
39057
|
+
"veranda"
|
|
39058
|
+
]
|
|
39059
|
+
},
|
|
39060
|
+
{
|
|
39061
|
+
key: "dumbbell",
|
|
39062
|
+
group: "place",
|
|
39063
|
+
synonyms: ["palestra", "attrezzi"]
|
|
39064
|
+
},
|
|
39065
|
+
{
|
|
39066
|
+
key: "wrench",
|
|
39067
|
+
group: "place",
|
|
39068
|
+
synonyms: [
|
|
39069
|
+
"officina",
|
|
39070
|
+
"manutenzione",
|
|
39071
|
+
"attrezzi",
|
|
39072
|
+
"utenze"
|
|
39073
|
+
]
|
|
39074
|
+
},
|
|
39075
|
+
{
|
|
39076
|
+
key: "hammer",
|
|
39077
|
+
group: "place",
|
|
39078
|
+
synonyms: [
|
|
39079
|
+
"officina",
|
|
39080
|
+
"martello",
|
|
39081
|
+
"lavori"
|
|
39082
|
+
]
|
|
39083
|
+
},
|
|
39084
|
+
{
|
|
39085
|
+
key: "drill",
|
|
39086
|
+
group: "place",
|
|
39087
|
+
synonyms: [
|
|
39088
|
+
"officina",
|
|
39089
|
+
"trapano",
|
|
39090
|
+
"lavori"
|
|
39091
|
+
]
|
|
39092
|
+
},
|
|
39093
|
+
{
|
|
39094
|
+
key: "waves-ladder",
|
|
39095
|
+
group: "place",
|
|
39096
|
+
synonyms: ["piscina", "vasca"]
|
|
39097
|
+
},
|
|
39098
|
+
{
|
|
39099
|
+
key: "circle-parking",
|
|
39100
|
+
group: "place",
|
|
39101
|
+
synonyms: [
|
|
39102
|
+
"parcheggio",
|
|
39103
|
+
"posto",
|
|
39104
|
+
"auto",
|
|
39105
|
+
"garage"
|
|
39106
|
+
]
|
|
39107
|
+
},
|
|
39108
|
+
{
|
|
39109
|
+
key: "route",
|
|
39110
|
+
group: "place",
|
|
39111
|
+
synonyms: [
|
|
39112
|
+
"percorso",
|
|
39113
|
+
"strada",
|
|
39114
|
+
"vialetto",
|
|
39115
|
+
"tragitto"
|
|
39116
|
+
]
|
|
39117
|
+
},
|
|
39118
|
+
{
|
|
39119
|
+
key: "car",
|
|
39120
|
+
group: "life",
|
|
39121
|
+
synonyms: [
|
|
39122
|
+
"auto",
|
|
39123
|
+
"macchina",
|
|
39124
|
+
"veicolo",
|
|
39125
|
+
"parcheggio"
|
|
39126
|
+
]
|
|
39127
|
+
},
|
|
39128
|
+
{
|
|
39129
|
+
key: "bike",
|
|
39130
|
+
group: "life",
|
|
39131
|
+
synonyms: [
|
|
39132
|
+
"bicicletta",
|
|
39133
|
+
"bici",
|
|
39134
|
+
"moto"
|
|
39135
|
+
]
|
|
39136
|
+
},
|
|
39137
|
+
{
|
|
39138
|
+
key: "bus",
|
|
39139
|
+
group: "life",
|
|
39140
|
+
synonyms: ["autobus", "pullman"]
|
|
39141
|
+
},
|
|
39142
|
+
{
|
|
39143
|
+
key: "truck",
|
|
39144
|
+
group: "life",
|
|
39145
|
+
synonyms: [
|
|
39146
|
+
"camion",
|
|
39147
|
+
"furgone",
|
|
39148
|
+
"consegna"
|
|
39149
|
+
]
|
|
39150
|
+
},
|
|
39151
|
+
{
|
|
39152
|
+
key: "plane",
|
|
39153
|
+
group: "life",
|
|
39154
|
+
synonyms: ["aereo"]
|
|
39155
|
+
},
|
|
39156
|
+
{
|
|
39157
|
+
key: "train-front",
|
|
39158
|
+
group: "life",
|
|
39159
|
+
synonyms: ["treno"]
|
|
39160
|
+
},
|
|
39161
|
+
{
|
|
39162
|
+
key: "sailboat",
|
|
39163
|
+
group: "life",
|
|
39164
|
+
synonyms: ["barca", "imbarcazione"]
|
|
39165
|
+
},
|
|
39166
|
+
{
|
|
39167
|
+
key: "dog",
|
|
39168
|
+
group: "life",
|
|
39169
|
+
synonyms: ["cane", "animale"]
|
|
39170
|
+
},
|
|
39171
|
+
{
|
|
39172
|
+
key: "cat",
|
|
39173
|
+
group: "life",
|
|
39174
|
+
synonyms: ["gatto", "animale"]
|
|
39175
|
+
},
|
|
39176
|
+
{
|
|
39177
|
+
key: "bird",
|
|
39178
|
+
group: "life",
|
|
39179
|
+
synonyms: ["uccello", "animale"]
|
|
39180
|
+
},
|
|
39181
|
+
{
|
|
39182
|
+
key: "paw-print",
|
|
39183
|
+
group: "life",
|
|
39184
|
+
synonyms: [
|
|
39185
|
+
"animale",
|
|
39186
|
+
"zampa",
|
|
39187
|
+
"pet"
|
|
39188
|
+
]
|
|
39189
|
+
},
|
|
39190
|
+
{
|
|
39191
|
+
key: "baby",
|
|
39192
|
+
group: "life",
|
|
39193
|
+
synonyms: [
|
|
39194
|
+
"neonato",
|
|
39195
|
+
"bimbo",
|
|
39196
|
+
"cameretta",
|
|
39197
|
+
"babymonitor"
|
|
39198
|
+
]
|
|
39199
|
+
},
|
|
39200
|
+
{
|
|
39201
|
+
key: "user",
|
|
39202
|
+
group: "life",
|
|
39203
|
+
synonyms: ["persona", "utente"]
|
|
39204
|
+
},
|
|
39205
|
+
{
|
|
39206
|
+
key: "users",
|
|
39207
|
+
group: "life",
|
|
39208
|
+
synonyms: [
|
|
39209
|
+
"persone",
|
|
39210
|
+
"gruppo",
|
|
39211
|
+
"famiglia"
|
|
39212
|
+
]
|
|
39213
|
+
},
|
|
39214
|
+
{
|
|
39215
|
+
key: "person-standing",
|
|
39216
|
+
group: "life",
|
|
39217
|
+
synonyms: [
|
|
39218
|
+
"persona",
|
|
39219
|
+
"piedi",
|
|
39220
|
+
"presenza"
|
|
39221
|
+
]
|
|
39222
|
+
}
|
|
39223
|
+
];
|
|
39224
|
+
DISPLAY_ICON_VOCABULARY.map((e) => e.key);
|
|
39225
|
+
DISPLAY_ICON_VOCABULARY.filter((e) => e.aliasOf === void 0).map((e) => e.key);
|
|
39226
|
+
new Map(DISPLAY_ICON_VOCABULARY.map((e) => [e.key, e]));
|
|
36490
39227
|
/** Reject after `ms`; always clears its own timer. */
|
|
36491
39228
|
async function withTimeout$1(promise, ms, label) {
|
|
36492
39229
|
let timer;
|
|
@@ -36694,6 +39431,12 @@ function createRuntimeStateBridge(params) {
|
|
|
36694
39431
|
};
|
|
36695
39432
|
}
|
|
36696
39433
|
Object.freeze({
|
|
39434
|
+
"accessories.getStatus": {
|
|
39435
|
+
capName: "accessories",
|
|
39436
|
+
capScope: "device",
|
|
39437
|
+
addonId: null,
|
|
39438
|
+
access: "view"
|
|
39439
|
+
},
|
|
36697
39440
|
"accessories.setChildHidden": {
|
|
36698
39441
|
capName: "accessories",
|
|
36699
39442
|
capScope: "device",
|
|
@@ -36976,6 +39719,12 @@ Object.freeze({
|
|
|
36976
39719
|
addonId: null,
|
|
36977
39720
|
access: "view"
|
|
36978
39721
|
},
|
|
39722
|
+
"airQualitySensor.getStatus": {
|
|
39723
|
+
capName: "air-quality-sensor",
|
|
39724
|
+
capScope: "device",
|
|
39725
|
+
addonId: null,
|
|
39726
|
+
access: "view"
|
|
39727
|
+
},
|
|
36979
39728
|
"alarmPanel.arm": {
|
|
36980
39729
|
capName: "alarm-panel",
|
|
36981
39730
|
capScope: "device",
|
|
@@ -36988,6 +39737,12 @@ Object.freeze({
|
|
|
36988
39737
|
addonId: null,
|
|
36989
39738
|
access: "create"
|
|
36990
39739
|
},
|
|
39740
|
+
"alarmPanel.getStatus": {
|
|
39741
|
+
capName: "alarm-panel",
|
|
39742
|
+
capScope: "device",
|
|
39743
|
+
addonId: null,
|
|
39744
|
+
access: "view"
|
|
39745
|
+
},
|
|
36991
39746
|
"alarmPanel.trigger": {
|
|
36992
39747
|
capName: "alarm-panel",
|
|
36993
39748
|
capScope: "device",
|
|
@@ -37036,6 +39791,30 @@ Object.freeze({
|
|
|
37036
39791
|
addonId: null,
|
|
37037
39792
|
access: "create"
|
|
37038
39793
|
},
|
|
39794
|
+
"ambientLightSensor.getStatus": {
|
|
39795
|
+
capName: "ambient-light-sensor",
|
|
39796
|
+
capScope: "device",
|
|
39797
|
+
addonId: null,
|
|
39798
|
+
access: "view"
|
|
39799
|
+
},
|
|
39800
|
+
"audioAnalysis.applyDeviceSettingsPatch": {
|
|
39801
|
+
capName: "audio-analysis",
|
|
39802
|
+
capScope: "device",
|
|
39803
|
+
addonId: null,
|
|
39804
|
+
access: "create"
|
|
39805
|
+
},
|
|
39806
|
+
"audioAnalysis.getDeviceLiveContribution": {
|
|
39807
|
+
capName: "audio-analysis",
|
|
39808
|
+
capScope: "device",
|
|
39809
|
+
addonId: null,
|
|
39810
|
+
access: "view"
|
|
39811
|
+
},
|
|
39812
|
+
"audioAnalysis.getDeviceSettingsContribution": {
|
|
39813
|
+
capName: "audio-analysis",
|
|
39814
|
+
capScope: "device",
|
|
39815
|
+
addonId: null,
|
|
39816
|
+
access: "view"
|
|
39817
|
+
},
|
|
37039
39818
|
"audioAnalysis.resolveDeviceSettings": {
|
|
37040
39819
|
capName: "audio-analysis",
|
|
37041
39820
|
capScope: "device",
|
|
@@ -37198,6 +39977,12 @@ Object.freeze({
|
|
|
37198
39977
|
addonId: null,
|
|
37199
39978
|
access: "create"
|
|
37200
39979
|
},
|
|
39980
|
+
"automationControl.getStatus": {
|
|
39981
|
+
capName: "automation-control",
|
|
39982
|
+
capScope: "device",
|
|
39983
|
+
addonId: null,
|
|
39984
|
+
access: "view"
|
|
39985
|
+
},
|
|
37201
39986
|
"automationControl.trigger": {
|
|
37202
39987
|
capName: "automation-control",
|
|
37203
39988
|
capScope: "device",
|
|
@@ -37294,12 +40079,30 @@ Object.freeze({
|
|
|
37294
40079
|
addonId: null,
|
|
37295
40080
|
access: "create"
|
|
37296
40081
|
},
|
|
40082
|
+
"battery.getStatus": {
|
|
40083
|
+
capName: "battery",
|
|
40084
|
+
capScope: "device",
|
|
40085
|
+
addonId: null,
|
|
40086
|
+
access: "view"
|
|
40087
|
+
},
|
|
37297
40088
|
"battery.wakeForStream": {
|
|
37298
40089
|
capName: "battery",
|
|
37299
40090
|
capScope: "device",
|
|
37300
40091
|
addonId: null,
|
|
37301
40092
|
access: "create"
|
|
37302
40093
|
},
|
|
40094
|
+
"binary.getStatus": {
|
|
40095
|
+
capName: "binary",
|
|
40096
|
+
capScope: "device",
|
|
40097
|
+
addonId: null,
|
|
40098
|
+
access: "view"
|
|
40099
|
+
},
|
|
40100
|
+
"brightness.getStatus": {
|
|
40101
|
+
capName: "brightness",
|
|
40102
|
+
capScope: "device",
|
|
40103
|
+
addonId: null,
|
|
40104
|
+
access: "view"
|
|
40105
|
+
},
|
|
37303
40106
|
"brightness.setBrightness": {
|
|
37304
40107
|
capName: "brightness",
|
|
37305
40108
|
capScope: "device",
|
|
@@ -37414,6 +40217,30 @@ Object.freeze({
|
|
|
37414
40217
|
addonId: null,
|
|
37415
40218
|
access: "view"
|
|
37416
40219
|
},
|
|
40220
|
+
"cameraCredentials.getStatus": {
|
|
40221
|
+
capName: "camera-credentials",
|
|
40222
|
+
capScope: "device",
|
|
40223
|
+
addonId: null,
|
|
40224
|
+
access: "view"
|
|
40225
|
+
},
|
|
40226
|
+
"cameraGridLayout.applyDeviceSettingsPatch": {
|
|
40227
|
+
capName: "camera-grid-layout",
|
|
40228
|
+
capScope: "device",
|
|
40229
|
+
addonId: null,
|
|
40230
|
+
access: "create"
|
|
40231
|
+
},
|
|
40232
|
+
"cameraGridLayout.getDeviceLiveContribution": {
|
|
40233
|
+
capName: "camera-grid-layout",
|
|
40234
|
+
capScope: "device",
|
|
40235
|
+
addonId: null,
|
|
40236
|
+
access: "view"
|
|
40237
|
+
},
|
|
40238
|
+
"cameraGridLayout.getDeviceSettingsContribution": {
|
|
40239
|
+
capName: "camera-grid-layout",
|
|
40240
|
+
capScope: "device",
|
|
40241
|
+
addonId: null,
|
|
40242
|
+
access: "view"
|
|
40243
|
+
},
|
|
37417
40244
|
"cameraGridLayout.getLayout": {
|
|
37418
40245
|
capName: "camera-grid-layout",
|
|
37419
40246
|
capScope: "device",
|
|
@@ -37426,6 +40253,24 @@ Object.freeze({
|
|
|
37426
40253
|
addonId: null,
|
|
37427
40254
|
access: "create"
|
|
37428
40255
|
},
|
|
40256
|
+
"cameraPipelineConfig.applyDeviceSettingsPatch": {
|
|
40257
|
+
capName: "camera-pipeline-config",
|
|
40258
|
+
capScope: "device",
|
|
40259
|
+
addonId: null,
|
|
40260
|
+
access: "create"
|
|
40261
|
+
},
|
|
40262
|
+
"cameraPipelineConfig.getDeviceLiveContribution": {
|
|
40263
|
+
capName: "camera-pipeline-config",
|
|
40264
|
+
capScope: "device",
|
|
40265
|
+
addonId: null,
|
|
40266
|
+
access: "view"
|
|
40267
|
+
},
|
|
40268
|
+
"cameraPipelineConfig.getDeviceSettingsContribution": {
|
|
40269
|
+
capName: "camera-pipeline-config",
|
|
40270
|
+
capScope: "device",
|
|
40271
|
+
addonId: null,
|
|
40272
|
+
access: "view"
|
|
40273
|
+
},
|
|
37429
40274
|
"cameraStreams.getBrokerStreams": {
|
|
37430
40275
|
capName: "camera-streams",
|
|
37431
40276
|
capScope: "device",
|
|
@@ -37456,6 +40301,18 @@ Object.freeze({
|
|
|
37456
40301
|
addonId: null,
|
|
37457
40302
|
access: "view"
|
|
37458
40303
|
},
|
|
40304
|
+
"carbonMonoxide.getStatus": {
|
|
40305
|
+
capName: "carbon-monoxide",
|
|
40306
|
+
capScope: "device",
|
|
40307
|
+
addonId: null,
|
|
40308
|
+
access: "view"
|
|
40309
|
+
},
|
|
40310
|
+
"climateControl.getStatus": {
|
|
40311
|
+
capName: "climate-control",
|
|
40312
|
+
capScope: "device",
|
|
40313
|
+
addonId: null,
|
|
40314
|
+
access: "view"
|
|
40315
|
+
},
|
|
37459
40316
|
"climateControl.setFanMode": {
|
|
37460
40317
|
capName: "climate-control",
|
|
37461
40318
|
capScope: "device",
|
|
@@ -37504,6 +40361,12 @@ Object.freeze({
|
|
|
37504
40361
|
addonId: null,
|
|
37505
40362
|
access: "create"
|
|
37506
40363
|
},
|
|
40364
|
+
"color.getStatus": {
|
|
40365
|
+
capName: "color",
|
|
40366
|
+
capScope: "device",
|
|
40367
|
+
addonId: null,
|
|
40368
|
+
access: "view"
|
|
40369
|
+
},
|
|
37507
40370
|
"color.setColor": {
|
|
37508
40371
|
capName: "color",
|
|
37509
40372
|
capScope: "device",
|
|
@@ -37522,12 +40385,36 @@ Object.freeze({
|
|
|
37522
40385
|
addonId: null,
|
|
37523
40386
|
access: "create"
|
|
37524
40387
|
},
|
|
40388
|
+
"connectivity.getStatus": {
|
|
40389
|
+
capName: "connectivity",
|
|
40390
|
+
capScope: "device",
|
|
40391
|
+
addonId: null,
|
|
40392
|
+
access: "view"
|
|
40393
|
+
},
|
|
40394
|
+
"consumables.getStatus": {
|
|
40395
|
+
capName: "consumables",
|
|
40396
|
+
capScope: "device",
|
|
40397
|
+
addonId: null,
|
|
40398
|
+
access: "view"
|
|
40399
|
+
},
|
|
37525
40400
|
"consumables.reset": {
|
|
37526
40401
|
capName: "consumables",
|
|
37527
40402
|
capScope: "device",
|
|
37528
40403
|
addonId: null,
|
|
37529
40404
|
access: "delete"
|
|
37530
40405
|
},
|
|
40406
|
+
"contact.getStatus": {
|
|
40407
|
+
capName: "contact",
|
|
40408
|
+
capScope: "device",
|
|
40409
|
+
addonId: null,
|
|
40410
|
+
access: "view"
|
|
40411
|
+
},
|
|
40412
|
+
"control.getStatus": {
|
|
40413
|
+
capName: "control",
|
|
40414
|
+
capScope: "device",
|
|
40415
|
+
addonId: null,
|
|
40416
|
+
access: "view"
|
|
40417
|
+
},
|
|
37531
40418
|
"control.setValue": {
|
|
37532
40419
|
capName: "control",
|
|
37533
40420
|
capScope: "device",
|
|
@@ -37594,6 +40481,12 @@ Object.freeze({
|
|
|
37594
40481
|
addonId: null,
|
|
37595
40482
|
access: "create"
|
|
37596
40483
|
},
|
|
40484
|
+
"cover.getStatus": {
|
|
40485
|
+
capName: "cover",
|
|
40486
|
+
capScope: "device",
|
|
40487
|
+
addonId: null,
|
|
40488
|
+
access: "view"
|
|
40489
|
+
},
|
|
37597
40490
|
"cover.open": {
|
|
37598
40491
|
capName: "cover",
|
|
37599
40492
|
capScope: "device",
|
|
@@ -37726,6 +40619,12 @@ Object.freeze({
|
|
|
37726
40619
|
addonId: null,
|
|
37727
40620
|
access: "view"
|
|
37728
40621
|
},
|
|
40622
|
+
"dayNight.getStatus": {
|
|
40623
|
+
capName: "day-night",
|
|
40624
|
+
capScope: "device",
|
|
40625
|
+
addonId: null,
|
|
40626
|
+
access: "view"
|
|
40627
|
+
},
|
|
37729
40628
|
"dayNight.setSettings": {
|
|
37730
40629
|
capName: "day-night",
|
|
37731
40630
|
capScope: "device",
|
|
@@ -37816,6 +40715,30 @@ Object.freeze({
|
|
|
37816
40715
|
addonId: null,
|
|
37817
40716
|
access: "view"
|
|
37818
40717
|
},
|
|
40718
|
+
"detectionPipeline.applyDeviceSettingsPatch": {
|
|
40719
|
+
capName: "detection-pipeline",
|
|
40720
|
+
capScope: "device",
|
|
40721
|
+
addonId: null,
|
|
40722
|
+
access: "create"
|
|
40723
|
+
},
|
|
40724
|
+
"detectionPipeline.getDeviceLiveContribution": {
|
|
40725
|
+
capName: "detection-pipeline",
|
|
40726
|
+
capScope: "device",
|
|
40727
|
+
addonId: null,
|
|
40728
|
+
access: "view"
|
|
40729
|
+
},
|
|
40730
|
+
"detectionPipeline.getDeviceSettingsContribution": {
|
|
40731
|
+
capName: "detection-pipeline",
|
|
40732
|
+
capScope: "device",
|
|
40733
|
+
addonId: null,
|
|
40734
|
+
access: "view"
|
|
40735
|
+
},
|
|
40736
|
+
"deviceAdminLink.getAdminLink": {
|
|
40737
|
+
capName: "device-admin-link",
|
|
40738
|
+
capScope: "device",
|
|
40739
|
+
addonId: null,
|
|
40740
|
+
access: "view"
|
|
40741
|
+
},
|
|
37819
40742
|
"deviceAdoption.adopt": {
|
|
37820
40743
|
capName: "device-adoption",
|
|
37821
40744
|
capScope: "system",
|
|
@@ -37828,6 +40751,12 @@ Object.freeze({
|
|
|
37828
40751
|
addonId: null,
|
|
37829
40752
|
access: "view"
|
|
37830
40753
|
},
|
|
40754
|
+
"deviceAdoption.getStatus": {
|
|
40755
|
+
capName: "device-adoption",
|
|
40756
|
+
capScope: "system",
|
|
40757
|
+
addonId: null,
|
|
40758
|
+
access: "view"
|
|
40759
|
+
},
|
|
37831
40760
|
"deviceAdoption.listCandidateFilters": {
|
|
37832
40761
|
capName: "device-adoption",
|
|
37833
40762
|
capScope: "system",
|
|
@@ -37864,6 +40793,12 @@ Object.freeze({
|
|
|
37864
40793
|
addonId: null,
|
|
37865
40794
|
access: "create"
|
|
37866
40795
|
},
|
|
40796
|
+
"deviceDiscovery.getStatus": {
|
|
40797
|
+
capName: "device-discovery",
|
|
40798
|
+
capScope: "device",
|
|
40799
|
+
addonId: null,
|
|
40800
|
+
access: "view"
|
|
40801
|
+
},
|
|
37867
40802
|
"deviceDiscovery.listDiscovered": {
|
|
37868
40803
|
capName: "device-discovery",
|
|
37869
40804
|
capScope: "device",
|
|
@@ -37882,12 +40817,30 @@ Object.freeze({
|
|
|
37882
40817
|
addonId: null,
|
|
37883
40818
|
access: "create"
|
|
37884
40819
|
},
|
|
40820
|
+
"deviceExport.applyDeviceSettingsPatch": {
|
|
40821
|
+
capName: "device-export",
|
|
40822
|
+
capScope: "system",
|
|
40823
|
+
addonId: null,
|
|
40824
|
+
access: "create"
|
|
40825
|
+
},
|
|
37885
40826
|
"deviceExport.exposeDevice": {
|
|
37886
40827
|
capName: "device-export",
|
|
37887
40828
|
capScope: "system",
|
|
37888
40829
|
addonId: null,
|
|
37889
40830
|
access: "create"
|
|
37890
40831
|
},
|
|
40832
|
+
"deviceExport.getDeviceLiveContribution": {
|
|
40833
|
+
capName: "device-export",
|
|
40834
|
+
capScope: "system",
|
|
40835
|
+
addonId: null,
|
|
40836
|
+
access: "view"
|
|
40837
|
+
},
|
|
40838
|
+
"deviceExport.getDeviceSettingsContribution": {
|
|
40839
|
+
capName: "device-export",
|
|
40840
|
+
capScope: "system",
|
|
40841
|
+
addonId: null,
|
|
40842
|
+
access: "view"
|
|
40843
|
+
},
|
|
37891
40844
|
"deviceExport.getStatus": {
|
|
37892
40845
|
capName: "device-export",
|
|
37893
40846
|
capScope: "system",
|
|
@@ -37984,6 +40937,12 @@ Object.freeze({
|
|
|
37984
40937
|
addonId: null,
|
|
37985
40938
|
access: "create"
|
|
37986
40939
|
},
|
|
40940
|
+
"deviceManager.applyDeviceSettingsPatch": {
|
|
40941
|
+
capName: "device-manager",
|
|
40942
|
+
capScope: "system",
|
|
40943
|
+
addonId: null,
|
|
40944
|
+
access: "create"
|
|
40945
|
+
},
|
|
37987
40946
|
"deviceManager.applyInitialMeta": {
|
|
37988
40947
|
capName: "device-manager",
|
|
37989
40948
|
capScope: "system",
|
|
@@ -38086,6 +41045,12 @@ Object.freeze({
|
|
|
38086
41045
|
addonId: null,
|
|
38087
41046
|
access: "view"
|
|
38088
41047
|
},
|
|
41048
|
+
"deviceManager.getDeviceLiveContribution": {
|
|
41049
|
+
capName: "device-manager",
|
|
41050
|
+
capScope: "system",
|
|
41051
|
+
addonId: null,
|
|
41052
|
+
access: "view"
|
|
41053
|
+
},
|
|
38089
41054
|
"deviceManager.getDeviceLiveInfoAggregate": {
|
|
38090
41055
|
capName: "device-manager",
|
|
38091
41056
|
capScope: "system",
|
|
@@ -38098,6 +41063,12 @@ Object.freeze({
|
|
|
38098
41063
|
addonId: null,
|
|
38099
41064
|
access: "view"
|
|
38100
41065
|
},
|
|
41066
|
+
"deviceManager.getDeviceSettingsContribution": {
|
|
41067
|
+
capName: "device-manager",
|
|
41068
|
+
capScope: "system",
|
|
41069
|
+
addonId: null,
|
|
41070
|
+
access: "view"
|
|
41071
|
+
},
|
|
38101
41072
|
"deviceManager.getDeviceStatusAggregate": {
|
|
38102
41073
|
capName: "device-manager",
|
|
38103
41074
|
capScope: "system",
|
|
@@ -38590,6 +41561,36 @@ Object.freeze({
|
|
|
38590
41561
|
addonId: null,
|
|
38591
41562
|
access: "create"
|
|
38592
41563
|
},
|
|
41564
|
+
"deviceStatus.getStatus": {
|
|
41565
|
+
capName: "device-status",
|
|
41566
|
+
capScope: "device",
|
|
41567
|
+
addonId: null,
|
|
41568
|
+
access: "view"
|
|
41569
|
+
},
|
|
41570
|
+
"doorbell.applyDeviceSettingsPatch": {
|
|
41571
|
+
capName: "doorbell",
|
|
41572
|
+
capScope: "device",
|
|
41573
|
+
addonId: null,
|
|
41574
|
+
access: "create"
|
|
41575
|
+
},
|
|
41576
|
+
"doorbell.getDeviceLiveContribution": {
|
|
41577
|
+
capName: "doorbell",
|
|
41578
|
+
capScope: "device",
|
|
41579
|
+
addonId: null,
|
|
41580
|
+
access: "view"
|
|
41581
|
+
},
|
|
41582
|
+
"doorbell.getDeviceSettingsContribution": {
|
|
41583
|
+
capName: "doorbell",
|
|
41584
|
+
capScope: "device",
|
|
41585
|
+
addonId: null,
|
|
41586
|
+
access: "view"
|
|
41587
|
+
},
|
|
41588
|
+
"doorbell.getStatus": {
|
|
41589
|
+
capName: "doorbell",
|
|
41590
|
+
capScope: "device",
|
|
41591
|
+
addonId: null,
|
|
41592
|
+
access: "view"
|
|
41593
|
+
},
|
|
38593
41594
|
"embeddingEncoder.encode": {
|
|
38594
41595
|
capName: "embedding-encoder",
|
|
38595
41596
|
capScope: "system",
|
|
@@ -38608,6 +41609,18 @@ Object.freeze({
|
|
|
38608
41609
|
addonId: null,
|
|
38609
41610
|
access: "view"
|
|
38610
41611
|
},
|
|
41612
|
+
"enumSensor.getStatus": {
|
|
41613
|
+
capName: "enum-sensor",
|
|
41614
|
+
capScope: "device",
|
|
41615
|
+
addonId: null,
|
|
41616
|
+
access: "view"
|
|
41617
|
+
},
|
|
41618
|
+
"eventEmitter.getStatus": {
|
|
41619
|
+
capName: "event-emitter",
|
|
41620
|
+
capScope: "device",
|
|
41621
|
+
addonId: null,
|
|
41622
|
+
access: "view"
|
|
41623
|
+
},
|
|
38611
41624
|
"events.getEventClipUrl": {
|
|
38612
41625
|
capName: "events",
|
|
38613
41626
|
capScope: "device",
|
|
@@ -38728,6 +41741,12 @@ Object.freeze({
|
|
|
38728
41741
|
addonId: null,
|
|
38729
41742
|
access: "view"
|
|
38730
41743
|
},
|
|
41744
|
+
"fanControl.getStatus": {
|
|
41745
|
+
capName: "fan-control",
|
|
41746
|
+
capScope: "device",
|
|
41747
|
+
addonId: null,
|
|
41748
|
+
access: "view"
|
|
41749
|
+
},
|
|
38731
41750
|
"fanControl.setDirection": {
|
|
38732
41751
|
capName: "fan-control",
|
|
38733
41752
|
capScope: "device",
|
|
@@ -38752,6 +41771,12 @@ Object.freeze({
|
|
|
38752
41771
|
addonId: null,
|
|
38753
41772
|
access: "create"
|
|
38754
41773
|
},
|
|
41774
|
+
"featureProbe.getStatus": {
|
|
41775
|
+
capName: "feature-probe",
|
|
41776
|
+
capScope: "device",
|
|
41777
|
+
addonId: null,
|
|
41778
|
+
access: "view"
|
|
41779
|
+
},
|
|
38755
41780
|
"filesystemBrowse.browse": {
|
|
38756
41781
|
capName: "filesystem-browse",
|
|
38757
41782
|
capScope: "system",
|
|
@@ -38770,6 +41795,24 @@ Object.freeze({
|
|
|
38770
41795
|
addonId: null,
|
|
38771
41796
|
access: "view"
|
|
38772
41797
|
},
|
|
41798
|
+
"flood.getStatus": {
|
|
41799
|
+
capName: "flood",
|
|
41800
|
+
capScope: "device",
|
|
41801
|
+
addonId: null,
|
|
41802
|
+
access: "view"
|
|
41803
|
+
},
|
|
41804
|
+
"gas.getStatus": {
|
|
41805
|
+
capName: "gas",
|
|
41806
|
+
capScope: "device",
|
|
41807
|
+
addonId: null,
|
|
41808
|
+
access: "view"
|
|
41809
|
+
},
|
|
41810
|
+
"humidifier.getStatus": {
|
|
41811
|
+
capName: "humidifier",
|
|
41812
|
+
capScope: "device",
|
|
41813
|
+
addonId: null,
|
|
41814
|
+
access: "view"
|
|
41815
|
+
},
|
|
38773
41816
|
"humidifier.setMode": {
|
|
38774
41817
|
capName: "humidifier",
|
|
38775
41818
|
capScope: "device",
|
|
@@ -38788,12 +41831,30 @@ Object.freeze({
|
|
|
38788
41831
|
addonId: null,
|
|
38789
41832
|
access: "create"
|
|
38790
41833
|
},
|
|
41834
|
+
"humiditySensor.getStatus": {
|
|
41835
|
+
capName: "humidity-sensor",
|
|
41836
|
+
capScope: "device",
|
|
41837
|
+
addonId: null,
|
|
41838
|
+
access: "view"
|
|
41839
|
+
},
|
|
41840
|
+
"image.getStatus": {
|
|
41841
|
+
capName: "image",
|
|
41842
|
+
capScope: "device",
|
|
41843
|
+
addonId: null,
|
|
41844
|
+
access: "view"
|
|
41845
|
+
},
|
|
38791
41846
|
"imageSettings.getOptions": {
|
|
38792
41847
|
capName: "image-settings",
|
|
38793
41848
|
capScope: "device",
|
|
38794
41849
|
addonId: null,
|
|
38795
41850
|
access: "view"
|
|
38796
41851
|
},
|
|
41852
|
+
"imageSettings.getStatus": {
|
|
41853
|
+
capName: "image-settings",
|
|
41854
|
+
capScope: "device",
|
|
41855
|
+
addonId: null,
|
|
41856
|
+
access: "view"
|
|
41857
|
+
},
|
|
38797
41858
|
"imageSettings.setSettings": {
|
|
38798
41859
|
capName: "image-settings",
|
|
38799
41860
|
capScope: "device",
|
|
@@ -38866,6 +41927,12 @@ Object.freeze({
|
|
|
38866
41927
|
addonId: null,
|
|
38867
41928
|
access: "create"
|
|
38868
41929
|
},
|
|
41930
|
+
"intercom.getStatus": {
|
|
41931
|
+
capName: "intercom",
|
|
41932
|
+
capScope: "device",
|
|
41933
|
+
addonId: null,
|
|
41934
|
+
access: "view"
|
|
41935
|
+
},
|
|
38869
41936
|
"intercom.handleAnswer": {
|
|
38870
41937
|
capName: "intercom",
|
|
38871
41938
|
capScope: "device",
|
|
@@ -38902,6 +41969,12 @@ Object.freeze({
|
|
|
38902
41969
|
addonId: null,
|
|
38903
41970
|
access: "create"
|
|
38904
41971
|
},
|
|
41972
|
+
"lawnMowerControl.getStatus": {
|
|
41973
|
+
capName: "lawn-mower-control",
|
|
41974
|
+
capScope: "device",
|
|
41975
|
+
addonId: null,
|
|
41976
|
+
access: "view"
|
|
41977
|
+
},
|
|
38905
41978
|
"lawnMowerControl.pause": {
|
|
38906
41979
|
capName: "lawn-mower-control",
|
|
38907
41980
|
capScope: "device",
|
|
@@ -39184,6 +42257,12 @@ Object.freeze({
|
|
|
39184
42257
|
addonId: null,
|
|
39185
42258
|
access: "create"
|
|
39186
42259
|
},
|
|
42260
|
+
"lockControl.getStatus": {
|
|
42261
|
+
capName: "lock-control",
|
|
42262
|
+
capScope: "device",
|
|
42263
|
+
addonId: null,
|
|
42264
|
+
access: "view"
|
|
42265
|
+
},
|
|
39187
42266
|
"lockControl.lock": {
|
|
39188
42267
|
capName: "lock-control",
|
|
39189
42268
|
capScope: "device",
|
|
@@ -39232,6 +42311,12 @@ Object.freeze({
|
|
|
39232
42311
|
addonId: null,
|
|
39233
42312
|
access: "view"
|
|
39234
42313
|
},
|
|
42314
|
+
"mediaPlayer.getStatus": {
|
|
42315
|
+
capName: "media-player",
|
|
42316
|
+
capScope: "device",
|
|
42317
|
+
addonId: null,
|
|
42318
|
+
access: "view"
|
|
42319
|
+
},
|
|
39235
42320
|
"mediaPlayer.next": {
|
|
39236
42321
|
capName: "media-player",
|
|
39237
42322
|
capScope: "device",
|
|
@@ -39424,6 +42509,12 @@ Object.freeze({
|
|
|
39424
42509
|
addonId: null,
|
|
39425
42510
|
access: "create"
|
|
39426
42511
|
},
|
|
42512
|
+
"motion.getStatus": {
|
|
42513
|
+
capName: "motion",
|
|
42514
|
+
capScope: "device",
|
|
42515
|
+
addonId: null,
|
|
42516
|
+
access: "view"
|
|
42517
|
+
},
|
|
39427
42518
|
"motion.isDetected": {
|
|
39428
42519
|
capName: "motion",
|
|
39429
42520
|
capScope: "device",
|
|
@@ -39436,6 +42527,24 @@ Object.freeze({
|
|
|
39436
42527
|
addonId: null,
|
|
39437
42528
|
access: "create"
|
|
39438
42529
|
},
|
|
42530
|
+
"motionDetection.applyDeviceSettingsPatch": {
|
|
42531
|
+
capName: "motion-detection",
|
|
42532
|
+
capScope: "device",
|
|
42533
|
+
addonId: null,
|
|
42534
|
+
access: "create"
|
|
42535
|
+
},
|
|
42536
|
+
"motionDetection.getDeviceLiveContribution": {
|
|
42537
|
+
capName: "motion-detection",
|
|
42538
|
+
capScope: "device",
|
|
42539
|
+
addonId: null,
|
|
42540
|
+
access: "view"
|
|
42541
|
+
},
|
|
42542
|
+
"motionDetection.getDeviceSettingsContribution": {
|
|
42543
|
+
capName: "motion-detection",
|
|
42544
|
+
capScope: "device",
|
|
42545
|
+
addonId: null,
|
|
42546
|
+
access: "view"
|
|
42547
|
+
},
|
|
39439
42548
|
"motionDetection.removeCamera": {
|
|
39440
42549
|
capName: "motion-detection",
|
|
39441
42550
|
capScope: "device",
|
|
@@ -39448,6 +42557,12 @@ Object.freeze({
|
|
|
39448
42557
|
addonId: null,
|
|
39449
42558
|
access: "delete"
|
|
39450
42559
|
},
|
|
42560
|
+
"motionTrigger.getStatus": {
|
|
42561
|
+
capName: "motion-trigger",
|
|
42562
|
+
capScope: "device",
|
|
42563
|
+
addonId: null,
|
|
42564
|
+
access: "view"
|
|
42565
|
+
},
|
|
39451
42566
|
"motionTrigger.setMotionTrigger": {
|
|
39452
42567
|
capName: "motion-trigger",
|
|
39453
42568
|
capScope: "device",
|
|
@@ -39460,6 +42575,12 @@ Object.freeze({
|
|
|
39460
42575
|
addonId: null,
|
|
39461
42576
|
access: "view"
|
|
39462
42577
|
},
|
|
42578
|
+
"motionZones.getStatus": {
|
|
42579
|
+
capName: "motion-zones",
|
|
42580
|
+
capScope: "device",
|
|
42581
|
+
addonId: null,
|
|
42582
|
+
access: "view"
|
|
42583
|
+
},
|
|
39463
42584
|
"motionZones.setZone": {
|
|
39464
42585
|
capName: "motion-zones",
|
|
39465
42586
|
capScope: "device",
|
|
@@ -39520,6 +42641,12 @@ Object.freeze({
|
|
|
39520
42641
|
addonId: null,
|
|
39521
42642
|
access: "view"
|
|
39522
42643
|
},
|
|
42644
|
+
"nativeObjectDetection.getStatus": {
|
|
42645
|
+
capName: "native-object-detection",
|
|
42646
|
+
capScope: "device",
|
|
42647
|
+
addonId: null,
|
|
42648
|
+
access: "view"
|
|
42649
|
+
},
|
|
39523
42650
|
"nativeObjectDetection.setEnabled": {
|
|
39524
42651
|
capName: "native-object-detection",
|
|
39525
42652
|
capScope: "device",
|
|
@@ -39538,6 +42665,12 @@ Object.freeze({
|
|
|
39538
42665
|
addonId: null,
|
|
39539
42666
|
access: "view"
|
|
39540
42667
|
},
|
|
42668
|
+
"navigation.getStatus": {
|
|
42669
|
+
capName: "navigation",
|
|
42670
|
+
capScope: "device",
|
|
42671
|
+
addonId: null,
|
|
42672
|
+
access: "view"
|
|
42673
|
+
},
|
|
39541
42674
|
"navigation.goToPoint": {
|
|
39542
42675
|
capName: "navigation",
|
|
39543
42676
|
capScope: "device",
|
|
@@ -39622,6 +42755,12 @@ Object.freeze({
|
|
|
39622
42755
|
addonId: null,
|
|
39623
42756
|
access: "create"
|
|
39624
42757
|
},
|
|
42758
|
+
"networkLink.getStatus": {
|
|
42759
|
+
capName: "network-link",
|
|
42760
|
+
capScope: "device",
|
|
42761
|
+
addonId: null,
|
|
42762
|
+
access: "view"
|
|
42763
|
+
},
|
|
39625
42764
|
"networkQuality.getAllStats": {
|
|
39626
42765
|
capName: "network-quality",
|
|
39627
42766
|
capScope: "system",
|
|
@@ -39880,18 +43019,36 @@ Object.freeze({
|
|
|
39880
43019
|
addonId: null,
|
|
39881
43020
|
access: "create"
|
|
39882
43021
|
},
|
|
43022
|
+
"notifier.getStatus": {
|
|
43023
|
+
capName: "notifier",
|
|
43024
|
+
capScope: "device",
|
|
43025
|
+
addonId: null,
|
|
43026
|
+
access: "view"
|
|
43027
|
+
},
|
|
39883
43028
|
"notifier.send": {
|
|
39884
43029
|
capName: "notifier",
|
|
39885
43030
|
capScope: "device",
|
|
39886
43031
|
addonId: null,
|
|
39887
43032
|
access: "create"
|
|
39888
43033
|
},
|
|
43034
|
+
"numericSensor.getStatus": {
|
|
43035
|
+
capName: "numeric-sensor",
|
|
43036
|
+
capScope: "device",
|
|
43037
|
+
addonId: null,
|
|
43038
|
+
access: "view"
|
|
43039
|
+
},
|
|
39889
43040
|
"oauthIntegration.getDescriptor": {
|
|
39890
43041
|
capName: "oauth-integration",
|
|
39891
43042
|
capScope: "system",
|
|
39892
43043
|
addonId: null,
|
|
39893
43044
|
access: "view"
|
|
39894
43045
|
},
|
|
43046
|
+
"osd.getStatus": {
|
|
43047
|
+
capName: "osd",
|
|
43048
|
+
capScope: "device",
|
|
43049
|
+
addonId: null,
|
|
43050
|
+
access: "view"
|
|
43051
|
+
},
|
|
39895
43052
|
"osd.setOverlay": {
|
|
39896
43053
|
capName: "osd",
|
|
39897
43054
|
capScope: "device",
|
|
@@ -39964,6 +43121,12 @@ Object.freeze({
|
|
|
39964
43121
|
addonId: null,
|
|
39965
43122
|
access: "create"
|
|
39966
43123
|
},
|
|
43124
|
+
"petFeeder.getStatus": {
|
|
43125
|
+
capName: "pet-feeder",
|
|
43126
|
+
capScope: "device",
|
|
43127
|
+
addonId: null,
|
|
43128
|
+
access: "view"
|
|
43129
|
+
},
|
|
39967
43130
|
"petFeeder.markFoodReplenished": {
|
|
39968
43131
|
capName: "pet-feeder",
|
|
39969
43132
|
capScope: "device",
|
|
@@ -40006,6 +43169,12 @@ Object.freeze({
|
|
|
40006
43169
|
addonId: null,
|
|
40007
43170
|
access: "create"
|
|
40008
43171
|
},
|
|
43172
|
+
"pipelineAnalytics.applyDeviceSettingsPatch": {
|
|
43173
|
+
capName: "pipeline-analytics",
|
|
43174
|
+
capScope: "device",
|
|
43175
|
+
addonId: null,
|
|
43176
|
+
access: "create"
|
|
43177
|
+
},
|
|
40009
43178
|
"pipelineAnalytics.cancelRelocateMedia": {
|
|
40010
43179
|
capName: "pipeline-analytics",
|
|
40011
43180
|
capScope: "device",
|
|
@@ -40072,6 +43241,18 @@ Object.freeze({
|
|
|
40072
43241
|
addonId: null,
|
|
40073
43242
|
access: "view"
|
|
40074
43243
|
},
|
|
43244
|
+
"pipelineAnalytics.getDeviceLiveContribution": {
|
|
43245
|
+
capName: "pipeline-analytics",
|
|
43246
|
+
capScope: "device",
|
|
43247
|
+
addonId: null,
|
|
43248
|
+
access: "view"
|
|
43249
|
+
},
|
|
43250
|
+
"pipelineAnalytics.getDeviceSettingsContribution": {
|
|
43251
|
+
capName: "pipeline-analytics",
|
|
43252
|
+
capScope: "device",
|
|
43253
|
+
addonId: null,
|
|
43254
|
+
access: "view"
|
|
43255
|
+
},
|
|
40075
43256
|
"pipelineAnalytics.getEventDensity": {
|
|
40076
43257
|
capName: "pipeline-analytics",
|
|
40077
43258
|
capScope: "device",
|
|
@@ -40282,6 +43463,12 @@ Object.freeze({
|
|
|
40282
43463
|
addonId: null,
|
|
40283
43464
|
access: "view"
|
|
40284
43465
|
},
|
|
43466
|
+
"pipelineAnalytics.ownersWithMedia": {
|
|
43467
|
+
capName: "pipeline-analytics",
|
|
43468
|
+
capScope: "device",
|
|
43469
|
+
addonId: null,
|
|
43470
|
+
access: "view"
|
|
43471
|
+
},
|
|
40285
43472
|
"pipelineAnalytics.pauseForStorageMigration": {
|
|
40286
43473
|
capName: "pipeline-analytics",
|
|
40287
43474
|
capScope: "device",
|
|
@@ -40636,6 +43823,12 @@ Object.freeze({
|
|
|
40636
43823
|
addonId: null,
|
|
40637
43824
|
access: "view"
|
|
40638
43825
|
},
|
|
43826
|
+
"pipelineOrchestrator.applyDeviceSettingsPatch": {
|
|
43827
|
+
capName: "pipeline-orchestrator",
|
|
43828
|
+
capScope: "system",
|
|
43829
|
+
addonId: null,
|
|
43830
|
+
access: "create"
|
|
43831
|
+
},
|
|
40639
43832
|
"pipelineOrchestrator.assignAudio": {
|
|
40640
43833
|
capName: "pipeline-orchestrator",
|
|
40641
43834
|
capScope: "system",
|
|
@@ -40726,6 +43919,18 @@ Object.freeze({
|
|
|
40726
43919
|
addonId: null,
|
|
40727
43920
|
access: "view"
|
|
40728
43921
|
},
|
|
43922
|
+
"pipelineOrchestrator.getDeviceLiveContribution": {
|
|
43923
|
+
capName: "pipeline-orchestrator",
|
|
43924
|
+
capScope: "system",
|
|
43925
|
+
addonId: null,
|
|
43926
|
+
access: "view"
|
|
43927
|
+
},
|
|
43928
|
+
"pipelineOrchestrator.getDeviceSettingsContribution": {
|
|
43929
|
+
capName: "pipeline-orchestrator",
|
|
43930
|
+
capScope: "system",
|
|
43931
|
+
addonId: null,
|
|
43932
|
+
access: "view"
|
|
43933
|
+
},
|
|
40729
43934
|
"pipelineOrchestrator.getGlobalMetrics": {
|
|
40730
43935
|
capName: "pipeline-orchestrator",
|
|
40731
43936
|
capScope: "system",
|
|
@@ -41134,12 +44339,36 @@ Object.freeze({
|
|
|
41134
44339
|
addonId: null,
|
|
41135
44340
|
access: "view"
|
|
41136
44341
|
},
|
|
44342
|
+
"powerMeter.getStatus": {
|
|
44343
|
+
capName: "power-meter",
|
|
44344
|
+
capScope: "device",
|
|
44345
|
+
addonId: null,
|
|
44346
|
+
access: "view"
|
|
44347
|
+
},
|
|
44348
|
+
"presence.getStatus": {
|
|
44349
|
+
capName: "presence",
|
|
44350
|
+
capScope: "device",
|
|
44351
|
+
addonId: null,
|
|
44352
|
+
access: "view"
|
|
44353
|
+
},
|
|
44354
|
+
"pressureSensor.getStatus": {
|
|
44355
|
+
capName: "pressure-sensor",
|
|
44356
|
+
capScope: "device",
|
|
44357
|
+
addonId: null,
|
|
44358
|
+
access: "view"
|
|
44359
|
+
},
|
|
41137
44360
|
"privacyMask.getOptions": {
|
|
41138
44361
|
capName: "privacy-mask",
|
|
41139
44362
|
capScope: "device",
|
|
41140
44363
|
addonId: null,
|
|
41141
44364
|
access: "view"
|
|
41142
44365
|
},
|
|
44366
|
+
"privacyMask.getStatus": {
|
|
44367
|
+
capName: "privacy-mask",
|
|
44368
|
+
capScope: "device",
|
|
44369
|
+
addonId: null,
|
|
44370
|
+
access: "view"
|
|
44371
|
+
},
|
|
41143
44372
|
"privacyMask.setAudioEnabled": {
|
|
41144
44373
|
capName: "privacy-mask",
|
|
41145
44374
|
capScope: "device",
|
|
@@ -41200,6 +44429,12 @@ Object.freeze({
|
|
|
41200
44429
|
addonId: null,
|
|
41201
44430
|
access: "view"
|
|
41202
44431
|
},
|
|
44432
|
+
"ptz.getStatus": {
|
|
44433
|
+
capName: "ptz",
|
|
44434
|
+
capScope: "device",
|
|
44435
|
+
addonId: null,
|
|
44436
|
+
access: "view"
|
|
44437
|
+
},
|
|
41203
44438
|
"ptz.goHome": {
|
|
41204
44439
|
capName: "ptz",
|
|
41205
44440
|
capScope: "device",
|
|
@@ -41278,6 +44513,12 @@ Object.freeze({
|
|
|
41278
44513
|
addonId: null,
|
|
41279
44514
|
access: "create"
|
|
41280
44515
|
},
|
|
44516
|
+
"recording.applyDeviceSettingsPatch": {
|
|
44517
|
+
capName: "recording",
|
|
44518
|
+
capScope: "system",
|
|
44519
|
+
addonId: null,
|
|
44520
|
+
access: "create"
|
|
44521
|
+
},
|
|
41281
44522
|
"recording.cancelRelocateJob": {
|
|
41282
44523
|
capName: "recording",
|
|
41283
44524
|
capScope: "system",
|
|
@@ -41326,6 +44567,18 @@ Object.freeze({
|
|
|
41326
44567
|
addonId: null,
|
|
41327
44568
|
access: "view"
|
|
41328
44569
|
},
|
|
44570
|
+
"recording.getDeviceLiveContribution": {
|
|
44571
|
+
capName: "recording",
|
|
44572
|
+
capScope: "system",
|
|
44573
|
+
addonId: null,
|
|
44574
|
+
access: "view"
|
|
44575
|
+
},
|
|
44576
|
+
"recording.getDeviceSettingsContribution": {
|
|
44577
|
+
capName: "recording",
|
|
44578
|
+
capScope: "system",
|
|
44579
|
+
addonId: null,
|
|
44580
|
+
access: "view"
|
|
44581
|
+
},
|
|
41329
44582
|
"recording.getPlacement": {
|
|
41330
44583
|
capName: "recording",
|
|
41331
44584
|
capScope: "system",
|
|
@@ -41344,6 +44597,12 @@ Object.freeze({
|
|
|
41344
44597
|
addonId: null,
|
|
41345
44598
|
access: "view"
|
|
41346
44599
|
},
|
|
44600
|
+
"recording.getStatus": {
|
|
44601
|
+
capName: "recording",
|
|
44602
|
+
capScope: "system",
|
|
44603
|
+
addonId: null,
|
|
44604
|
+
access: "view"
|
|
44605
|
+
},
|
|
41347
44606
|
"recording.getStorageMigrationMoveStatus": {
|
|
41348
44607
|
capName: "recording",
|
|
41349
44608
|
capScope: "system",
|
|
@@ -41518,6 +44777,24 @@ Object.freeze({
|
|
|
41518
44777
|
addonId: null,
|
|
41519
44778
|
access: "view"
|
|
41520
44779
|
},
|
|
44780
|
+
"recordingOnboard.getOptions": {
|
|
44781
|
+
capName: "recording-onboard",
|
|
44782
|
+
capScope: "device",
|
|
44783
|
+
addonId: null,
|
|
44784
|
+
access: "view"
|
|
44785
|
+
},
|
|
44786
|
+
"recordingOnboard.getStatus": {
|
|
44787
|
+
capName: "recording-onboard",
|
|
44788
|
+
capScope: "device",
|
|
44789
|
+
addonId: null,
|
|
44790
|
+
access: "view"
|
|
44791
|
+
},
|
|
44792
|
+
"recordingOnboard.setSettings": {
|
|
44793
|
+
capName: "recording-onboard",
|
|
44794
|
+
capScope: "device",
|
|
44795
|
+
addonId: null,
|
|
44796
|
+
access: "create"
|
|
44797
|
+
},
|
|
41521
44798
|
"recordingSignal.getStatus": {
|
|
41522
44799
|
capName: "recording-signal",
|
|
41523
44800
|
capScope: "device",
|
|
@@ -41548,6 +44825,12 @@ Object.freeze({
|
|
|
41548
44825
|
addonId: null,
|
|
41549
44826
|
access: "delete"
|
|
41550
44827
|
},
|
|
44828
|
+
"sceneMonitor.getStatus": {
|
|
44829
|
+
capName: "scene-monitor",
|
|
44830
|
+
capScope: "device",
|
|
44831
|
+
addonId: null,
|
|
44832
|
+
access: "view"
|
|
44833
|
+
},
|
|
41551
44834
|
"sceneMonitor.listScenes": {
|
|
41552
44835
|
capName: "scene-monitor",
|
|
41553
44836
|
capScope: "device",
|
|
@@ -41578,6 +44861,12 @@ Object.freeze({
|
|
|
41578
44861
|
addonId: null,
|
|
41579
44862
|
access: "create"
|
|
41580
44863
|
},
|
|
44864
|
+
"scriptRunner.getStatus": {
|
|
44865
|
+
capName: "script-runner",
|
|
44866
|
+
capScope: "device",
|
|
44867
|
+
addonId: null,
|
|
44868
|
+
access: "view"
|
|
44869
|
+
},
|
|
41581
44870
|
"scriptRunner.run": {
|
|
41582
44871
|
capName: "script-runner",
|
|
41583
44872
|
capScope: "device",
|
|
@@ -41710,6 +44999,12 @@ Object.freeze({
|
|
|
41710
44999
|
addonId: null,
|
|
41711
45000
|
access: "create"
|
|
41712
45001
|
},
|
|
45002
|
+
"smoke.getStatus": {
|
|
45003
|
+
capName: "smoke",
|
|
45004
|
+
capScope: "device",
|
|
45005
|
+
addonId: null,
|
|
45006
|
+
access: "view"
|
|
45007
|
+
},
|
|
41713
45008
|
"smtpProvider.getStatus": {
|
|
41714
45009
|
capName: "smtp-provider",
|
|
41715
45010
|
capScope: "system",
|
|
@@ -41728,12 +45023,30 @@ Object.freeze({
|
|
|
41728
45023
|
addonId: null,
|
|
41729
45024
|
access: "view"
|
|
41730
45025
|
},
|
|
45026
|
+
"snapshot.applyDeviceSettingsPatch": {
|
|
45027
|
+
capName: "snapshot",
|
|
45028
|
+
capScope: "device",
|
|
45029
|
+
addonId: null,
|
|
45030
|
+
access: "create"
|
|
45031
|
+
},
|
|
41731
45032
|
"snapshot.getDebugState": {
|
|
41732
45033
|
capName: "snapshot",
|
|
41733
45034
|
capScope: "device",
|
|
41734
45035
|
addonId: null,
|
|
41735
45036
|
access: "view"
|
|
41736
45037
|
},
|
|
45038
|
+
"snapshot.getDeviceLiveContribution": {
|
|
45039
|
+
capName: "snapshot",
|
|
45040
|
+
capScope: "device",
|
|
45041
|
+
addonId: null,
|
|
45042
|
+
access: "view"
|
|
45043
|
+
},
|
|
45044
|
+
"snapshot.getDeviceSettingsContribution": {
|
|
45045
|
+
capName: "snapshot",
|
|
45046
|
+
capScope: "device",
|
|
45047
|
+
addonId: null,
|
|
45048
|
+
access: "view"
|
|
45049
|
+
},
|
|
41737
45050
|
"snapshot.getSnapshot": {
|
|
41738
45051
|
capName: "snapshot",
|
|
41739
45052
|
capScope: "device",
|
|
@@ -41752,6 +45065,12 @@ Object.freeze({
|
|
|
41752
45065
|
addonId: null,
|
|
41753
45066
|
access: "view"
|
|
41754
45067
|
},
|
|
45068
|
+
"snapshot.getStatus": {
|
|
45069
|
+
capName: "snapshot",
|
|
45070
|
+
capScope: "device",
|
|
45071
|
+
addonId: null,
|
|
45072
|
+
access: "view"
|
|
45073
|
+
},
|
|
41755
45074
|
"snapshot.invalidateCache": {
|
|
41756
45075
|
capName: "snapshot",
|
|
41757
45076
|
capScope: "device",
|
|
@@ -42088,6 +45407,12 @@ Object.freeze({
|
|
|
42088
45407
|
addonId: null,
|
|
42089
45408
|
access: "create"
|
|
42090
45409
|
},
|
|
45410
|
+
"streamBroker.applyDeviceSettingsPatch": {
|
|
45411
|
+
capName: "stream-broker",
|
|
45412
|
+
capScope: "system",
|
|
45413
|
+
addonId: null,
|
|
45414
|
+
access: "create"
|
|
45415
|
+
},
|
|
42091
45416
|
"streamBroker.assignProfile": {
|
|
42092
45417
|
capName: "stream-broker",
|
|
42093
45418
|
capScope: "system",
|
|
@@ -42124,6 +45449,18 @@ Object.freeze({
|
|
|
42124
45449
|
addonId: null,
|
|
42125
45450
|
access: "view"
|
|
42126
45451
|
},
|
|
45452
|
+
"streamBroker.getDeviceLiveContribution": {
|
|
45453
|
+
capName: "stream-broker",
|
|
45454
|
+
capScope: "system",
|
|
45455
|
+
addonId: null,
|
|
45456
|
+
access: "view"
|
|
45457
|
+
},
|
|
45458
|
+
"streamBroker.getDeviceSettingsContribution": {
|
|
45459
|
+
capName: "stream-broker",
|
|
45460
|
+
capScope: "system",
|
|
45461
|
+
addonId: null,
|
|
45462
|
+
access: "view"
|
|
45463
|
+
},
|
|
42127
45464
|
"streamBroker.getPreBufferInfo": {
|
|
42128
45465
|
capName: "stream-broker",
|
|
42129
45466
|
capScope: "system",
|
|
@@ -42322,6 +45659,12 @@ Object.freeze({
|
|
|
42322
45659
|
addonId: null,
|
|
42323
45660
|
access: "view"
|
|
42324
45661
|
},
|
|
45662
|
+
"streamParams.getStatus": {
|
|
45663
|
+
capName: "stream-params",
|
|
45664
|
+
capScope: "device",
|
|
45665
|
+
addonId: null,
|
|
45666
|
+
access: "view"
|
|
45667
|
+
},
|
|
42325
45668
|
"streamParams.setProfile": {
|
|
42326
45669
|
capName: "stream-params",
|
|
42327
45670
|
capScope: "device",
|
|
@@ -42334,6 +45677,12 @@ Object.freeze({
|
|
|
42334
45677
|
addonId: null,
|
|
42335
45678
|
access: "view"
|
|
42336
45679
|
},
|
|
45680
|
+
"switch.getStatus": {
|
|
45681
|
+
capName: "switch",
|
|
45682
|
+
capScope: "device",
|
|
45683
|
+
addonId: null,
|
|
45684
|
+
access: "view"
|
|
45685
|
+
},
|
|
42337
45686
|
"switch.setState": {
|
|
42338
45687
|
capName: "switch",
|
|
42339
45688
|
capScope: "device",
|
|
@@ -42430,6 +45779,18 @@ Object.freeze({
|
|
|
42430
45779
|
addonId: null,
|
|
42431
45780
|
access: "create"
|
|
42432
45781
|
},
|
|
45782
|
+
"tamper.getStatus": {
|
|
45783
|
+
capName: "tamper",
|
|
45784
|
+
capScope: "device",
|
|
45785
|
+
addonId: null,
|
|
45786
|
+
access: "view"
|
|
45787
|
+
},
|
|
45788
|
+
"temperatureSensor.getStatus": {
|
|
45789
|
+
capName: "temperature-sensor",
|
|
45790
|
+
capScope: "device",
|
|
45791
|
+
addonId: null,
|
|
45792
|
+
access: "view"
|
|
45793
|
+
},
|
|
42433
45794
|
"terminalSession.adoptLegacyMonitor": {
|
|
42434
45795
|
capName: "terminal-session",
|
|
42435
45796
|
capScope: "system",
|
|
@@ -42526,6 +45887,12 @@ Object.freeze({
|
|
|
42526
45887
|
addonId: null,
|
|
42527
45888
|
access: "view"
|
|
42528
45889
|
},
|
|
45890
|
+
"update.getStatus": {
|
|
45891
|
+
capName: "update",
|
|
45892
|
+
capScope: "device",
|
|
45893
|
+
addonId: null,
|
|
45894
|
+
access: "view"
|
|
45895
|
+
},
|
|
42529
45896
|
"update.installUpdate": {
|
|
42530
45897
|
capName: "update",
|
|
42531
45898
|
capScope: "device",
|
|
@@ -42748,6 +46115,12 @@ Object.freeze({
|
|
|
42748
46115
|
addonId: null,
|
|
42749
46116
|
access: "create"
|
|
42750
46117
|
},
|
|
46118
|
+
"vacuumControl.getStatus": {
|
|
46119
|
+
capName: "vacuum-control",
|
|
46120
|
+
capScope: "device",
|
|
46121
|
+
addonId: null,
|
|
46122
|
+
access: "view"
|
|
46123
|
+
},
|
|
42751
46124
|
"vacuumControl.locate": {
|
|
42752
46125
|
capName: "vacuum-control",
|
|
42753
46126
|
capScope: "device",
|
|
@@ -42790,6 +46163,12 @@ Object.freeze({
|
|
|
42790
46163
|
addonId: null,
|
|
42791
46164
|
access: "create"
|
|
42792
46165
|
},
|
|
46166
|
+
"valve.getStatus": {
|
|
46167
|
+
capName: "valve",
|
|
46168
|
+
capScope: "device",
|
|
46169
|
+
addonId: null,
|
|
46170
|
+
access: "view"
|
|
46171
|
+
},
|
|
42793
46172
|
"valve.open": {
|
|
42794
46173
|
capName: "valve",
|
|
42795
46174
|
capScope: "device",
|
|
@@ -42862,6 +46241,18 @@ Object.freeze({
|
|
|
42862
46241
|
addonId: null,
|
|
42863
46242
|
access: "create"
|
|
42864
46243
|
},
|
|
46244
|
+
"vibration.getStatus": {
|
|
46245
|
+
capName: "vibration",
|
|
46246
|
+
capScope: "device",
|
|
46247
|
+
addonId: null,
|
|
46248
|
+
access: "view"
|
|
46249
|
+
},
|
|
46250
|
+
"videoclips.dialClipStream": {
|
|
46251
|
+
capName: "videoclips",
|
|
46252
|
+
capScope: "device",
|
|
46253
|
+
addonId: null,
|
|
46254
|
+
access: "view"
|
|
46255
|
+
},
|
|
42865
46256
|
"videoclips.getClipPlayback": {
|
|
42866
46257
|
capName: "videoclips",
|
|
42867
46258
|
capScope: "device",
|
|
@@ -42874,6 +46265,18 @@ Object.freeze({
|
|
|
42874
46265
|
addonId: null,
|
|
42875
46266
|
access: "view"
|
|
42876
46267
|
},
|
|
46268
|
+
"videoclips.listSources": {
|
|
46269
|
+
capName: "videoclips",
|
|
46270
|
+
capScope: "device",
|
|
46271
|
+
addonId: null,
|
|
46272
|
+
access: "view"
|
|
46273
|
+
},
|
|
46274
|
+
"videoclips.readClipBytes": {
|
|
46275
|
+
capName: "videoclips",
|
|
46276
|
+
capScope: "device",
|
|
46277
|
+
addonId: null,
|
|
46278
|
+
access: "view"
|
|
46279
|
+
},
|
|
42877
46280
|
"viewerUi.getStaticDir": {
|
|
42878
46281
|
capName: "viewer-ui",
|
|
42879
46282
|
capScope: "system",
|
|
@@ -42886,6 +46289,12 @@ Object.freeze({
|
|
|
42886
46289
|
addonId: null,
|
|
42887
46290
|
access: "view"
|
|
42888
46291
|
},
|
|
46292
|
+
"waterHeater.getStatus": {
|
|
46293
|
+
capName: "water-heater",
|
|
46294
|
+
capScope: "device",
|
|
46295
|
+
addonId: null,
|
|
46296
|
+
access: "view"
|
|
46297
|
+
},
|
|
42889
46298
|
"waterHeater.setAway": {
|
|
42890
46299
|
capName: "water-heater",
|
|
42891
46300
|
capScope: "device",
|
|
@@ -42904,6 +46313,12 @@ Object.freeze({
|
|
|
42904
46313
|
addonId: null,
|
|
42905
46314
|
access: "create"
|
|
42906
46315
|
},
|
|
46316
|
+
"weather.getStatus": {
|
|
46317
|
+
capName: "weather",
|
|
46318
|
+
capScope: "device",
|
|
46319
|
+
addonId: null,
|
|
46320
|
+
access: "view"
|
|
46321
|
+
},
|
|
42907
46322
|
"webrtcSession.addIceCandidate": {
|
|
42908
46323
|
capName: "webrtc-session",
|
|
42909
46324
|
capScope: "device",
|
|
@@ -42928,6 +46343,12 @@ Object.freeze({
|
|
|
42928
46343
|
addonId: null,
|
|
42929
46344
|
access: "view"
|
|
42930
46345
|
},
|
|
46346
|
+
"webrtcSession.getSessionDebug": {
|
|
46347
|
+
capName: "webrtc-session",
|
|
46348
|
+
capScope: "device",
|
|
46349
|
+
addonId: null,
|
|
46350
|
+
access: "view"
|
|
46351
|
+
},
|
|
42931
46352
|
"webrtcSession.getSessionState": {
|
|
42932
46353
|
capName: "webrtc-session",
|
|
42933
46354
|
capScope: "device",
|
|
@@ -43260,6 +46681,11 @@ Object.freeze({
|
|
|
43260
46681
|
form: "single",
|
|
43261
46682
|
optional: true
|
|
43262
46683
|
}],
|
|
46684
|
+
"deviceAdminLink.getAdminLink": [{
|
|
46685
|
+
name: "deviceId",
|
|
46686
|
+
form: "single",
|
|
46687
|
+
optional: false
|
|
46688
|
+
}],
|
|
43263
46689
|
"deviceAdoption.release": [{
|
|
43264
46690
|
name: "camDeviceId",
|
|
43265
46691
|
form: "single",
|
|
@@ -44170,6 +47596,11 @@ Object.freeze({
|
|
|
44170
47596
|
form: "single",
|
|
44171
47597
|
optional: false
|
|
44172
47598
|
}],
|
|
47599
|
+
"pipelineAnalytics.ownersWithMedia": [{
|
|
47600
|
+
name: "deviceId",
|
|
47601
|
+
form: "single",
|
|
47602
|
+
optional: false
|
|
47603
|
+
}],
|
|
44173
47604
|
"pipelineAnalytics.proposeRetrainAnnotations": [{
|
|
44174
47605
|
name: "deviceId",
|
|
44175
47606
|
form: "single",
|
|
@@ -44640,6 +48071,16 @@ Object.freeze({
|
|
|
44640
48071
|
form: "single",
|
|
44641
48072
|
optional: true
|
|
44642
48073
|
}],
|
|
48074
|
+
"recordingOnboard.getOptions": [{
|
|
48075
|
+
name: "deviceId",
|
|
48076
|
+
form: "single",
|
|
48077
|
+
optional: false
|
|
48078
|
+
}],
|
|
48079
|
+
"recordingOnboard.setSettings": [{
|
|
48080
|
+
name: "deviceId",
|
|
48081
|
+
form: "single",
|
|
48082
|
+
optional: false
|
|
48083
|
+
}],
|
|
44643
48084
|
"recordingSignal.getStatus": [{
|
|
44644
48085
|
name: "deviceId",
|
|
44645
48086
|
form: "single",
|
|
@@ -44866,6 +48307,11 @@ Object.freeze({
|
|
|
44866
48307
|
form: "single",
|
|
44867
48308
|
optional: false
|
|
44868
48309
|
}],
|
|
48310
|
+
"videoclips.dialClipStream": [{
|
|
48311
|
+
name: "deviceId",
|
|
48312
|
+
form: "single",
|
|
48313
|
+
optional: false
|
|
48314
|
+
}],
|
|
44869
48315
|
"videoclips.getClipPlayback": [{
|
|
44870
48316
|
name: "deviceId",
|
|
44871
48317
|
form: "single",
|
|
@@ -44876,6 +48322,16 @@ Object.freeze({
|
|
|
44876
48322
|
form: "single",
|
|
44877
48323
|
optional: false
|
|
44878
48324
|
}],
|
|
48325
|
+
"videoclips.listSources": [{
|
|
48326
|
+
name: "deviceId",
|
|
48327
|
+
form: "single",
|
|
48328
|
+
optional: false
|
|
48329
|
+
}],
|
|
48330
|
+
"videoclips.readClipBytes": [{
|
|
48331
|
+
name: "deviceId",
|
|
48332
|
+
form: "single",
|
|
48333
|
+
optional: false
|
|
48334
|
+
}],
|
|
44879
48335
|
"waterHeater.setAway": [{
|
|
44880
48336
|
name: "deviceId",
|
|
44881
48337
|
form: "single",
|
|
@@ -44911,6 +48367,11 @@ Object.freeze({
|
|
|
44911
48367
|
form: "single",
|
|
44912
48368
|
optional: false
|
|
44913
48369
|
}],
|
|
48370
|
+
"webrtcSession.getSessionDebug": [{
|
|
48371
|
+
name: "deviceId",
|
|
48372
|
+
form: "single",
|
|
48373
|
+
optional: false
|
|
48374
|
+
}],
|
|
44914
48375
|
"webrtcSession.getSessionState": [{
|
|
44915
48376
|
name: "deviceId",
|
|
44916
48377
|
form: "single",
|
|
@@ -46637,6 +50098,7 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
46637
50098
|
registerNativeCapabilities() {
|
|
46638
50099
|
this.registerStreamCatalogProvider();
|
|
46639
50100
|
this.registerSnapshotCap();
|
|
50101
|
+
this.registerAdminLinkCap();
|
|
46640
50102
|
this.registerPtzCap();
|
|
46641
50103
|
this.registerStreamParamsCap();
|
|
46642
50104
|
this.registerMotionZonesCap();
|
|
@@ -47207,6 +50669,39 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
|
|
|
47207
50669
|
invalidateCache: async () => {}
|
|
47208
50670
|
});
|
|
47209
50671
|
}
|
|
50672
|
+
/**
|
|
50673
|
+
* The camera's own web console. Amcrest is the same exact case as
|
|
50674
|
+
* Hikvision: `port` IS the HTTP CGI port, which is the port the web UI is
|
|
50675
|
+
* served on, and `https` is a real per-device flag — nothing is assumed.
|
|
50676
|
+
*
|
|
50677
|
+
* No credentials in the URL, although `username`/`password` are two fields
|
|
50678
|
+
* away in the same config object. See `buildDeviceAdminUrl`.
|
|
50679
|
+
*/
|
|
50680
|
+
registerAdminLinkCap() {
|
|
50681
|
+
this.ctx.registerNativeCap(deviceAdminLinkCapability, { getAdminLink: async ({ deviceId }) => {
|
|
50682
|
+
if (deviceId !== this.id) throw new Error(`AmcrestCamera: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
|
|
50683
|
+
const host = this.config.get("host") ?? "";
|
|
50684
|
+
const built = buildDeviceAdminUrl({
|
|
50685
|
+
host,
|
|
50686
|
+
port: this.config.get("port") ?? null,
|
|
50687
|
+
scheme: this.config.get("https") === true ? "https" : "http",
|
|
50688
|
+
path: "/"
|
|
50689
|
+
});
|
|
50690
|
+
if (!built.ok) {
|
|
50691
|
+
this.ctx.logger.debug("amcrest admin link unavailable", {
|
|
50692
|
+
tags: { deviceId: this.id },
|
|
50693
|
+
meta: { reason: built.reason }
|
|
50694
|
+
});
|
|
50695
|
+
return null;
|
|
50696
|
+
}
|
|
50697
|
+
return {
|
|
50698
|
+
url: built.url,
|
|
50699
|
+
label: "Web UI",
|
|
50700
|
+
target: "device",
|
|
50701
|
+
host
|
|
50702
|
+
};
|
|
50703
|
+
} });
|
|
50704
|
+
}
|
|
47210
50705
|
/** Duration (ms) a one-shot `move` pulse runs before its safety
|
|
47211
50706
|
* auto-stop fires. The `usePTZ` UI hook already issues its own paired
|
|
47212
50707
|
* `stop` after a client-side pulse window; this timer is a backstop so
|