@camstack/addon-provider-rademacher 0.2.113 → 0.2.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +3536 -33
- package/dist/addon.mjs +3536 -33
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -6344,7 +6344,7 @@ var ZodIssueCode = {
|
|
|
6344
6344
|
var ZodFirstPartyTypeKind;
|
|
6345
6345
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6346
6346
|
//#endregion
|
|
6347
|
-
//#region ../types/dist/sleep-
|
|
6347
|
+
//#region ../types/dist/sleep-i3eUVc-d.mjs
|
|
6348
6348
|
/**
|
|
6349
6349
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6350
6350
|
* window to float samples (D455).
|
|
@@ -7486,6 +7486,17 @@ var CamProfileSchema = _enum([
|
|
|
7486
7486
|
"mid",
|
|
7487
7487
|
"low"
|
|
7488
7488
|
]);
|
|
7489
|
+
/**
|
|
7490
|
+
* Which rung a per-camera consumer listens on: an explicit profile, or `auto`.
|
|
7491
|
+
*
|
|
7492
|
+
* `auto` is a REAL value, not an absent one — it means "resolve it by the rule
|
|
7493
|
+
* that consumer owns". It lives beside `CamProfile` because a rung is the only
|
|
7494
|
+
* operator-facing way to name a stream: a cam-stream id is broker-internal and
|
|
7495
|
+
* moves when a slot is reassigned, so a stored id silently stops meaning what
|
|
7496
|
+
* the operator picked. Today's one user is the audio analyser, whose rule is
|
|
7497
|
+
* "the lowest AVAILABLE native rung" (D537).
|
|
7498
|
+
*/
|
|
7499
|
+
var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
|
|
7489
7500
|
var CamStreamKindSchema = _enum([
|
|
7490
7501
|
"pull-rtsp",
|
|
7491
7502
|
"pull-rtmp",
|
|
@@ -7904,6 +7915,18 @@ function systemMethod(input, output, options) {
|
|
|
7904
7915
|
systemOnly: true
|
|
7905
7916
|
};
|
|
7906
7917
|
}
|
|
7918
|
+
/**
|
|
7919
|
+
* A method a SOURCE of a collection cap may legitimately not serve — OPTIONAL
|
|
7920
|
+
* on `InferProvider`. The `providerOptional: true` literal is what
|
|
7921
|
+
* `InferProvider` keys on; see {@link CapabilityMethodSchema.providerOptional}
|
|
7922
|
+
* for when this is the honest answer and when it is a soft stub.
|
|
7923
|
+
*/
|
|
7924
|
+
function optionalMethod(input, output, options) {
|
|
7925
|
+
return {
|
|
7926
|
+
...method(input, output, options),
|
|
7927
|
+
providerOptional: true
|
|
7928
|
+
};
|
|
7929
|
+
}
|
|
7907
7930
|
/** Shorthand to define an event schema */
|
|
7908
7931
|
function event(data) {
|
|
7909
7932
|
return { data };
|
|
@@ -8334,6 +8357,8 @@ var EVENT_OWNER_TYPES = [
|
|
|
8334
8357
|
* nothing failing until a caller asked.
|
|
8335
8358
|
*/
|
|
8336
8359
|
var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
|
|
8360
|
+
/** The same list as a Zod enum, for the cap input that carries it. */
|
|
8361
|
+
var MediaPresenceOwnerKindSchema = _enum([...EVENT_OWNER_TYPES, "track"]);
|
|
8337
8362
|
new Set(EVENT_OWNER_TYPES);
|
|
8338
8363
|
var EncodeProfileSchema = object({
|
|
8339
8364
|
video: object({
|
|
@@ -8706,6 +8731,7 @@ var CameraSwitchIdSchema = _enum([
|
|
|
8706
8731
|
"device-audio",
|
|
8707
8732
|
"broker-audio",
|
|
8708
8733
|
"audio-analysis",
|
|
8734
|
+
"event-saving",
|
|
8709
8735
|
"recording",
|
|
8710
8736
|
"notifications"
|
|
8711
8737
|
]);
|
|
@@ -8741,10 +8767,20 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
|
|
|
8741
8767
|
var CameraSwitchUnavailableReasonSchema = _enum([
|
|
8742
8768
|
"no-provider",
|
|
8743
8769
|
"source-unreachable",
|
|
8744
|
-
"not-configured",
|
|
8745
8770
|
"device-disabled"
|
|
8746
8771
|
]);
|
|
8747
8772
|
/**
|
|
8773
|
+
* Something true about a switch that is nonetheless OFFERED.
|
|
8774
|
+
*
|
|
8775
|
+
* The distinction from {@link CameraSwitchUnavailableReasonSchema} is the
|
|
8776
|
+
* whole point: a reason replaces the control, a warning travels WITH it. The
|
|
8777
|
+
* privacy mask taught us the difference — greying the one button that starts
|
|
8778
|
+
* the feature teaches the operator the feature is broken, while a live toggle
|
|
8779
|
+
* that flips nothing teaches them the switch is broken. Neither is true; the
|
|
8780
|
+
* honest surface is a working toggle that says what is missing.
|
|
8781
|
+
*/
|
|
8782
|
+
var CameraSwitchWarningSchema = _enum(["no-privacy-zones"]);
|
|
8783
|
+
/**
|
|
8748
8784
|
* One switch, resolved for one camera.
|
|
8749
8785
|
*
|
|
8750
8786
|
* `label` and `costWhenOff` travel ON THE WIRE rather than being looked up
|
|
@@ -8763,6 +8799,11 @@ var CameraSwitchSchema = object({
|
|
|
8763
8799
|
/** False = do not render a control. `unavailableReason` says why. */
|
|
8764
8800
|
available: boolean(),
|
|
8765
8801
|
unavailableReason: CameraSwitchUnavailableReasonSchema.optional(),
|
|
8802
|
+
/**
|
|
8803
|
+
* Present on an AVAILABLE switch only. Rendered beside the live control;
|
|
8804
|
+
* never a substitute for one.
|
|
8805
|
+
*/
|
|
8806
|
+
warning: CameraSwitchWarningSchema.optional(),
|
|
8766
8807
|
/** Current state. Meaningless when `available` is false — read it as `true`. */
|
|
8767
8808
|
enabled: boolean(),
|
|
8768
8809
|
authority: CameraSwitchAuthoritySchema
|
|
@@ -9180,11 +9221,31 @@ var RecordingBandTriggersSchema = RecordingTriggersSchema;
|
|
|
9180
9221
|
* band engine's `applies` rule). `start`/`end` are `HH:MM`; an `end <= start`
|
|
9181
9222
|
* span wraps past midnight (handled by the band engine).
|
|
9182
9223
|
*/
|
|
9224
|
+
/**
|
|
9225
|
+
* Whether a band's footage carries sound.
|
|
9226
|
+
*
|
|
9227
|
+
* The recorder has always attached with `audio: 'aac'`, so every band of every
|
|
9228
|
+
* camera recorded sound whether anyone wanted it or not. The broker already
|
|
9229
|
+
* serves the other answer — `audio: 'none'` takes the video-only `/muted`
|
|
9230
|
+
* restream — so this was a choice the product could always have offered.
|
|
9231
|
+
*
|
|
9232
|
+
* It belongs on the BAND rather than the camera because one camera can want
|
|
9233
|
+
* both: silent across the shop floor during opening hours, sound overnight.
|
|
9234
|
+
* And it applies to BOTH modes — an events band firing on motion has the same
|
|
9235
|
+
* question to answer as a continuous one.
|
|
9236
|
+
*/
|
|
9237
|
+
var RecordingBandAudioSchema = _enum(["on", "muted"]);
|
|
9183
9238
|
var RecordingBandSchema = object({
|
|
9184
9239
|
days: array(RecordingWeekdaySchema),
|
|
9185
9240
|
start: string().regex(HHMM),
|
|
9186
9241
|
end: string().regex(HHMM),
|
|
9187
9242
|
mode: RecordingBandModeSchema,
|
|
9243
|
+
/**
|
|
9244
|
+
* Sound, or silence. OPTIONAL and absent-means-`on`: every recording
|
|
9245
|
+
* written before this existed carried sound, and a band whose key is
|
|
9246
|
+
* missing must keep meaning what it meant. See {@link RecordingBandAudioSchema}.
|
|
9247
|
+
*/
|
|
9248
|
+
audio: RecordingBandAudioSchema.optional(),
|
|
9188
9249
|
triggers: RecordingBandTriggersSchema.optional(),
|
|
9189
9250
|
/**
|
|
9190
9251
|
* RETIRED (D381). A band no longer carries a pre-buffer of its own.
|
|
@@ -14468,6 +14529,210 @@ method(object({ integrationId: string() }), object({ filters: array(AdoptionFilt
|
|
|
14468
14529
|
auth: "admin"
|
|
14469
14530
|
});
|
|
14470
14531
|
/**
|
|
14532
|
+
* device-admin-link — "this device has a management page of its own, and here
|
|
14533
|
+
* is its address".
|
|
14534
|
+
*
|
|
14535
|
+
* ## Why this is not a `deviceConfig` cap
|
|
14536
|
+
*
|
|
14537
|
+
* There is nothing to edit. A `deviceConfig` cap (D14) exists so the framework
|
|
14538
|
+
* can DERIVE a settings form from `getOptions` + `getStatus` and route a flat
|
|
14539
|
+
* patch back through a setter; it costs a `builderId` reducer in
|
|
14540
|
+
* `device-config-contribution.ts` and a `*-config-schema.ts` beside it, and it
|
|
14541
|
+
* renders a form section. This cap answers ONE question with ONE read and
|
|
14542
|
+
* renders a button. Nothing about it is a form, so it carries no `deviceConfig`
|
|
14543
|
+
* block, no `settings`, no `runtimeState` and no reducer — exactly like
|
|
14544
|
+
* `reboot`, the other pure-RPC device-native cap.
|
|
14545
|
+
*
|
|
14546
|
+
* ## Absent, and the difference between "no page" and "we cannot say"
|
|
14547
|
+
*
|
|
14548
|
+
* The two are answered at DIFFERENT layers, on purpose:
|
|
14549
|
+
*
|
|
14550
|
+
* - **"We cannot say"** → the provider never registers the cap for that
|
|
14551
|
+
* device. A VeSync humidifier, a Petkit feeder, a Dreame vacuum and a Dreo
|
|
14552
|
+
* fan are reached only through a vendor cloud; there is no address to hand
|
|
14553
|
+
* out and no page to open. A Tuya plug, a Wyze camera and a Gree air
|
|
14554
|
+
* conditioner DO have a LAN IP, and still have no HTTP management page
|
|
14555
|
+
* behind it. None of them register, so `deviceManager.getBindings` never
|
|
14556
|
+
* lists the cap and no surface asks.
|
|
14557
|
+
* - **"This device has no page, and I know that"** → the provider registers
|
|
14558
|
+
* and `getAdminLink` returns `null`. This is the answer for a device whose
|
|
14559
|
+
* sibling DOES have a page: a Reolink battery camera reached over UDP by
|
|
14560
|
+
* `uid` with a blank `host`, an Ecowitt gateway configured in `listener`
|
|
14561
|
+
* transport, a Home Assistant broker authenticated by supervisor token
|
|
14562
|
+
* (which carries no `baseUrl` at all).
|
|
14563
|
+
*
|
|
14564
|
+
* Both draw NOTHING. A button that opens a browser error is worse than no
|
|
14565
|
+
* button, and D62 is the same rule from the other side: an off switch is
|
|
14566
|
+
* reported off, never made to look broken. There is no third state where the
|
|
14567
|
+
* UI renders a disabled button "because the device might have a page".
|
|
14568
|
+
*
|
|
14569
|
+
* ## The URL never carries credentials
|
|
14570
|
+
*
|
|
14571
|
+
* Not in userinfo, not in a query string. Every provider builds through
|
|
14572
|
+
* `buildDeviceAdminUrl` (`device-admin-link-url.ts`), which takes host, port,
|
|
14573
|
+
* scheme and path as separate arguments — there is no parameter a secret could
|
|
14574
|
+
* arrive in — and re-checks its own output for the `scheme://user:pass@` shape
|
|
14575
|
+
* that `scripts/check-no-credential-urls-in-fixtures.ts` bans from recorded
|
|
14576
|
+
* output. `scripts/check-admin-link-builder-is-the-only-url-source.ts` is what
|
|
14577
|
+
* keeps providers from hand-rolling one anyway.
|
|
14578
|
+
*
|
|
14579
|
+
* This matters here more than anywhere else in the repo, because every provider
|
|
14580
|
+
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
14581
|
+
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
14582
|
+
* and `http://admin:hunter2@192.168.50.139/` is a URL a browser accepts. The
|
|
14583
|
+
* camera's own page will ask for its own login. That is correct, and pre-
|
|
14584
|
+
* filling it is the operator's business, not ours.
|
|
14585
|
+
*
|
|
14586
|
+
* ## It is a LAN fact
|
|
14587
|
+
*
|
|
14588
|
+
* The URL addresses the device where the NODE can see it. It is not proxied,
|
|
14589
|
+
* not made reachable from outside, and not sent anywhere. A surface renders it
|
|
14590
|
+
* as a link the operator's own browser follows, on the operator's own network,
|
|
14591
|
+
* or renders nothing.
|
|
14592
|
+
*/
|
|
14593
|
+
/**
|
|
14594
|
+
* Whose page is it. The distinction is for the OPERATOR, who needs to know
|
|
14595
|
+
* before clicking whether he is about to land on a camera's own web server or
|
|
14596
|
+
* inside Home Assistant.
|
|
14597
|
+
*/
|
|
14598
|
+
var AdminLinkTargetEnum = _enum(["device", "integration"]);
|
|
14599
|
+
var DeviceAdminLinkSchema = object({
|
|
14600
|
+
/**
|
|
14601
|
+
* Absolute `http(s)://` URL. Built by `buildDeviceAdminUrl` and therefore
|
|
14602
|
+
* free of userinfo and of any credential-shaped query key.
|
|
14603
|
+
*/
|
|
14604
|
+
url: string(),
|
|
14605
|
+
/**
|
|
14606
|
+
* What the surface calls it — "Web UI", "Home Assistant", "UniFi controller".
|
|
14607
|
+
* The PROVIDER names it, because only the provider knows what the page is;
|
|
14608
|
+
* a UI that invented the label from the addon id would call the Home
|
|
14609
|
+
* Assistant device page "Provider Homeassistant".
|
|
14610
|
+
*/
|
|
14611
|
+
label: string(),
|
|
14612
|
+
target: AdminLinkTargetEnum,
|
|
14613
|
+
/**
|
|
14614
|
+
* Host the URL points at, without scheme, port or path — for the tooltip, so
|
|
14615
|
+
* an operator can see WHERE the button goes before he follows it. Redundant
|
|
14616
|
+
* with `url` by construction; carried separately so no surface has to parse
|
|
14617
|
+
* a URL to show it.
|
|
14618
|
+
*/
|
|
14619
|
+
host: string()
|
|
14620
|
+
});
|
|
14621
|
+
var deviceAdminLinkCapability = {
|
|
14622
|
+
name: "device-admin-link",
|
|
14623
|
+
scope: "device",
|
|
14624
|
+
deviceNative: true,
|
|
14625
|
+
mode: "singleton",
|
|
14626
|
+
methods: {
|
|
14627
|
+
/**
|
|
14628
|
+
* The device's management page, or `null` when this device has none.
|
|
14629
|
+
*
|
|
14630
|
+
* `auth: 'admin'` deliberately. This is administration, not actuation —
|
|
14631
|
+
* the same bucket as `reboot` and `camera-credentials`, and explicitly NOT
|
|
14632
|
+
* the actuation set `scripts/check-actuation-not-admin.ts` protects (D403).
|
|
14633
|
+
* The URL is also a statement about the LAN, which a household member with
|
|
14634
|
+
* a `view` grant on a light has no reason to be handed.
|
|
14635
|
+
*
|
|
14636
|
+
* The surfaces gate on the QUERY, never on a role they guessed: a caller
|
|
14637
|
+
* without the right loses the query and draws nothing, which is the same
|
|
14638
|
+
* thing a device with no page draws. There is no path on which a button
|
|
14639
|
+
* appears and then fails — the D403 failure mode, from the other end.
|
|
14640
|
+
*/
|
|
14641
|
+
getAdminLink: method(object({ deviceId: number().int().nonnegative() }), DeviceAdminLinkSchema.nullable(), { auth: "admin" }) }
|
|
14642
|
+
};
|
|
14643
|
+
/**
|
|
14644
|
+
* Query keys that may never appear on an admin link. A credential smuggled as
|
|
14645
|
+
* `?password=` is the same leak as userinfo, in a form the userinfo check
|
|
14646
|
+
* cannot see: it survives copy-paste, referrer headers and proxy logs
|
|
14647
|
+
* identically.
|
|
14648
|
+
*/
|
|
14649
|
+
var CREDENTIAL_QUERY_KEYS = [
|
|
14650
|
+
"password",
|
|
14651
|
+
"passwd",
|
|
14652
|
+
"pwd",
|
|
14653
|
+
"pass",
|
|
14654
|
+
"user",
|
|
14655
|
+
"username",
|
|
14656
|
+
"usr",
|
|
14657
|
+
"login",
|
|
14658
|
+
"token",
|
|
14659
|
+
"access_token",
|
|
14660
|
+
"auth",
|
|
14661
|
+
"authorization",
|
|
14662
|
+
"apikey",
|
|
14663
|
+
"api_key",
|
|
14664
|
+
"secret",
|
|
14665
|
+
"credential",
|
|
14666
|
+
"credentials",
|
|
14667
|
+
"session",
|
|
14668
|
+
"sessionid",
|
|
14669
|
+
"key"
|
|
14670
|
+
];
|
|
14671
|
+
/**
|
|
14672
|
+
* The same userinfo shape `scripts/check-no-credential-urls-in-fixtures.ts`
|
|
14673
|
+
* scans recorded fixtures for, applied to what we are about to EMIT. Kept
|
|
14674
|
+
* structurally identical on purpose: a URL this function returns is a URL that
|
|
14675
|
+
* guard would pass.
|
|
14676
|
+
*/
|
|
14677
|
+
var CREDENTIAL_URL = /[a-z][a-z0-9+.-]*:\/\/[^/\s:@'"]+:[^/\s:@'"]+@/i;
|
|
14678
|
+
/** A host that is safe to place in an authority component verbatim. */
|
|
14679
|
+
var PLAIN_HOST = /^[a-zA-Z0-9._-]+$/;
|
|
14680
|
+
/** A bracketed IPv6 literal, the only other authority form we emit. */
|
|
14681
|
+
var IPV6_HOST = /^\[[0-9a-fA-F:.]+\]$/;
|
|
14682
|
+
function defaultPortFor(scheme) {
|
|
14683
|
+
return scheme === "https" ? 443 : 80;
|
|
14684
|
+
}
|
|
14685
|
+
/**
|
|
14686
|
+
* Build the admin-page URL, or refuse by name.
|
|
14687
|
+
*
|
|
14688
|
+
* The refusal is never thrown: a provider answering "no page for this device"
|
|
14689
|
+
* is an ordinary answer (`getAdminLink` → `null`), and a throw here would turn
|
|
14690
|
+
* a missing host on one device into a failed query for the whole surface.
|
|
14691
|
+
*/
|
|
14692
|
+
function buildDeviceAdminUrl(parts) {
|
|
14693
|
+
const host = parts.host.trim();
|
|
14694
|
+
if (host.length === 0) return {
|
|
14695
|
+
ok: false,
|
|
14696
|
+
reason: "empty-host"
|
|
14697
|
+
};
|
|
14698
|
+
if (host.includes("@")) return {
|
|
14699
|
+
ok: false,
|
|
14700
|
+
reason: "host-carries-userinfo"
|
|
14701
|
+
};
|
|
14702
|
+
if (!PLAIN_HOST.test(host) && !IPV6_HOST.test(host)) return {
|
|
14703
|
+
ok: false,
|
|
14704
|
+
reason: "host-not-plain"
|
|
14705
|
+
};
|
|
14706
|
+
if (parts.port !== null) {
|
|
14707
|
+
if (!Number.isInteger(parts.port) || parts.port < 1 || parts.port > 65535) return {
|
|
14708
|
+
ok: false,
|
|
14709
|
+
reason: "port-out-of-range"
|
|
14710
|
+
};
|
|
14711
|
+
}
|
|
14712
|
+
if (!parts.path.startsWith("/")) return {
|
|
14713
|
+
ok: false,
|
|
14714
|
+
reason: "path-not-absolute"
|
|
14715
|
+
};
|
|
14716
|
+
const query = parts.query ?? {};
|
|
14717
|
+
for (const key of Object.keys(query)) if (CREDENTIAL_QUERY_KEYS.includes(key.toLowerCase())) return {
|
|
14718
|
+
ok: false,
|
|
14719
|
+
reason: "credential-query-key"
|
|
14720
|
+
};
|
|
14721
|
+
const authority = parts.port === null || parts.port === defaultPortFor(parts.scheme) ? host : `${host}:${String(parts.port)}`;
|
|
14722
|
+
const search = new URLSearchParams();
|
|
14723
|
+
for (const [key, value] of Object.entries(query)) search.append(key, value);
|
|
14724
|
+
const suffix = search.size > 0 ? `?${search.toString()}` : "";
|
|
14725
|
+
const url = `${parts.scheme}://${authority}${parts.path}${suffix}`;
|
|
14726
|
+
if (CREDENTIAL_URL.test(url)) return {
|
|
14727
|
+
ok: false,
|
|
14728
|
+
reason: "userinfo-in-result"
|
|
14729
|
+
};
|
|
14730
|
+
return {
|
|
14731
|
+
ok: true,
|
|
14732
|
+
url
|
|
14733
|
+
};
|
|
14734
|
+
}
|
|
14735
|
+
/**
|
|
14471
14736
|
* `device-export` — collection cap for addons that export camstack
|
|
14472
14737
|
* devices to external ecosystems (HomeAssistant via MQTT discovery,
|
|
14473
14738
|
* HomeKit/HAP, Alexa Smart Home, …).
|
|
@@ -21234,6 +21499,7 @@ var MediaFileKindEnum = _enum([
|
|
|
21234
21499
|
"keyFrameSmall",
|
|
21235
21500
|
"thumbnailSmall",
|
|
21236
21501
|
"confirmationCrop",
|
|
21502
|
+
"debugCrop",
|
|
21237
21503
|
"sceneFrame"
|
|
21238
21504
|
]);
|
|
21239
21505
|
/**
|
|
@@ -22313,7 +22579,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
22313
22579
|
ownerType: EventOwnerTypeSchema,
|
|
22314
22580
|
eventId: number().int(),
|
|
22315
22581
|
deviceId: number()
|
|
22316
|
-
}), array(MediaFileInfoSchema).readonly()), method(
|
|
22582
|
+
}), array(MediaFileInfoSchema).readonly()), method(object({
|
|
22583
|
+
deviceId: number(),
|
|
22584
|
+
ownerKind: MediaPresenceOwnerKindSchema,
|
|
22585
|
+
ownerIds: array(string()).max(5e3)
|
|
22586
|
+
}), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
|
|
22317
22587
|
kind: "mutation",
|
|
22318
22588
|
auth: "admin"
|
|
22319
22589
|
}), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
|
|
@@ -22898,6 +23168,18 @@ var RunnerCameraConfigSchema = object({
|
|
|
22898
23168
|
motionStreamId: string(),
|
|
22899
23169
|
detectionStreamId: string(),
|
|
22900
23170
|
/**
|
|
23171
|
+
* Which rung this camera's audio analysis listens on — `auto` (the lowest
|
|
23172
|
+
* AVAILABLE native rung) or an explicit one.
|
|
23173
|
+
*
|
|
23174
|
+
* Not consumed by the runner: carried here so it travels with the camera's
|
|
23175
|
+
* config the way `audioMotionWindowMs` does, and so the orchestrator's
|
|
23176
|
+
* watchdog resolves the same stream the audio subscription did. OPTIONAL
|
|
23177
|
+
* with no default — absent means `auto`, which is what every camera written
|
|
23178
|
+
* before this field had, and an absent optional adds nothing to the
|
|
23179
|
+
* generated cap-input defaults (D537).
|
|
23180
|
+
*/
|
|
23181
|
+
audioStreamPick: AudioStreamPickSchema.optional(),
|
|
23182
|
+
/**
|
|
22901
23183
|
* Motion sources that can activate the pipeline. At least one must
|
|
22902
23184
|
* be present. Multiple sources OR-ed: any one firing arms the
|
|
22903
23185
|
* cooldown timer and transitions watching → active. The same
|
|
@@ -23968,10 +24250,9 @@ var ServerBootModeSchema = _enum([
|
|
|
23968
24250
|
* - `idle` / `checking` / `staging` — steady / in-flight registry work.
|
|
23969
24251
|
* - `pending-restart` — a version is staged and the node has NOT yet
|
|
23970
24252
|
* restarted onto it (still running the OLD version).
|
|
23971
|
-
* - `awaiting-confirmation` —
|
|
23972
|
-
*
|
|
23973
|
-
*
|
|
23974
|
-
* manually restarted, or the probation boot auto-rolls-back.
|
|
24253
|
+
* - `awaiting-confirmation` — RETIRED with the probation boot (single-copy
|
|
24254
|
+
* collapse, 2026-07-18). Kept in the enum so an older client still parses;
|
|
24255
|
+
* no provider emits it.
|
|
23975
24256
|
*/
|
|
23976
24257
|
var ServerUpdateStateSchema = _enum([
|
|
23977
24258
|
"idle",
|
|
@@ -24060,12 +24341,35 @@ var ServerRollbackInfoSchema = object({
|
|
|
24060
24341
|
var ServerPackageStatusSchema = object({
|
|
24061
24342
|
/** Root package name (`@camstack/server` on the hub). */
|
|
24062
24343
|
packageName: string(),
|
|
24063
|
-
/**
|
|
24344
|
+
/**
|
|
24345
|
+
* Version of the code THIS PROCESS loaded — captured once, at boot, from the
|
|
24346
|
+
* package.json of the copy the code was loaded from, and never re-read. A
|
|
24347
|
+
* copy replaced on disk under a live process must not move this number;
|
|
24348
|
+
* that divergence is what `installedVersion` is for. Before 2026-09-20 this
|
|
24349
|
+
* was a lazy disk read that would have followed such a swap silently.
|
|
24350
|
+
*/
|
|
24064
24351
|
runningVersion: string().nullable(),
|
|
24065
24352
|
/** Node.js runtime version the node's process runs on (`process.versions.node`). */
|
|
24066
24353
|
nodeRuntimeVersion: string().nullable(),
|
|
24067
24354
|
/** Active data-dir root version; null when booted from seed/workspace. */
|
|
24068
24355
|
activeVersion: string().nullable(),
|
|
24356
|
+
/**
|
|
24357
|
+
* Version of the root package in the data-root `current/` copy RIGHT NOW —
|
|
24358
|
+
* what the next boot would load if nothing is staged. Normally equal to
|
|
24359
|
+
* `runningVersion`; differs only when the copy on disk changed under the
|
|
24360
|
+
* live process. `null` when there is no data-root copy (baked seed or
|
|
24361
|
+
* workspace boot). Optional for version skew: an older provider omits it.
|
|
24362
|
+
*/
|
|
24363
|
+
installedVersion: string().nullable().optional(),
|
|
24364
|
+
/**
|
|
24365
|
+
* True when the code that would run after a restart is NOT the code running
|
|
24366
|
+
* now: a version is staged (`pendingVersion`), or `installedVersion` differs
|
|
24367
|
+
* from `runningVersion`. Derived on every read, never stored. This is the
|
|
24368
|
+
* one field that lets an operator or a script say "1.2.353 is installed and
|
|
24369
|
+
* will run after a restart" instead of reading it off a number that means
|
|
24370
|
+
* "installed". Optional for version skew.
|
|
24371
|
+
*/
|
|
24372
|
+
restartRequired: boolean().optional(),
|
|
24069
24373
|
/** N-1 version kept for rollback; null when no previous version exists. */
|
|
24070
24374
|
previousVersion: string().nullable(),
|
|
24071
24375
|
/** Version of the immutable baked seed closure (image fallback). */
|
|
@@ -24100,19 +24404,71 @@ var ServerPackageStatusSchema = object({
|
|
|
24100
24404
|
});
|
|
24101
24405
|
var ServerUpdateCheckResultSchema = object({
|
|
24102
24406
|
packageName: string(),
|
|
24407
|
+
/** Same meaning as on the status payload: what THIS process loaded. */
|
|
24103
24408
|
runningVersion: string().nullable(),
|
|
24104
24409
|
latestVersion: string().nullable(),
|
|
24410
|
+
/**
|
|
24411
|
+
* `latestVersion` is newer than `runningVersion` AND is not the version
|
|
24412
|
+
* already staged on this node. A staged-and-waiting version is not "an
|
|
24413
|
+
* update available" — applying it again is refused (`already-staged`), and
|
|
24414
|
+
* a button that announced "updating…" on this boolean then got refused
|
|
24415
|
+
* would be lying. It is reported through `pendingVersion` instead.
|
|
24416
|
+
*/
|
|
24105
24417
|
updateAvailable: boolean(),
|
|
24106
24418
|
checkedAtMs: number(),
|
|
24107
24419
|
/** Non-null when the registry lookup failed (offline, bad registry, …). */
|
|
24108
|
-
error: string().nullable()
|
|
24420
|
+
error: string().nullable(),
|
|
24421
|
+
/**
|
|
24422
|
+
* Version staged on this node and awaiting its restart, when one is. Before
|
|
24423
|
+
* 2026-09-20 a check answered `{runningVersion, updateAvailable:false}` for a
|
|
24424
|
+
* node whose staged closure had never booted, and the caller had no way to
|
|
24425
|
+
* see the difference. Optional for version skew.
|
|
24426
|
+
*/
|
|
24427
|
+
pendingVersion: string().nullable().optional(),
|
|
24428
|
+
/** See the status payload: staged, or installed-on-disk ≠ running. Optional for skew. */
|
|
24429
|
+
restartRequired: boolean().optional()
|
|
24109
24430
|
});
|
|
24431
|
+
/**
|
|
24432
|
+
* What an action DID — the discriminant `accepted` cannot carry. Before
|
|
24433
|
+
* 2026-09-20 `applyServerUpdate` answered `accepted:false` both for a refusal
|
|
24434
|
+
* where nothing was wrong (already current, already staged) and for a staging
|
|
24435
|
+
* install that died on `npm error code ECONNRESET`; the only difference was
|
|
24436
|
+
* prose in `message`, and an operator restarted a container on the strength of
|
|
24437
|
+
* it, booting the previous version.
|
|
24438
|
+
*
|
|
24439
|
+
* - `staged` — the closure is installed and validated; the node
|
|
24440
|
+
* restarts to apply it (`restarting:true`). NOTHING
|
|
24441
|
+
* runs the new code until that restart completes.
|
|
24442
|
+
* - `restart-scheduled` — a plain, version-preserving restart was accepted.
|
|
24443
|
+
* - `already-current` — the target IS the running version; nothing done.
|
|
24444
|
+
* - `refused` — dropped by a gate; `reason` names which.
|
|
24445
|
+
* - `failed` — attempted and did not land; `error` says why.
|
|
24446
|
+
*/
|
|
24447
|
+
var ServerUpdateOutcomeSchema = _enum([
|
|
24448
|
+
"staged",
|
|
24449
|
+
"restart-scheduled",
|
|
24450
|
+
"already-current",
|
|
24451
|
+
"refused",
|
|
24452
|
+
"failed"
|
|
24453
|
+
]);
|
|
24110
24454
|
var ServerUpdateActionResultSchema = object({
|
|
24111
24455
|
accepted: boolean(),
|
|
24112
24456
|
targetVersion: string().nullable(),
|
|
24113
24457
|
/** True when a graceful restart was scheduled to apply the change. */
|
|
24114
24458
|
restarting: boolean(),
|
|
24115
|
-
message: string()
|
|
24459
|
+
message: string(),
|
|
24460
|
+
/** See {@link ServerUpdateOutcomeSchema}. Optional for version skew. */
|
|
24461
|
+
outcome: ServerUpdateOutcomeSchema.optional(),
|
|
24462
|
+
/**
|
|
24463
|
+
* Stable, greppable cause for `refused` / `failed` (the engine's
|
|
24464
|
+
* `UpdateRefusalReason`, `staging-failed`, …). `null` on success.
|
|
24465
|
+
*/
|
|
24466
|
+
reason: string().nullable().optional(),
|
|
24467
|
+
/**
|
|
24468
|
+
* The failure itself, verbatim enough to act on (the npm output for a
|
|
24469
|
+
* staging install). Non-null only for `failed`.
|
|
24470
|
+
*/
|
|
24471
|
+
error: string().nullable().optional()
|
|
24116
24472
|
});
|
|
24117
24473
|
method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), ServerUpdateCheckResultSchema, {
|
|
24118
24474
|
kind: "mutation",
|
|
@@ -25221,6 +25577,18 @@ method(VectorDeclareIndexInputSchema, _void(), {
|
|
|
25221
25577
|
kind: "mutation",
|
|
25222
25578
|
auth: "admin"
|
|
25223
25579
|
}), method(VectorStatsInputSchema, VectorStatsResultSchema, { auth: "admin" });
|
|
25580
|
+
_enum([
|
|
25581
|
+
"queue-full",
|
|
25582
|
+
"camera-backoff",
|
|
25583
|
+
"sleeping",
|
|
25584
|
+
"camera-refused",
|
|
25585
|
+
"no-keyframe",
|
|
25586
|
+
"no-catalog-row",
|
|
25587
|
+
"unsupported",
|
|
25588
|
+
"unknown-device",
|
|
25589
|
+
"uid-missing"
|
|
25590
|
+
]);
|
|
25591
|
+
_enum(["deferred", "final"]);
|
|
25224
25592
|
var ClipSchema = object({
|
|
25225
25593
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
25226
25594
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -25295,29 +25663,455 @@ var ClipSchema = object({
|
|
|
25295
25663
|
holes: array(object({
|
|
25296
25664
|
startMs: number(),
|
|
25297
25665
|
endMs: number()
|
|
25298
|
-
})).optional()
|
|
25666
|
+
})).optional(),
|
|
25667
|
+
/**
|
|
25668
|
+
* A URL that MAY produce this clip's still, asked only for rows actually on
|
|
25669
|
+
* screen. The opposite of {@link ClipSchema.thumbnail}: that one is VOUCHED
|
|
25670
|
+
* (a JPEG is already on disk), this one is an offer. The route answers 200
|
|
25671
|
+
* with the image, or 204 with `x-camstack-reason` when it could not mint one
|
|
25672
|
+
* — a surface latches that refusal to the instant rather than retrying.
|
|
25673
|
+
*
|
|
25674
|
+
* Never both: a clip with a vouched `thumbnail` needs no mint.
|
|
25675
|
+
*/
|
|
25676
|
+
thumbnailMint: string().optional(),
|
|
25677
|
+
/**
|
|
25678
|
+
* Why no still will be produced for this clip RIGHT NOW — set when the
|
|
25679
|
+
* provider already knows, so the surface draws the glyph and the reason
|
|
25680
|
+
* instead of firing a mint that cannot succeed.
|
|
25681
|
+
*
|
|
25682
|
+
* `sleeping` — a standalone battery camera; a read would be a wake (D549 4).
|
|
25683
|
+
* `camera-refused` — the camera answered the CoverPreview with a refusal.
|
|
25684
|
+
* `no-keyframe` — the window holds no decodable I-frame (an event that sits
|
|
25685
|
+
* inside no file is the measured case).
|
|
25686
|
+
* `unsupported` — this source cannot mint stills at all.
|
|
25687
|
+
*/
|
|
25688
|
+
thumbnailUnavailable: object({ reason: _enum([
|
|
25689
|
+
"sleeping",
|
|
25690
|
+
"camera-refused",
|
|
25691
|
+
"no-keyframe",
|
|
25692
|
+
"unsupported"
|
|
25693
|
+
]) }).optional(),
|
|
25694
|
+
/**
|
|
25695
|
+
* When the catalog this row came from was last CONFIRMED against the device.
|
|
25696
|
+
* Absent means "this row was read live". A persisted catalog served while a
|
|
25697
|
+
* camera sleeps carries the age it really has — a cached list is never drawn
|
|
25698
|
+
* as current (D549 13).
|
|
25699
|
+
*/
|
|
25700
|
+
catalogAsOf: number().optional(),
|
|
25701
|
+
/**
|
|
25702
|
+
* The camera's OWN type strings for this clip, all of them, unmapped
|
|
25703
|
+
* (`md`, `people`, `dog_cat`, `sched`, …). Kept beside {@link labels}
|
|
25704
|
+
* because a firmware inventing a type must not vanish: the mapping into our
|
|
25705
|
+
* filter vocabulary is lossy on purpose and this is the lossless copy
|
|
25706
|
+
* (D549 20).
|
|
25707
|
+
*/
|
|
25708
|
+
nativeTypes: array(string()).optional(),
|
|
25709
|
+
/**
|
|
25710
|
+
* What the subject DID — `crossline`, `intrude`, `loitering`. A behaviour
|
|
25711
|
+
* travels BESIDE a class, never instead of one, and the class filter ignores
|
|
25712
|
+
* it: "a person crossed a line" is still a person (D549 20).
|
|
25713
|
+
*/
|
|
25714
|
+
behaviours: array(string()).optional(),
|
|
25715
|
+
/**
|
|
25716
|
+
* The same recording as two files — the sub twin (what the row and its
|
|
25717
|
+
* thumbnail are) and its main twin, matched at listing time so a quality
|
|
25718
|
+
* change never re-searches the camera (D549 15). `getClipPlayback`'s
|
|
25719
|
+
* `profile` picks between them: `low | mid` → sub, `high` → main.
|
|
25720
|
+
*/
|
|
25721
|
+
streams: object({
|
|
25722
|
+
sub: object({
|
|
25723
|
+
id: string(),
|
|
25724
|
+
bytes: number().optional()
|
|
25725
|
+
}).optional(),
|
|
25726
|
+
main: object({
|
|
25727
|
+
id: string(),
|
|
25728
|
+
bytes: number().optional()
|
|
25729
|
+
}).optional()
|
|
25730
|
+
}).optional(),
|
|
25731
|
+
/** The camera says it holds a sub-stream copy of this recording. */
|
|
25732
|
+
supportSub: boolean().optional(),
|
|
25733
|
+
/**
|
|
25734
|
+
* Whether this row has bytes behind it. ABSENT means yes — every clip that
|
|
25735
|
+
* IS a file is playable, and only a source that lists EVENTS can produce a
|
|
25736
|
+
* row with nothing to play (the measured case: 1 of 29 hub events on 3628
|
|
25737
|
+
* fell inside no file). Such a row is shown, never dropped and never offered
|
|
25738
|
+
* as playable-then-failing (D549 19).
|
|
25739
|
+
*/
|
|
25740
|
+
playable: boolean().optional(),
|
|
25741
|
+
/** Why {@link playable} is false, verbatim (`no-file-for-window`). */
|
|
25742
|
+
unplayableReason: string().optional(),
|
|
25743
|
+
/**
|
|
25744
|
+
* This clip is STILL BEING WRITTEN, so {@link ClipSchema.timeRange}`.endMs`
|
|
25745
|
+
* is not its end.
|
|
25746
|
+
*
|
|
25747
|
+
* Absent — the common case — means the row is closed and its `endMs` is the
|
|
25748
|
+
* end of the recording. Present and `true` means the source told us the file
|
|
25749
|
+
* has no end yet, and the `endMs` we carry is whatever the camera's index
|
|
25750
|
+
* entry happened to hold: on a Reolink E1 Outdoor PoE (592, measured
|
|
25751
|
+
* 2026-09-20) the newest file `…_192927_000000_…_0.mp4` reported an `endTime`
|
|
25752
|
+
* of 19:29:56 and STILL reported it eight minutes later, while the file went
|
|
25753
|
+
* on growing. A surface that drew 29 s there was lying about a clip that
|
|
25754
|
+
* plays for minutes, on the one row the operator looks at first.
|
|
25755
|
+
*
|
|
25756
|
+
* `endMs` is deliberately still a number: it is the best bound anything has
|
|
25757
|
+
* for a mint window or a byte fetch, and every consumer already requires it.
|
|
25758
|
+
* This flag says what it is WORTH, and a duration is not drawn from it.
|
|
25759
|
+
*/
|
|
25760
|
+
inProgress: boolean().optional()
|
|
25299
25761
|
});
|
|
25300
25762
|
var ClipPlaybackSchema = object({
|
|
25301
|
-
/**
|
|
25763
|
+
/**
|
|
25764
|
+
* A media URL through the hub data-plane. {@link ClipPlaybackSchema.format}
|
|
25765
|
+
* says what kind — an HLS master playlist for a recording-derived clip, a
|
|
25766
|
+
* progressive MP4 for a native file the vendor addon muxed and serves with
|
|
25767
|
+
* `Range`. The URL is NOT a credential: both planes are registered
|
|
25768
|
+
* `access: 'authenticated'` and the hub's `/addon/<id>/<prefix>` proxy takes
|
|
25769
|
+
* the session cookie, so nothing is appended to it (D549 22).
|
|
25770
|
+
*/
|
|
25302
25771
|
playbackUrl: string(),
|
|
25772
|
+
/**
|
|
25773
|
+
* How to play {@link playbackUrl}. Absent means `hls` — the shape every
|
|
25774
|
+
* existing consumer already assumes. A player that branches on this is the
|
|
25775
|
+
* one change a native clip needs; a player that ignores it will hand an MP4
|
|
25776
|
+
* to hls.js and fail parsing it as a manifest.
|
|
25777
|
+
*/
|
|
25778
|
+
format: _enum(["hls", "mp4"]).optional(),
|
|
25779
|
+
/**
|
|
25780
|
+
* Which twin was actually served. A clip that holds only one stream answers
|
|
25781
|
+
* with the one it has, and the surface SAYS so — a missing main twin is
|
|
25782
|
+
* never served silently as if it were the asked-for quality (D549 15).
|
|
25783
|
+
*/
|
|
25784
|
+
served: CamProfileSchema.optional(),
|
|
25303
25785
|
/** Optional LAN/remote alternates for the same clip. */
|
|
25304
25786
|
playbackEndpoints: array(string()).optional(),
|
|
25305
25787
|
token: string().optional()
|
|
25306
25788
|
});
|
|
25789
|
+
/**
|
|
25790
|
+
* Why a source cannot answer right now — per SOURCE, never fleet-wide.
|
|
25791
|
+
*
|
|
25792
|
+
* `ok` is the only state whose clip list may be read as complete. The other
|
|
25793
|
+
* three exist because an empty list from a sleeping camera reads as "this
|
|
25794
|
+
* camera has no recordings", which is the defect this whole line of work is
|
|
25795
|
+
* about: a battery camera nobody woke (`sleeping`), a camera that could not be
|
|
25796
|
+
* reached at all (`unreachable`, also what a provider that THREW reports), a
|
|
25797
|
+
* camera whose SD card is not mounted (`no-storage`, measured on 640 —
|
|
25798
|
+
* `HddInfo mount 0`, honestly nothing to list rather than "no clips"), and a
|
|
25799
|
+
* camera whose OWN index disagrees with its OWN calendar (`index-empty`,
|
|
25800
|
+
* measured on 618: the calendar marks 3–20 September, days 6–20 list zero
|
|
25801
|
+
* files on both streams and both filters, with 177 GB free). That last one is
|
|
25802
|
+
* a defect ON THE CAMERA, and the only honest thing a provider can do is say
|
|
25803
|
+
* which days it asked for and got nothing — "no clips" would be a lie about a
|
|
25804
|
+
* card that is full of them.
|
|
25805
|
+
*/
|
|
25806
|
+
/**
|
|
25807
|
+
* The operator's authorisation to WAKE a sleeping camera for one clip read.
|
|
25808
|
+
*
|
|
25809
|
+
* ONE value, absent by default, and it is a `force`-shaped operator signal in
|
|
25810
|
+
* exactly the sense `snapshot-wake-gate.ts` uses the word: *"`snapshot.getSnapshot`'s
|
|
25811
|
+
* `force` flag, and nothing else… a background caller must never set it…
|
|
25812
|
+
* Stale but honest beats woken"*. A scheduler, a retry, a reconcile and a
|
|
25813
|
+
* prefetch never set it; a surface sets it only behind the same confirm the
|
|
25814
|
+
* "Wake and refresh" gesture uses, and it is refused below 15 % battery
|
|
25815
|
+
* exactly as that gesture is.
|
|
25816
|
+
*
|
|
25817
|
+
* The gate is decided BEFORE `getApi()`, because on UDP the login IS the wake
|
|
25818
|
+
* (D549) — a read that opened a session and then checked would have woken the
|
|
25819
|
+
* camera to find out it was not allowed to.
|
|
25820
|
+
*
|
|
25821
|
+
* **One authorised yes is one wake.** `next-natural` — take the clip the next
|
|
25822
|
+
* time the camera is awake for its own reasons — is deliberately not a member:
|
|
25823
|
+
* it was proposed, it is free on the battery, and the operator declined it on
|
|
25824
|
+
* 2026-09-20 (*"Quando un export viene richiesto si sveglia la camera."*,
|
|
25825
|
+
* D558 "Considered and not taken").
|
|
25826
|
+
*/
|
|
25827
|
+
var ClipWakeSchema = _enum(["authorised"]);
|
|
25828
|
+
/**
|
|
25829
|
+
* A clip's finished bytes, inline — the twin of `recordingExport.readExportBytes`.
|
|
25830
|
+
*
|
|
25831
|
+
* `bytes` is the DECODED length, so nobody infers it from the base64 length,
|
|
25832
|
+
* and `served` says which twin the caller actually got.
|
|
25833
|
+
*/
|
|
25834
|
+
var ClipBytesSchema = object({
|
|
25835
|
+
base64: string(),
|
|
25836
|
+
contentType: string(),
|
|
25837
|
+
/** Suggested filename, extension included. */
|
|
25838
|
+
name: string(),
|
|
25839
|
+
bytes: number().int().nonnegative(),
|
|
25840
|
+
/**
|
|
25841
|
+
* Which twin was actually served — the same contract
|
|
25842
|
+
* {@link ClipPlaybackSchema.served} carries, and REQUIRED here because the
|
|
25843
|
+
* export record persists it: a row read a week later must say the same thing
|
|
25844
|
+
* the panel said at the moment of the tap. A missing main twin is never
|
|
25845
|
+
* served silently as if it were the asked-for quality (D549 15).
|
|
25846
|
+
*/
|
|
25847
|
+
served: CamProfileSchema,
|
|
25848
|
+
/**
|
|
25849
|
+
* The DECODED duration of the delivered file, when the fetch measured one.
|
|
25850
|
+
*
|
|
25851
|
+
* A clip fetch verifies its own completion against the catalog row and
|
|
25852
|
+
* retries a materially short pass (D568); this is that measurement, carried
|
|
25853
|
+
* so a consumer can say the same thing rather than re-deriving it. Absent
|
|
25854
|
+
* when the producer did not measure — never zero, which would say the file
|
|
25855
|
+
* is empty.
|
|
25856
|
+
*/
|
|
25857
|
+
durationMs: number().positive().optional()
|
|
25858
|
+
});
|
|
25859
|
+
/**
|
|
25860
|
+
* Where a clip's STREAM can be dialled (D597) — the answer to
|
|
25861
|
+
* {@link videoclipsCapability.methods.dialClipStream}.
|
|
25862
|
+
*
|
|
25863
|
+
* `url` is plain `http://` on the provider's own token-gated listener, one
|
|
25864
|
+
* shot, short-lived. It is NOT the hub-proxied `/addon/<id>/clip-stream/…`
|
|
25865
|
+
* path: that plane takes only a user credential, which an addon does not
|
|
25866
|
+
* hold, and the framework offers no addon→addon byte transport — the one
|
|
25867
|
+
* precedent is the broker's own restream URLs (`getStreamWithCodec`), which
|
|
25868
|
+
* a consumer dials directly, and this is that precedent one addon over.
|
|
25869
|
+
*/
|
|
25870
|
+
/**
|
|
25871
|
+
* The audio a clip stream can be asked for, and the audio it carries.
|
|
25872
|
+
*
|
|
25873
|
+
* The camera stores AAC and no WebRTC session speaks it, so a stream that
|
|
25874
|
+
* carries sound carries it TRANSCODED, muxed as a second track beside the
|
|
25875
|
+
* video by the one ffmpeg the provider already runs (D600). `opus` is
|
|
25876
|
+
* 48 kHz mono, `pcmu` is 8 kHz µ-law — the two codecs a broker session
|
|
25877
|
+
* negotiates — and `none` is a stream asked for, or served, without sound.
|
|
25878
|
+
*/
|
|
25879
|
+
var ClipStreamAudioSchema = _enum([
|
|
25880
|
+
"opus",
|
|
25881
|
+
"pcmu",
|
|
25882
|
+
"none"
|
|
25883
|
+
]);
|
|
25884
|
+
/**
|
|
25885
|
+
* Why a stream that was ASKED for sound is not carrying it (D600).
|
|
25886
|
+
*
|
|
25887
|
+
* Only ever set beside `servedAudio: 'none'` after a non-`none` ask. Absent
|
|
25888
|
+
* means the ask was honoured, or there was no ask.
|
|
25889
|
+
*
|
|
25890
|
+
* - `not-requested` — the caller asked for `none`.
|
|
25891
|
+
* - `unsupported-ask` — the provider cannot produce that codec.
|
|
25892
|
+
*
|
|
25893
|
+
* Whether the RECORDING has sound at all is not knowable here: the camera
|
|
25894
|
+
* declares no audio track before its first frame arrives. That answer is in
|
|
25895
|
+
* the stream itself — an fMP4 with no audio track — and the broker reads it
|
|
25896
|
+
* there. A dial that says `opus` promises the codec, never the content.
|
|
25897
|
+
*/
|
|
25898
|
+
var ClipStreamAudioReasonSchema = _enum(["not-requested", "unsupported-ask"]);
|
|
25899
|
+
var ClipStreamDialSchema = object({
|
|
25900
|
+
url: string().min(1),
|
|
25901
|
+
/** After this the token is gone and the URL answers 404 `dial-expired`. */
|
|
25902
|
+
expiresAtMs: number(),
|
|
25903
|
+
/** What the stream IS — a consumer that cannot honour it must not dial. */
|
|
25904
|
+
transport: literal("forward-only"),
|
|
25905
|
+
/**
|
|
25906
|
+
* Which twin the stream ACTUALLY carries — not what was asked for.
|
|
25907
|
+
*
|
|
25908
|
+
* The stream serves every profile (D599), and a recording whose main twin
|
|
25909
|
+
* does not exist is streamed as its sub copy, which this field reports as
|
|
25910
|
+
* `low`. That is the whole reason the dial no longer refuses `high`:
|
|
25911
|
+
* stating what was served is what makes accepting honest, where a silent
|
|
25912
|
+
* substitution would be the 33x lie D576 forbids. Stated rather than
|
|
25913
|
+
* assumed, the way {@link ClipPlaybackSchema.served} is (D549 15).
|
|
25914
|
+
*/
|
|
25915
|
+
served: CamProfileSchema,
|
|
25916
|
+
/**
|
|
25917
|
+
* Which audio the stream will ACTUALLY carry — `served`'s companion for
|
|
25918
|
+
* sound, and the same reasoning (D599, D600).
|
|
25919
|
+
*
|
|
25920
|
+
* The broker asks for the codec its WebRTC session negotiated, and the
|
|
25921
|
+
* provider either muxes that codec or carries none. Stating it is what
|
|
25922
|
+
* lets the broker wire the matching sink before the first byte, and what
|
|
25923
|
+
* keeps a session from being handed a codec it cannot send — the audio
|
|
25924
|
+
* shape of the substitution `served` exists to prevent.
|
|
25925
|
+
*
|
|
25926
|
+
* It is a promise about the CODEC, not about the content: a recording with
|
|
25927
|
+
* no microphone track answers `opus` here and then arrives with no audio
|
|
25928
|
+
* track at all. That second answer is in the stream, and the broker reads
|
|
25929
|
+
* it from the `moov`.
|
|
25930
|
+
*/
|
|
25931
|
+
servedAudio: ClipStreamAudioSchema,
|
|
25932
|
+
/** Why `servedAudio` is `none` although sound was asked for. */
|
|
25933
|
+
audioReason: ClipStreamAudioReasonSchema.optional()
|
|
25934
|
+
});
|
|
25935
|
+
var ClipSourceAvailabilitySchema = object({
|
|
25936
|
+
state: _enum([
|
|
25937
|
+
"ok",
|
|
25938
|
+
"sleeping",
|
|
25939
|
+
"unreachable",
|
|
25940
|
+
"no-storage",
|
|
25941
|
+
"index-empty"
|
|
25942
|
+
]),
|
|
25943
|
+
/** Free text, shown verbatim. Names the camera's own refusal when there is one. */
|
|
25944
|
+
reason: string().optional(),
|
|
25945
|
+
/** When this source's catalog was last CONFIRMED. A cached list is never
|
|
25946
|
+
* drawn as current: the surface shows the age whenever it is older than the
|
|
25947
|
+
* refresh interval. */
|
|
25948
|
+
catalogAsOf: number().optional()
|
|
25949
|
+
});
|
|
25950
|
+
/**
|
|
25951
|
+
* One SOURCE of clips for a camera — a row of the picker, and the namespace
|
|
25952
|
+
* every clip id from it is prefixed with (`analytics`,
|
|
25953
|
+
* `native:reolink:onboard`, `hksv`, …).
|
|
25954
|
+
*
|
|
25955
|
+
* A provider lists the sources IT serves for that device, and answers for each
|
|
25956
|
+
* of them whether it can answer at all. A provider with nothing to offer on a
|
|
25957
|
+
* camera returns `[]` — it is not that camera's business.
|
|
25958
|
+
*/
|
|
25959
|
+
var ClipSourceSchema = object({
|
|
25960
|
+
/** The value this source stamps on {@link ClipSchema.source}, and the prefix
|
|
25961
|
+
* of every clip id it mints. `getClipPlayback` routes on it. */
|
|
25962
|
+
source: string(),
|
|
25963
|
+
/** Operator-facing name of the source ("CamStack recordings", "SD card"). */
|
|
25964
|
+
label: string(),
|
|
25965
|
+
/**
|
|
25966
|
+
* The addon that SERVES this row.
|
|
25967
|
+
*
|
|
25968
|
+
* A surface cannot otherwise resolve a source to who answers it, and the
|
|
25969
|
+
* alternative — a `native:reolink:* → provider-reolink` table inside the
|
|
25970
|
+
* widget — is a second authority on provider identity living in the one
|
|
25971
|
+
* package with no business knowing it, wrong the day a third source appears
|
|
25972
|
+
* (D557). One addon may serve SEVERAL sources, which is why the per-device
|
|
25973
|
+
* switch is keyed by SOURCE and not by this (D555) — `addon-provider-reolink`
|
|
25974
|
+
* served a hub child two of them until the two views were measured to be one
|
|
25975
|
+
* store read twice (D555) and then read ONE way (D565).
|
|
25976
|
+
*
|
|
25977
|
+
* Optional for version skew only. The collection dispatcher stamps it from
|
|
25978
|
+
* the registry, so a row that travelled through the fan-out carries the
|
|
25979
|
+
* authoritative id whatever the provider filled in.
|
|
25980
|
+
*/
|
|
25981
|
+
addonId: string().optional(),
|
|
25982
|
+
availability: ClipSourceAvailabilitySchema
|
|
25983
|
+
});
|
|
25307
25984
|
DeviceType.Camera, method(object({
|
|
25308
25985
|
deviceId: number(),
|
|
25309
25986
|
since: number(),
|
|
25310
25987
|
until: number(),
|
|
25311
|
-
limit: number().int().positive().optional()
|
|
25988
|
+
limit: number().int().positive().optional(),
|
|
25989
|
+
/**
|
|
25990
|
+
* WHICH provider to ask — the `addonId` a {@link ClipSourceSchema} row
|
|
25991
|
+
* carries, never a source id and never a list. **Required** (D554 amended).
|
|
25992
|
+
*
|
|
25993
|
+
* A provider the device is not bound to is refused by name rather than
|
|
25994
|
+
* answered by another one (D552's `rejectUnresolvedAddonPin` rule).
|
|
25995
|
+
*
|
|
25996
|
+
* It was optional, documented as "absent means the device's BOUND
|
|
25997
|
+
* provider, which is CamStack on every camera". No code implemented
|
|
25998
|
+
* that. Measured on the live hub 2026-09-20 — device 592, bound to
|
|
25999
|
+
* `recorder` AND `provider-reolink` — a bare call with `limit: 3`
|
|
26000
|
+
* answered SIX rows, three from each source, merged newest-first:
|
|
26001
|
+
* `device-collection-dispatch.ts` simply left the fan-out un-narrowed,
|
|
26002
|
+
* so absence bought the union this method exists not to be, and
|
|
26003
|
+
* `limit` meant `limit × sources`.
|
|
26004
|
+
*
|
|
26005
|
+
* There is nothing to restore the default to. `getBindings` answers a
|
|
26006
|
+
* collection cap with a DERIVED, PLURAL set (D554 amended, step 0);
|
|
26007
|
+
* `setWrapperActive` — the singleton authority that could name one —
|
|
26008
|
+
* throws for a collection cap by design. Naming CamStack here instead
|
|
26009
|
+
* would privilege one addon by id inside a surface whose premise is
|
|
26010
|
+
* that sources are peers, and would be wrong on the first camera with
|
|
26011
|
+
* no recorder binding. So absence is REFUSED, in the schema, where the
|
|
26012
|
+
* generated types make it unomittable rather than merely discouraged.
|
|
26013
|
+
*
|
|
26014
|
+
* The default belongs to the SURFACE, which has the `listSources` rows
|
|
26015
|
+
* and can say which one it picked (D569 § 1–3; the viewer already
|
|
26016
|
+
* always sends this).
|
|
26017
|
+
*
|
|
26018
|
+
* It replaced `sources?: string[]`, a VIEW filter over source ids that
|
|
26019
|
+
* assumed the answer was a fan-out over everything a camera has. The
|
|
26020
|
+
* operator settled otherwise on 2026-09-20 — *"l'utilizzatore è uno
|
|
26021
|
+
* solo"* — so the list is asked of one provider at a time and there is
|
|
26022
|
+
* nothing to filter out of it.
|
|
26023
|
+
*/
|
|
26024
|
+
provider: string().min(1)
|
|
25312
26025
|
}), array(ClipSchema).readonly(), {
|
|
25313
26026
|
kind: "query",
|
|
25314
|
-
auth: "
|
|
26027
|
+
auth: "protected"
|
|
26028
|
+
}), method(object({ deviceId: number() }), array(ClipSourceSchema).readonly(), {
|
|
26029
|
+
kind: "query",
|
|
26030
|
+
auth: "protected"
|
|
25315
26031
|
}), method(object({
|
|
25316
26032
|
deviceId: number(),
|
|
25317
|
-
clipId: string()
|
|
26033
|
+
clipId: string(),
|
|
26034
|
+
/**
|
|
26035
|
+
* Which twin to serve, on the ONE quality scale the system already has
|
|
26036
|
+
* (`CamProfileSchema`). `low | mid` → the sub file, `high` → the main
|
|
26037
|
+
* twin; both ids are already on the row so this never re-searches the
|
|
26038
|
+
* camera. Absent means the provider's own default (the sub file, which
|
|
26039
|
+
* every source is measured to hold).
|
|
26040
|
+
*
|
|
26041
|
+
* `auto` is deliberately NOT accepted here: a stored file has no
|
|
26042
|
+
* broker session, so the adaptive tier cannot be resolved for it. The
|
|
26043
|
+
* viewer resolves `auto` to a profile the same way live does, before
|
|
26044
|
+
* it calls (D549 15).
|
|
26045
|
+
*/
|
|
26046
|
+
profile: CamProfileSchema.optional()
|
|
25318
26047
|
}), ClipPlaybackSchema, {
|
|
25319
26048
|
kind: "query",
|
|
25320
|
-
auth: "
|
|
26049
|
+
auth: "protected"
|
|
26050
|
+
}), optionalMethod(object({
|
|
26051
|
+
deviceId: number(),
|
|
26052
|
+
clipId: string().min(1),
|
|
26053
|
+
/**
|
|
26054
|
+
* WHICH provider holds the bytes — the `addonId` a
|
|
26055
|
+
* {@link ClipSourceSchema} row carries. **Required**, for the reason
|
|
26056
|
+
* `listClips` states one method above and for a second one this method
|
|
26057
|
+
* learned the hard way.
|
|
26058
|
+
*
|
|
26059
|
+
* The caller is the stream broker, and the broker lives in
|
|
26060
|
+
* `addon-pipeline` — the same addon that registers the RECORDER's
|
|
26061
|
+
* videoclips provider. With no provider named, the in-process
|
|
26062
|
+
* resolution handed it its own sibling, which implements this method
|
|
26063
|
+
* solely to explain that an analytics visit has no file behind it. So
|
|
26064
|
+
* every onboard clip answered with that sentence, a flash of video and
|
|
26065
|
+
* nothing more, while the SAME call from outside the addon reached the
|
|
26066
|
+
* right provider and returned the bytes (measured on 3628,
|
|
26067
|
+
* 2026-09-21).
|
|
26068
|
+
*/
|
|
26069
|
+
provider: string().min(1),
|
|
26070
|
+
/**
|
|
26071
|
+
* Which twin to fetch, on the mapping D549 15 already fixed:
|
|
26072
|
+
* `low | mid` → the sub file, `high` → the main twin. `auto` is not
|
|
26073
|
+
* accepted here for the same reason it is not accepted by
|
|
26074
|
+
* `getClipPlayback` — a stored file has no broker session, so the
|
|
26075
|
+
* adaptive tier cannot be resolved for it.
|
|
26076
|
+
*/
|
|
26077
|
+
profile: CamProfileSchema.optional(),
|
|
26078
|
+
/**
|
|
26079
|
+
* The CALLER's byte bound, so an over-size clip is refused before it is
|
|
26080
|
+
* read and encoded rather than after. Capped by
|
|
26081
|
+
* {@link VIDEOCLIPS_MAX_READ_BYTES} whatever is passed; absent means
|
|
26082
|
+
* that ceiling.
|
|
26083
|
+
*/
|
|
26084
|
+
maxBytes: number().int().positive().optional(),
|
|
26085
|
+
/**
|
|
26086
|
+
* The operator's authorisation to wake a sleeping camera for this
|
|
26087
|
+
* read. Absent — the default — means a sleeping standalone battery
|
|
26088
|
+
* camera is REFUSED by name, before any session is opened.
|
|
26089
|
+
*/
|
|
26090
|
+
wake: ClipWakeSchema.optional()
|
|
26091
|
+
}), ClipBytesSchema, {
|
|
26092
|
+
kind: "query",
|
|
26093
|
+
auth: "protected"
|
|
26094
|
+
}), optionalMethod(object({
|
|
26095
|
+
deviceId: number(),
|
|
26096
|
+
clipId: string().min(1),
|
|
26097
|
+
/** WHICH provider holds the camera — see `readClipBytes.provider`. */
|
|
26098
|
+
provider: string().min(1),
|
|
26099
|
+
/** Which twin — `high` → the main file, everything else the sub (D599). */
|
|
26100
|
+
profile: CamProfileSchema.optional(),
|
|
26101
|
+
/**
|
|
26102
|
+
* Which audio codec the caller's session negotiated, so the provider
|
|
26103
|
+
* can mux it (D600). The ask travels on the DIAL because the mux is
|
|
26104
|
+
* spawned once, before the first byte, and cannot gain a track later.
|
|
26105
|
+
*
|
|
26106
|
+
* ABSENT IS `none`, never a guess: a caller that does not state an
|
|
26107
|
+
* ask is served the video-only stream that shipped first, so a broker
|
|
26108
|
+
* older than this field keeps working unchanged (D382 — an unstated
|
|
26109
|
+
* ask is zero).
|
|
26110
|
+
*/
|
|
26111
|
+
audio: ClipStreamAudioSchema.optional()
|
|
26112
|
+
}), ClipStreamDialSchema, {
|
|
26113
|
+
kind: "query",
|
|
26114
|
+
auth: "protected"
|
|
25321
26115
|
});
|
|
25322
26116
|
/**
|
|
25323
26117
|
* Optional client-side hints sent at session creation to help the provider
|
|
@@ -25389,6 +26183,104 @@ var WebrtcStreamChoiceSchema = object({
|
|
|
25389
26183
|
/** Bitrate in kbps (null for adaptive). */
|
|
25390
26184
|
bitrateKbps: number().nullable()
|
|
25391
26185
|
});
|
|
26186
|
+
/**
|
|
26187
|
+
* The last move the adaptive ladder made on this session, and the numbers it
|
|
26188
|
+
* rested on. Mirrors `LadderDecision` in the stream-broker's
|
|
26189
|
+
* `adaptive-controller.ts` — the log line's evidence, made readable.
|
|
26190
|
+
*
|
|
26191
|
+
* Nullability is load-bearing: `lossRatio: null` means the loss tracker had
|
|
26192
|
+
* no window to report, which is NOT a link with zero loss.
|
|
26193
|
+
*/
|
|
26194
|
+
var WebrtcLadderDecisionSchema = object({
|
|
26195
|
+
/** `loss` — RTCP / client-reported packet loss; `stall` — the picture
|
|
26196
|
+
* stopped moving; `healthy` — a promotion. */
|
|
26197
|
+
cause: _enum([
|
|
26198
|
+
"loss",
|
|
26199
|
+
"stall",
|
|
26200
|
+
"healthy"
|
|
26201
|
+
]),
|
|
26202
|
+
/** The rung the ladder moved TO. Null for a move that changed no rung
|
|
26203
|
+
* (an MTU reduction, a forced transcode-down at the bottom). */
|
|
26204
|
+
tier: CamProfileSchema.nullable(),
|
|
26205
|
+
/** Whether that move forced the egress transcode-down rung. */
|
|
26206
|
+
forcedTranscodeDown: boolean(),
|
|
26207
|
+
/** Loss ratio over the tracker's window at the moment of the decision. */
|
|
26208
|
+
lossRatio: number().nullable(),
|
|
26209
|
+
/** The client's reported downlink at that moment, in kbps. */
|
|
26210
|
+
clientKbps: number().nullable(),
|
|
26211
|
+
/** How long the stall detector had been seeing nothing arrive. */
|
|
26212
|
+
stalledForMs: number().nullable(),
|
|
26213
|
+
/** Whether the broker WAS sending while that was measured — the half that
|
|
26214
|
+
* separates "the camera stopped" from "the link stopped". */
|
|
26215
|
+
serverSending: boolean().nullable(),
|
|
26216
|
+
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
26217
|
+
* must not read like one from this second. */
|
|
26218
|
+
at: number()
|
|
26219
|
+
});
|
|
26220
|
+
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
26221
|
+
var WebrtcTierFailureSchema = object({
|
|
26222
|
+
tier: CamProfileSchema,
|
|
26223
|
+
failures: number().int().nonnegative()
|
|
26224
|
+
});
|
|
26225
|
+
/**
|
|
26226
|
+
* What the SERVER decided for one WebRTC session — the half the client cannot
|
|
26227
|
+
* measure. See `getSessionDebug`.
|
|
26228
|
+
*/
|
|
26229
|
+
var WebrtcSessionDebugSchema = object({
|
|
26230
|
+
sessionId: string(),
|
|
26231
|
+
/** Whether the session still exists here. Everything below is null when
|
|
26232
|
+
* it does not — a dead session has no measurements, not zero ones. */
|
|
26233
|
+
alive: boolean(),
|
|
26234
|
+
/** Epoch ms this snapshot was taken, so the overlay can show its age. */
|
|
26235
|
+
observedAt: number(),
|
|
26236
|
+
/** The broker actually feeding this session (`<deviceId>/<camStreamId>`). */
|
|
26237
|
+
brokerId: string().nullable(),
|
|
26238
|
+
camStreamId: string().nullable(),
|
|
26239
|
+
/** `derived` = this hub BUILT the rung being served (a transcode/scale of
|
|
26240
|
+
* another stream); `native` = the camera's own encode. */
|
|
26241
|
+
sourceKind: _enum([
|
|
26242
|
+
"native",
|
|
26243
|
+
"derived",
|
|
26244
|
+
"unknown"
|
|
26245
|
+
]),
|
|
26246
|
+
/** Whether the ladder is running at all (an `adaptive` target). */
|
|
26247
|
+
adaptive: boolean(),
|
|
26248
|
+
/** The rung `auto` ACTUALLY resolved to right now. Null when the broker
|
|
26249
|
+
* is bound to no profile slot (a manually-activated stream). */
|
|
26250
|
+
servedTier: CamProfileSchema.nullable(),
|
|
26251
|
+
/** Which node ingests the source. Null when unresolved. */
|
|
26252
|
+
ingestNodeId: string().nullable(),
|
|
26253
|
+
/** Whether that node is a remote one (the media crosses a node boundary
|
|
26254
|
+
* before it reaches this session). Null when unresolved. */
|
|
26255
|
+
ingestIsRemote: boolean().nullable(),
|
|
26256
|
+
/** Codec off the source, and the one negotiated with this client. */
|
|
26257
|
+
sourceCodec: string().nullable(),
|
|
26258
|
+
negotiatedCodec: string().nullable(),
|
|
26259
|
+
/** Whether the EGRESS leg re-encodes for this client or forwards the
|
|
26260
|
+
* source bytes untouched. */
|
|
26261
|
+
egressLeg: _enum([
|
|
26262
|
+
"copy",
|
|
26263
|
+
"transcode",
|
|
26264
|
+
"unknown"
|
|
26265
|
+
]),
|
|
26266
|
+
/** What the broker itself reports about the rung it is serving. */
|
|
26267
|
+
brokerStatus: string().nullable(),
|
|
26268
|
+
brokerCodec: string().nullable(),
|
|
26269
|
+
brokerBitrateKbps: number().nullable(),
|
|
26270
|
+
brokerInputFps: number().nullable(),
|
|
26271
|
+
brokerUptimeMs: number().nullable(),
|
|
26272
|
+
/** RTP packets this session has sent — the server's own "am I sending?". */
|
|
26273
|
+
rtpPacketsSent: number().nullable(),
|
|
26274
|
+
/** D42: the track may be carrying the recorded feeder, not live. The ladder
|
|
26275
|
+
* is suspended there, so a frozen picture in `recorded` mode means
|
|
26276
|
+
* something entirely different. */
|
|
26277
|
+
trackMode: _enum(["live", "recorded"]).nullable(),
|
|
26278
|
+
lastDecision: WebrtcLadderDecisionSchema.nullable(),
|
|
26279
|
+
tierFailures: array(WebrtcTierFailureSchema),
|
|
26280
|
+
/** Successful promotions in this viewer session (drives the progressive
|
|
26281
|
+
* healthy timer). Null when no ladder is running. */
|
|
26282
|
+
upgradeCount: number().nullable()
|
|
26283
|
+
});
|
|
25392
26284
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
25393
26285
|
deviceId: number().int().nonnegative(),
|
|
25394
26286
|
target: WebrtcStreamTargetSchema,
|
|
@@ -25553,7 +26445,10 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
|
|
|
25553
26445
|
* the same answer as "it ended": either way nothing is holding it up.
|
|
25554
26446
|
*/
|
|
25555
26447
|
alive: boolean()
|
|
25556
|
-
}))
|
|
26448
|
+
})), method(object({
|
|
26449
|
+
deviceId: number().int().nonnegative(),
|
|
26450
|
+
sessionId: string()
|
|
26451
|
+
}), WebrtcSessionDebugSchema, { kind: "query" });
|
|
25557
26452
|
object({
|
|
25558
26453
|
/** All accessory children of the parent. */
|
|
25559
26454
|
childDeviceIds: array(number()).readonly(),
|
|
@@ -27379,6 +28274,346 @@ var dayNightCapability = {
|
|
|
27379
28274
|
volatileStateFields: ["lastFetchedAt"]
|
|
27380
28275
|
};
|
|
27381
28276
|
/**
|
|
28277
|
+
* Vendor-neutral **onboard** recording + storage cap — what the CAMERA
|
|
28278
|
+
* writes to the CAMERA's own card, on the camera's own schedule.
|
|
28279
|
+
*
|
|
28280
|
+
* This is NOT `recording.cap.ts`. That one is CamStack's recorder: our
|
|
28281
|
+
* footage ledger, our storage locations, our retention. This one has a
|
|
28282
|
+
* different authority — the camera's firmware — and per D62 it stores
|
|
28283
|
+
* nothing of its own. Every value here is read from the camera and every
|
|
28284
|
+
* write goes back to the camera; there is no CamStack-side mirror that
|
|
28285
|
+
* could disagree with the device.
|
|
28286
|
+
*
|
|
28287
|
+
* ## One shape, two firmwares
|
|
28288
|
+
*
|
|
28289
|
+
* Measured 2026-09-22 against the live fleet:
|
|
28290
|
+
*
|
|
28291
|
+
* | fact | Hikvision (ISAPI) | Reolink (Baichuan) |
|
|
28292
|
+
* | --- | --- | --- |
|
|
28293
|
+
* | storage | `ContentMgmt/Storage` `<hdd>` rows: status, capacity, freeSpace (MB) | `getHddInfoList` (cmd 102): `mount`, `format`, `capacity` GB + `capacityM` MB remainder |
|
|
28294
|
+
* | tracks | several (101 **and** 103 on both 1436 and 3833), each with its own schedule | one per channel |
|
|
28295
|
+
* | schedule | per track, 7 `ScheduleAction` blocks: DayOfWeek + TimeOfDay range + ONE `ActionRecordingMode` | per trigger type, a 168-char weekly HOUR mask |
|
|
28296
|
+
* | triggers | `CMR`, `MOTION` | `Normal`, `MD`, `people`, `vehicle`, `dog_cat`, `crossline`, `intrude`, `loitering` |
|
|
28297
|
+
* | pre-record | `PreRecordTimeSeconds` | `preRecordTime` |
|
|
28298
|
+
* | post-record | `PostRecordTimeSeconds` | `recordDelayTime` |
|
|
28299
|
+
* | overwrite | per track `LoopEnable` | `cycle`, with `cycleList` enumerating the accepted values |
|
|
28300
|
+
* | segment length | not exposed on V5.7.1 | `packageTime` (minutes) |
|
|
28301
|
+
*
|
|
28302
|
+
* The two schedule models look different and are the same thing in
|
|
28303
|
+
* different coordinates: both answer "for this trigger, during which
|
|
28304
|
+
* weekly windows does the camera record". {@link RecordWindow} is that
|
|
28305
|
+
* question in one shape — Hikvision's ranges map straight onto it,
|
|
28306
|
+
* Reolink's mask expands into hour-aligned windows.
|
|
28307
|
+
*
|
|
28308
|
+
* ## Union, not intersection
|
|
28309
|
+
*
|
|
28310
|
+
* **The same fields exist on every camera.** What differs per device is
|
|
28311
|
+
* which VALUES that device accepts, and that is what {@link
|
|
28312
|
+
* RecordingOnboardOptions} reports — a `{ readable, writable, reason }`
|
|
28313
|
+
* per field plus the schedule's own limits. A control a camera cannot
|
|
28314
|
+
* honour is rendered DISABLED WITH ITS REASON, never missing and never
|
|
28315
|
+
* dead: disabled must not look like broken.
|
|
28316
|
+
*
|
|
28317
|
+
* ## Refusal by name
|
|
28318
|
+
*
|
|
28319
|
+
* A write a camera cannot honour is refused with a sentence the operator
|
|
28320
|
+
* can read — never accepted and dropped. Both providers refuse through
|
|
28321
|
+
* {@link describeOnboardRefusal}, so the vocabulary is one function and
|
|
28322
|
+
* one test, not two hand-written vendor opinions.
|
|
28323
|
+
*
|
|
28324
|
+
* Follows the D14 `deviceConfig` archetype (see `stream-params.cap.ts`):
|
|
28325
|
+
* `getOptions` advertises per-camera availability, `getStatus` (auto-
|
|
28326
|
+
* injected from `status`) reports the live values, and a single
|
|
28327
|
+
* `setSettings` mutation applies a partial change. No hand-written
|
|
28328
|
+
* settings-contribution methods.
|
|
28329
|
+
*/
|
|
28330
|
+
/**
|
|
28331
|
+
* What makes the camera start recording during a window.
|
|
28332
|
+
*
|
|
28333
|
+
* The union of both vendors' vocabularies. `continuous` is Hikvision's
|
|
28334
|
+
* `CMR` and Reolink's `Normal`; `motion` is `MOTION` / `MD`. The
|
|
28335
|
+
* object-class triggers are Reolink-only today and the smart-event ones
|
|
28336
|
+
* (`lineCrossing`, `intrusion`, `loitering`) are Reolink-only on the
|
|
28337
|
+
* firmwares measured — a camera that cannot record on a trigger simply
|
|
28338
|
+
* does not list it in `options.schedule.triggers`, and a window naming
|
|
28339
|
+
* it is REFUSED, not dropped.
|
|
28340
|
+
*/
|
|
28341
|
+
var RecordTriggerSchema = _enum([
|
|
28342
|
+
"continuous",
|
|
28343
|
+
"motion",
|
|
28344
|
+
"person",
|
|
28345
|
+
"vehicle",
|
|
28346
|
+
"animal",
|
|
28347
|
+
"lineCrossing",
|
|
28348
|
+
"intrusion",
|
|
28349
|
+
"loitering",
|
|
28350
|
+
"alarmInput"
|
|
28351
|
+
]);
|
|
28352
|
+
/**
|
|
28353
|
+
* One weekly recording window: "on `day`, from `startMinute` to
|
|
28354
|
+
* `endMinute`, record on `trigger`".
|
|
28355
|
+
*
|
|
28356
|
+
* `day` is 0 = Monday … 6 = Sunday (ISO order, which is also the order
|
|
28357
|
+
* both firmwares enumerate). Minutes are local camera time since
|
|
28358
|
+
* midnight; `endMinute` may be 1440, meaning end of day — that is
|
|
28359
|
+
* Hikvision's literal `24:00` and Reolink's 24th mask slot, and
|
|
28360
|
+
* collapsing it to 0 would turn a whole-day window into an empty one.
|
|
28361
|
+
*/
|
|
28362
|
+
var RecordWindowSchema = object({
|
|
28363
|
+
trigger: RecordTriggerSchema,
|
|
28364
|
+
day: number().int().min(0).max(6),
|
|
28365
|
+
startMinute: number().int().min(0).max(1439),
|
|
28366
|
+
endMinute: number().int().min(1).max(1440)
|
|
28367
|
+
});
|
|
28368
|
+
/** Status of one physical volume, as the camera itself describes it. */
|
|
28369
|
+
var OnboardStorageVolumeSchema = object({
|
|
28370
|
+
/** The camera's own id for the volume (`hdd/id`, Reolink `HddInfo/number`). */
|
|
28371
|
+
id: string(),
|
|
28372
|
+
/** The camera's own name for it, when it gives one (`hddName`). */
|
|
28373
|
+
label: string().optional(),
|
|
28374
|
+
status: _enum([
|
|
28375
|
+
"ok",
|
|
28376
|
+
"unformatted",
|
|
28377
|
+
"error",
|
|
28378
|
+
"offline",
|
|
28379
|
+
"unknown"
|
|
28380
|
+
]),
|
|
28381
|
+
/**
|
|
28382
|
+
* Total size in MB, or **null when the camera did not say**.
|
|
28383
|
+
*
|
|
28384
|
+
* Never 0 for an unreadable value: a measurement that failed is not a
|
|
28385
|
+
* measurement (D393), and a card whose size is unknown must not be
|
|
28386
|
+
* rendered as a card of size zero.
|
|
28387
|
+
*/
|
|
28388
|
+
capacityMb: number().nullable(),
|
|
28389
|
+
/**
|
|
28390
|
+
* Free space in MB, or null when unknown.
|
|
28391
|
+
*
|
|
28392
|
+
* **Not a proxy for "has footage".** Measured 2026-09-22: 1436 and
|
|
28393
|
+
* 1439 both report exactly 11776 MB free — the fixed reserve a looping
|
|
28394
|
+
* card converges on once it has wrapped. At loop steady state the
|
|
28395
|
+
* number is identical whether the camera recorded yesterday or stopped
|
|
28396
|
+
* a month ago.
|
|
28397
|
+
*/
|
|
28398
|
+
freeMb: number().nullable(),
|
|
28399
|
+
/** True when the camera reports the volume writable (`property` RW). */
|
|
28400
|
+
writable: boolean().optional()
|
|
28401
|
+
});
|
|
28402
|
+
/**
|
|
28403
|
+
* What the camera is doing with its own storage, right now.
|
|
28404
|
+
*
|
|
28405
|
+
* Every scalar is nullable and **null means the camera did not answer**,
|
|
28406
|
+
* never a default. A form that seeds `0` from an unanswered read invites
|
|
28407
|
+
* the operator to save that 0 back onto the camera.
|
|
28408
|
+
*/
|
|
28409
|
+
var RecordingOnboardStatusSchema = object({
|
|
28410
|
+
storage: discriminatedUnion("kind", [
|
|
28411
|
+
object({
|
|
28412
|
+
kind: literal("present"),
|
|
28413
|
+
volumes: array(OnboardStorageVolumeSchema)
|
|
28414
|
+
}),
|
|
28415
|
+
object({
|
|
28416
|
+
kind: literal("absent"),
|
|
28417
|
+
reason: string()
|
|
28418
|
+
}),
|
|
28419
|
+
object({
|
|
28420
|
+
kind: literal("unknown"),
|
|
28421
|
+
reason: string()
|
|
28422
|
+
})
|
|
28423
|
+
]),
|
|
28424
|
+
tracks: array(object({
|
|
28425
|
+
id: string(),
|
|
28426
|
+
enabled: boolean(),
|
|
28427
|
+
isVideo: boolean(),
|
|
28428
|
+
/** From the camera's own track description. Null when it does not say. */
|
|
28429
|
+
codec: string().nullable(),
|
|
28430
|
+
resolution: string().nullable(),
|
|
28431
|
+
/** Per-track overwrite flag, where the firmware keeps it per track. */
|
|
28432
|
+
overwriteWhenFull: boolean().nullable()
|
|
28433
|
+
})),
|
|
28434
|
+
/**
|
|
28435
|
+
* The track the write path targets — the enabled VIDEO one. Null when
|
|
28436
|
+
* no track could be identified, which is itself a refusal reason.
|
|
28437
|
+
*/
|
|
28438
|
+
primaryTrackId: string().nullable(),
|
|
28439
|
+
/** Master "record to the card at all" switch. */
|
|
28440
|
+
enabled: boolean().nullable(),
|
|
28441
|
+
overwriteWhenFull: boolean().nullable(),
|
|
28442
|
+
preRecordSec: number().nullable(),
|
|
28443
|
+
postRecordSec: number().nullable(),
|
|
28444
|
+
/** Length of one recorded file, in minutes. */
|
|
28445
|
+
segmentMinutes: number().nullable(),
|
|
28446
|
+
/** The primary track's weekly windows, flattened. */
|
|
28447
|
+
windows: array(RecordWindowSchema),
|
|
28448
|
+
/**
|
|
28449
|
+
* How many windows the camera described that CamStack could NOT read —
|
|
28450
|
+
* an unrecognised trigger, an unparseable clock, a weekday it does not
|
|
28451
|
+
* name.
|
|
28452
|
+
*
|
|
28453
|
+
* A dropped window is work the reader threw away, and a schedule that
|
|
28454
|
+
* silently shows fewer rows than the camera holds is how an operator
|
|
28455
|
+
* saves back a schedule shorter than the one they were looking at
|
|
28456
|
+
* (D391). Non-zero means the window list is INCOMPLETE and a write
|
|
28457
|
+
* that replaces it would delete what was not shown — which is why a
|
|
28458
|
+
* provider reporting a non-zero count also reports the schedule as not
|
|
28459
|
+
* writable.
|
|
28460
|
+
*/
|
|
28461
|
+
unreadableWindows: number(),
|
|
28462
|
+
/**
|
|
28463
|
+
* The camera is scheduled to record and has NO usable storage.
|
|
28464
|
+
*
|
|
28465
|
+
* A first-class fact because it is the fleet's most common silent
|
|
28466
|
+
* defect: measured 2026-09-22, 1441 and 3831 are both motion-recording
|
|
28467
|
+
* to a card that is not there. Neither the schedule nor the storage
|
|
28468
|
+
* read says anything wrong on its own; only the pair does.
|
|
28469
|
+
*/
|
|
28470
|
+
recordingToNowhere: boolean(),
|
|
28471
|
+
lastFetchedAt: number()
|
|
28472
|
+
});
|
|
28473
|
+
/** Numeric range descriptor — `{ min, max, step }` per the getOptions convention. */
|
|
28474
|
+
var RangeSchema = object({
|
|
28475
|
+
min: number(),
|
|
28476
|
+
max: number(),
|
|
28477
|
+
step: number()
|
|
28478
|
+
});
|
|
28479
|
+
/**
|
|
28480
|
+
* The values a camera actually takes for a numeric field, when they are a SET
|
|
28481
|
+
* rather than a range.
|
|
28482
|
+
*
|
|
28483
|
+
* `{min,max,step}` cannot say what these two firmwares do. Measured on 1436
|
|
28484
|
+
* (I91DN) on 2026-09-22 by writing each value and reading it back:
|
|
28485
|
+
*
|
|
28486
|
+
* - pre-record: `0, 5, 10, 15, 20, 25, 30` and `2147483647` (INT32_MAX, the
|
|
28487
|
+
* camera's "no limit" — `-1` and `4294967295` both land on it);
|
|
28488
|
+
* - post-record: `5, 10, 30, 60, 120, 300, 600`.
|
|
28489
|
+
*
|
|
28490
|
+
* Neither is expressible as a step: the first has a sentinel two billion away
|
|
28491
|
+
* from its neighbours, the second doubles and then jumps. A range that tried
|
|
28492
|
+
* would forbid values the camera takes AND permit values it silently replaces
|
|
28493
|
+
* with 5 — wrong in both directions at once.
|
|
28494
|
+
*
|
|
28495
|
+
* `sentinel` names the member that is not a duration, so a surface can render
|
|
28496
|
+
* "no limit" instead of `2147483647` seconds.
|
|
28497
|
+
*/
|
|
28498
|
+
var AllowedValuesSchema = object({
|
|
28499
|
+
values: array(number()).min(1),
|
|
28500
|
+
sentinel: object({
|
|
28501
|
+
value: number(),
|
|
28502
|
+
meaning: _enum(["no-limit", "disabled"])
|
|
28503
|
+
}).optional()
|
|
28504
|
+
});
|
|
28505
|
+
/**
|
|
28506
|
+
* Per-field availability on ONE camera.
|
|
28507
|
+
*
|
|
28508
|
+
* The field exists on every camera — this says whether this one can be
|
|
28509
|
+
* read and whether it can be written, and `reason` says why not when
|
|
28510
|
+
* either is false. The UI renders the control DISABLED with the reason
|
|
28511
|
+
* rather than hiding it, so a limitation is legible instead of looking
|
|
28512
|
+
* like a missing feature.
|
|
28513
|
+
*/
|
|
28514
|
+
var OnboardFieldSupportSchema = object({
|
|
28515
|
+
readable: boolean(),
|
|
28516
|
+
writable: boolean(),
|
|
28517
|
+
/** Required whenever `readable` or `writable` is false. */
|
|
28518
|
+
reason: string().optional()
|
|
28519
|
+
});
|
|
28520
|
+
/** What this camera's schedule model can express. */
|
|
28521
|
+
var OnboardScheduleSupportSchema = object({
|
|
28522
|
+
support: OnboardFieldSupportSchema,
|
|
28523
|
+
/**
|
|
28524
|
+
* The smallest time step the camera can express, in minutes.
|
|
28525
|
+
*
|
|
28526
|
+
* Hikvision takes arbitrary minutes (`00:05:00`–`23:57:00` observed on
|
|
28527
|
+
* 1436's track 103). Reolink's schedule is a 7×24 HOUR mask, so 60. A
|
|
28528
|
+
* window whose edges are not a multiple of this is REFUSED rather than
|
|
28529
|
+
* quietly rounded — rounding is how an operator's 06:30 becomes 06:00
|
|
28530
|
+
* and nothing says so.
|
|
28531
|
+
*/
|
|
28532
|
+
granularityMinutes: number(),
|
|
28533
|
+
/** Triggers this camera can record on. A window naming another is refused. */
|
|
28534
|
+
triggers: array(RecordTriggerSchema),
|
|
28535
|
+
/**
|
|
28536
|
+
* False when the camera stores ONE trigger per time range, so two
|
|
28537
|
+
* windows overlapping on the same day cannot carry different triggers.
|
|
28538
|
+
* True on Reolink, whose mask is per-trigger and independent.
|
|
28539
|
+
*/
|
|
28540
|
+
supportsOverlappingTriggers: boolean()
|
|
28541
|
+
});
|
|
28542
|
+
var RecordingOnboardOptionsSchema = object({
|
|
28543
|
+
enabled: OnboardFieldSupportSchema,
|
|
28544
|
+
overwriteWhenFull: OnboardFieldSupportSchema,
|
|
28545
|
+
preRecordSec: OnboardFieldSupportSchema,
|
|
28546
|
+
preRecordSecRange: RangeSchema.optional(),
|
|
28547
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
28548
|
+
preRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
28549
|
+
postRecordSec: OnboardFieldSupportSchema,
|
|
28550
|
+
postRecordSecRange: RangeSchema.optional(),
|
|
28551
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
28552
|
+
postRecordSecAllowed: AllowedValuesSchema.optional(),
|
|
28553
|
+
segmentMinutes: OnboardFieldSupportSchema,
|
|
28554
|
+
segmentMinutesRange: RangeSchema.optional(),
|
|
28555
|
+
/** Preferred over the range when the camera takes a SET, not a span. */
|
|
28556
|
+
segmentMinutesAllowed: AllowedValuesSchema.optional(),
|
|
28557
|
+
schedule: OnboardScheduleSupportSchema
|
|
28558
|
+
});
|
|
28559
|
+
/**
|
|
28560
|
+
* A partial change. Every field optional.
|
|
28561
|
+
*
|
|
28562
|
+
* Unlike the other `deviceConfig` caps, a provider here does **NOT**
|
|
28563
|
+
* silently ignore a field it cannot support — it refuses, by name,
|
|
28564
|
+
* through {@link describeOnboardRefusal}. Silence on a recording setting
|
|
28565
|
+
* is the failure D62 exists to prevent: the operator believes the camera
|
|
28566
|
+
* is recording the way the form says, and it is not.
|
|
28567
|
+
*/
|
|
28568
|
+
var RecordingOnboardPatchSchema = object({
|
|
28569
|
+
enabled: boolean().optional(),
|
|
28570
|
+
overwriteWhenFull: boolean().optional(),
|
|
28571
|
+
preRecordSec: number().optional(),
|
|
28572
|
+
postRecordSec: number().optional(),
|
|
28573
|
+
segmentMinutes: number().optional(),
|
|
28574
|
+
/** The complete new window set for the primary track — not a delta. */
|
|
28575
|
+
windows: array(RecordWindowSchema).optional()
|
|
28576
|
+
});
|
|
28577
|
+
var recordingOnboardCapability = {
|
|
28578
|
+
name: "recording-onboard",
|
|
28579
|
+
scope: "device",
|
|
28580
|
+
deviceNative: true,
|
|
28581
|
+
mode: "singleton",
|
|
28582
|
+
deviceTypes: [DeviceType.Camera],
|
|
28583
|
+
deviceConfig: { ui: {
|
|
28584
|
+
kind: "derived-form",
|
|
28585
|
+
builderId: "recording-onboard",
|
|
28586
|
+
tab: "recording"
|
|
28587
|
+
} },
|
|
28588
|
+
methods: {
|
|
28589
|
+
getOptions: method(object({ deviceId: number() }), RecordingOnboardOptionsSchema),
|
|
28590
|
+
setSettings: method(object({
|
|
28591
|
+
deviceId: number(),
|
|
28592
|
+
settings: RecordingOnboardPatchSchema
|
|
28593
|
+
}), _void(), {
|
|
28594
|
+
kind: "mutation",
|
|
28595
|
+
auth: "admin"
|
|
28596
|
+
})
|
|
28597
|
+
},
|
|
28598
|
+
status: {
|
|
28599
|
+
schema: RecordingOnboardStatusSchema,
|
|
28600
|
+
kind: "poll"
|
|
28601
|
+
},
|
|
28602
|
+
runtimeState: RecordingOnboardStatusSchema,
|
|
28603
|
+
/**
|
|
28604
|
+
* Runtime-state durability: **restored** — operator-set camera-side
|
|
28605
|
+
* recording config; mutation-driven, and the storage half is the last
|
|
28606
|
+
* thing the camera said about its own card.
|
|
28607
|
+
*
|
|
28608
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
28609
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
28610
|
+
*/
|
|
28611
|
+
durability: "restored",
|
|
28612
|
+
/** Clock fields: written, but excluded from the compare that decides
|
|
28613
|
+
* whether persisting is worth a SQLite commit. */
|
|
28614
|
+
volatileStateFields: ["lastFetchedAt"]
|
|
28615
|
+
};
|
|
28616
|
+
/**
|
|
27382
28617
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
27383
28618
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
27384
28619
|
* cap-keyed slice for the basic device flags every consumer expects to
|
|
@@ -29888,11 +31123,15 @@ var motionTriggerCapability = {
|
|
|
29888
31123
|
*
|
|
29889
31124
|
* Adding a fifth name to that list would have been the wrong fix twice over:
|
|
29890
31125
|
* that page is per-camera DETECTION tuning, and a grid's geometry belongs
|
|
29891
|
-
* beside PTZ and motion zones on the camera itself
|
|
29892
|
-
*
|
|
29893
|
-
*
|
|
29894
|
-
*
|
|
29895
|
-
*
|
|
31126
|
+
* beside PTZ and motion zones on the camera itself. The device page is
|
|
31127
|
+
* BINDING-driven (D12), so the way in is a capability bound to the device —
|
|
31128
|
+
* and this cap carries its section the way `recording` does, by RETURNING it
|
|
31129
|
+
* from `getDeviceSettingsContribution`.
|
|
31130
|
+
*
|
|
31131
|
+
* Seven other widgets are still declared the other way, through a
|
|
31132
|
+
* `deviceConfig.ui` block the framework derives a section from. That route
|
|
31133
|
+
* gives the addon no say in where its own panel lands and no way to decline
|
|
31134
|
+
* for a device the panel does not suit, which is why this one does not use it.
|
|
29896
31135
|
*
|
|
29897
31136
|
* ## Why one addon may implement it
|
|
29898
31137
|
*
|
|
@@ -29948,16 +31187,37 @@ var GridProfileOfferSchema = object({
|
|
|
29948
31187
|
]),
|
|
29949
31188
|
offered: boolean(),
|
|
29950
31189
|
/** Sources that cannot serve it. Empty when it is offered, or when there are no cells. */
|
|
29951
|
-
missingSources: array(number().int().positive())
|
|
31190
|
+
missingSources: array(number().int().positive()),
|
|
31191
|
+
/**
|
|
31192
|
+
* The canvas this profile composes onto, `WxH`, or empty when it is not
|
|
31193
|
+
* offered. DERIVED from the cells and the sources' own size at this profile —
|
|
31194
|
+
* it is reported because nothing else in the system would ever say what the
|
|
31195
|
+
* grid came out as, and because it is the number an operator would otherwise
|
|
31196
|
+
* expect to type.
|
|
31197
|
+
*/
|
|
31198
|
+
canvas: string(),
|
|
31199
|
+
/**
|
|
31200
|
+
* Whether this profile is PUBLISHED, of the ones the grid could serve.
|
|
31201
|
+
*
|
|
31202
|
+
* A grid's `high` is composed of its sources' `high`, so on a 4K fleet it is
|
|
31203
|
+
* a 4K canvas built from 4K decodes — something to opt into, not something a
|
|
31204
|
+
* viewer's adaptive should be handed by climbing to the top rung it can see.
|
|
31205
|
+
* Default is `mid` + `low`.
|
|
31206
|
+
*/
|
|
31207
|
+
published: boolean()
|
|
29952
31208
|
});
|
|
29953
31209
|
var GridLayoutViewSchema = object({
|
|
29954
31210
|
/** The persisted grid row this camera was declared from. */
|
|
29955
31211
|
instanceId: string(),
|
|
29956
31212
|
deviceId: number().int().nonnegative(),
|
|
29957
31213
|
name: string(),
|
|
29958
|
-
/**
|
|
29959
|
-
|
|
29960
|
-
|
|
31214
|
+
/**
|
|
31215
|
+
* NO canvas size. A grid's resolution is not authored: each profile derives
|
|
31216
|
+
* its own from the cells and its sources' dimensions. The two numbers that
|
|
31217
|
+
* used to be here were a text field that silently decided both how much the
|
|
31218
|
+
* composite cost and how sharp it was — see `profiles[].canvas` for what it
|
|
31219
|
+
* came out as.
|
|
31220
|
+
*/
|
|
29961
31221
|
fps: number().int(),
|
|
29962
31222
|
cells: array(GridLayoutCellSchema),
|
|
29963
31223
|
/** What the catalog will publish, and what it refuses to. Read-only. */
|
|
@@ -29966,9 +31226,13 @@ var GridLayoutViewSchema = object({
|
|
|
29966
31226
|
var GridLayoutPatchSchema = object({
|
|
29967
31227
|
deviceId: number().int().nonnegative(),
|
|
29968
31228
|
name: string().min(1).max(160).optional(),
|
|
29969
|
-
width: number().int().min(160).max(7680).optional(),
|
|
29970
|
-
height: number().int().min(120).max(4320).optional(),
|
|
29971
31229
|
fps: number().int().min(1).max(60).optional(),
|
|
31230
|
+
/** Which profiles to publish. See `GridProfileOffer.published`. */
|
|
31231
|
+
publishedProfiles: array(_enum([
|
|
31232
|
+
"high",
|
|
31233
|
+
"mid",
|
|
31234
|
+
"low"
|
|
31235
|
+
])).max(3).optional(),
|
|
29972
31236
|
/**
|
|
29973
31237
|
* The whole cell list at once. A per-cell patch would need an ordering the
|
|
29974
31238
|
* editor does not have, and a half-applied layout is a picture nobody asked
|
|
@@ -32251,6 +33515,25 @@ var ptzAutotrackCapability = {
|
|
|
32251
33515
|
scope: "device",
|
|
32252
33516
|
deviceNative: true,
|
|
32253
33517
|
mode: "singleton",
|
|
33518
|
+
/**
|
|
33519
|
+
* Two kinds of provider behind one name, the same shape `snapshot` and
|
|
33520
|
+
* `doorbell` use:
|
|
33521
|
+
*
|
|
33522
|
+
* - **Native**: the camera's own firmware autotrack, registered per device by
|
|
33523
|
+
* the driver when the probe says it exists (Reolink: yes; Hikvision:
|
|
33524
|
+
* model-dependent).
|
|
33525
|
+
* - **Wrapper**: the emulated implementation (`@camstack/system`'s `autotrack` builtin),
|
|
33526
|
+
* which follows a subject with `ptz.move` pulses driven by the detection
|
|
33527
|
+
* pipeline. `defaultActive: false` — the operator binds it per camera in
|
|
33528
|
+
* the bindings UI, exactly as `virtual-doorbell` is bound.
|
|
33529
|
+
*
|
|
33530
|
+
* The binding chooses WHICH implementation answers; `setEnabled` stays the
|
|
33531
|
+
* one switch for the function either way. A camera never has both driving its
|
|
33532
|
+
* head, which is the whole reason this is not a second cap with a second
|
|
33533
|
+
* toggle (D62, D520).
|
|
33534
|
+
*/
|
|
33535
|
+
kind: "wrapper",
|
|
33536
|
+
defaultActive: false,
|
|
32254
33537
|
deviceTypes: [DeviceType.Camera],
|
|
32255
33538
|
deviceConfig: { ui: {
|
|
32256
33539
|
kind: "widget",
|
|
@@ -32733,7 +34016,7 @@ method(object({
|
|
|
32733
34016
|
auth: "admin"
|
|
32734
34017
|
}), method(object({ deviceId: number() }), RecordingConfigSchema, {
|
|
32735
34018
|
kind: "query",
|
|
32736
|
-
auth: "
|
|
34019
|
+
auth: "protected"
|
|
32737
34020
|
}), method(object({
|
|
32738
34021
|
deviceId: number(),
|
|
32739
34022
|
profile: string(),
|
|
@@ -32963,13 +34246,101 @@ var ExportStateSchema = _enum([
|
|
|
32963
34246
|
"expired",
|
|
32964
34247
|
"deleted"
|
|
32965
34248
|
]);
|
|
32966
|
-
/**
|
|
34249
|
+
/**
|
|
34250
|
+
* WHAT an export is — the authority, as opposed to the four top-level fields
|
|
34251
|
+
* the Library sorts and labels on (D558 § 2.2).
|
|
34252
|
+
*
|
|
34253
|
+
* Read it through {@link exportSubjectOf}, never off the record directly: the
|
|
34254
|
+
* field is optional for the history rows written before it existed, and that
|
|
34255
|
+
* absence has exactly one interpreter.
|
|
34256
|
+
*/
|
|
34257
|
+
var ExportSubjectSchema = discriminatedUnion("kind", [object({
|
|
34258
|
+
kind: literal("footage"),
|
|
34259
|
+
deviceId: number(),
|
|
34260
|
+
profiles: array(string()).min(1),
|
|
34261
|
+
fromMs: number(),
|
|
34262
|
+
toMs: number()
|
|
34263
|
+
}), object({
|
|
34264
|
+
kind: literal("clip"),
|
|
34265
|
+
deviceId: number(),
|
|
34266
|
+
provider: string().min(1),
|
|
34267
|
+
source: string().min(1),
|
|
34268
|
+
sourceLabel: string().min(1),
|
|
34269
|
+
clipId: string().min(1),
|
|
34270
|
+
/**
|
|
34271
|
+
* WHERE IN THE CATALOG to confirm this clip — the day window the surface was
|
|
34272
|
+
* already listing when the operator picked the segment.
|
|
34273
|
+
*
|
|
34274
|
+
* It is **not** a time range control and it never becomes one: the record's
|
|
34275
|
+
* `fromMs`/`toMs` come from the catalog ROW and from nothing a caller
|
|
34276
|
+
* supplied (D558 § 5.4.3), and a window that does not contain the clip is a
|
|
34277
|
+
* `catalog-miss`, not a silently wider search. It exists because
|
|
34278
|
+
* `videoclips.listClips` takes `since`/`until` and has no by-handle twin:
|
|
34279
|
+
* the catalog check D558 asks for is literally that call, and a call needs a
|
|
34280
|
+
* window. The surface has one — `ClipsBrowser`'s `dayWindow`, one local
|
|
34281
|
+
* wall-clock day, which is also the only width measured to be cheap (one
|
|
34282
|
+
* day on 592 lists 171 clips in 712 ms, a busy day 546 in 2.0 s; twenty days
|
|
34283
|
+
* of one child's events took 3.6–5.4 s).
|
|
34284
|
+
*/
|
|
34285
|
+
catalogWindow: object({
|
|
34286
|
+
sinceMs: number(),
|
|
34287
|
+
untilMs: number()
|
|
34288
|
+
}),
|
|
34289
|
+
profile: _enum([
|
|
34290
|
+
"high",
|
|
34291
|
+
"mid",
|
|
34292
|
+
"low"
|
|
34293
|
+
]),
|
|
34294
|
+
/**
|
|
34295
|
+
* The operator's authorisation to wake a sleeping camera for this export.
|
|
34296
|
+
* ONE definition, in the cap that owns the clip read ({@link ClipWakeSchema}),
|
|
34297
|
+
* because the gate that honours it is the clip provider's sleep gate — a
|
|
34298
|
+
* second enum here would be a second contract. Absent by default; never
|
|
34299
|
+
* settable by a scheduler or a retry.
|
|
34300
|
+
*
|
|
34301
|
+
* **One authorised yes is ONE wake.** A failed clip export is retried only by
|
|
34302
|
+
* an operator act that asks again; a queued job that outlived its wake fails
|
|
34303
|
+
* with a reason rather than waking on its turn; and this subject carries
|
|
34304
|
+
* exactly one profile precisely so one tap is never two fetches (D558 § 5.2).
|
|
34305
|
+
*/
|
|
34306
|
+
wake: ClipWakeSchema.optional()
|
|
34307
|
+
})]);
|
|
34308
|
+
/**
|
|
34309
|
+
* One export job / history row.
|
|
34310
|
+
*
|
|
34311
|
+
* **`subject` is the AUTHORITY on what was exported. `deviceId`, `profile`,
|
|
34312
|
+
* `fromMs` and `toMs` are its PROJECTION** — kept top-level because the whole
|
|
34313
|
+
* Library sorts and labels on them (`library-items.ts` orders an export by
|
|
34314
|
+
* `fromMs`; `export-format.ts` draws `rangeLabel` from the pair), and a row
|
|
34315
|
+
* that did not fill them would sort under the epoch and render a blank range.
|
|
34316
|
+
* Write to the subject and read from the projection and the two will disagree;
|
|
34317
|
+
* the projection is derived at creation and never edited afterwards.
|
|
34318
|
+
*
|
|
34319
|
+
* And they mean DIFFERENT FACTS for the two kinds, which is the part a reader
|
|
34320
|
+
* who knows only the recording export will get wrong:
|
|
34321
|
+
*
|
|
34322
|
+
* | field | `kind:'footage'` | `kind:'clip'` |
|
|
34323
|
+
* | --- | --- | --- |
|
|
34324
|
+
* | `fromMs`/`toMs` | the stretch the operator ASKED for | the camera's own boundaries, always `clip.timeRange`, never anything a caller supplied |
|
|
34325
|
+
* | `profile` | the stream rendered | the twin actually SERVED (`subject.profile` is the one asked for) |
|
|
34326
|
+
*
|
|
34327
|
+
* Same type, different fact — the shape this repo keeps getting wrong (D385's
|
|
34328
|
+
* two authorities, D224's second copy).
|
|
34329
|
+
*/
|
|
32967
34330
|
var ExportRecordSchema = object({
|
|
32968
34331
|
id: string(),
|
|
32969
34332
|
deviceId: number(),
|
|
32970
34333
|
profile: string(),
|
|
32971
34334
|
fromMs: number(),
|
|
32972
34335
|
toMs: number(),
|
|
34336
|
+
/**
|
|
34337
|
+
* What this export IS. Optional ONLY for the rows written before D558: the
|
|
34338
|
+
* store parses every row through this schema on every read, so a required
|
|
34339
|
+
* field would make the export AUDIT — which is the whole reason rows survive
|
|
34340
|
+
* file deletion — unreadable in one release. Absence means `footage`, and
|
|
34341
|
+
* {@link exportSubjectOf} is the one place that says so.
|
|
34342
|
+
*/
|
|
34343
|
+
subject: ExportSubjectSchema.optional(),
|
|
32973
34344
|
options: ExportOptionsSchema,
|
|
32974
34345
|
state: ExportStateSchema,
|
|
32975
34346
|
/** 0–100 while rendering; null otherwise. */
|
|
@@ -32986,6 +34357,18 @@ var ExportRecordSchema = object({
|
|
|
32986
34357
|
/** Failure reason when state is 'failed'; null otherwise. */
|
|
32987
34358
|
error: string().nullable()
|
|
32988
34359
|
});
|
|
34360
|
+
_enum([
|
|
34361
|
+
"catalog-miss",
|
|
34362
|
+
"catalog-unreachable",
|
|
34363
|
+
"no-file-for-window",
|
|
34364
|
+
"clip-in-progress",
|
|
34365
|
+
"unsupported-option",
|
|
34366
|
+
"sleeping",
|
|
34367
|
+
"camera-refused",
|
|
34368
|
+
"fetch-failed",
|
|
34369
|
+
"too-large-to-transfer",
|
|
34370
|
+
"wake-expired"
|
|
34371
|
+
]);
|
|
32989
34372
|
/** Candidate download URLs (LAN first, then operator extra hosts). */
|
|
32990
34373
|
var ExportDownloadSchema = object({
|
|
32991
34374
|
url: string(),
|
|
@@ -33004,16 +34387,50 @@ var ExportBytesSchema = object({
|
|
|
33004
34387
|
name: string(),
|
|
33005
34388
|
bytes: number().int().nonnegative()
|
|
33006
34389
|
});
|
|
34390
|
+
/** Canonical `profiles[]`, falling back to the legacy singular `profile`. */
|
|
34391
|
+
function resolveExportProfiles(input) {
|
|
34392
|
+
if (input.profiles !== void 0 && input.profiles.length > 0) return [...input.profiles];
|
|
34393
|
+
if (typeof input.profile === "string" && input.profile.length > 0) return [input.profile];
|
|
34394
|
+
return [];
|
|
34395
|
+
}
|
|
33007
34396
|
method(object({
|
|
33008
34397
|
deviceId: number(),
|
|
33009
34398
|
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
33010
34399
|
profile: string().optional(),
|
|
33011
34400
|
profiles: array(string()).min(1).optional(),
|
|
33012
|
-
|
|
33013
|
-
|
|
34401
|
+
/** Footage only — a clip's boundaries are the camera's. */
|
|
34402
|
+
fromMs: number().optional(),
|
|
34403
|
+
toMs: number().optional(),
|
|
34404
|
+
/** What to export. Absent means the legacy flat footage request. */
|
|
34405
|
+
subject: ExportSubjectSchema.optional(),
|
|
33014
34406
|
options: ExportOptionsSchema
|
|
33015
34407
|
}).superRefine((v, ctx) => {
|
|
33016
|
-
if (
|
|
34408
|
+
if (v.subject?.kind === "clip") {
|
|
34409
|
+
if (v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
34410
|
+
code: ZodIssueCode.custom,
|
|
34411
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId}) — the top-level field is what per-device scope enforcement reads`,
|
|
34412
|
+
path: ["subject", "deviceId"]
|
|
34413
|
+
});
|
|
34414
|
+
if (v.fromMs !== void 0 || v.toMs !== void 0) ctx.addIssue({
|
|
34415
|
+
code: ZodIssueCode.custom,
|
|
34416
|
+
message: "a clip export asks for no time range: the camera chose the boundaries and they are read from the catalog row",
|
|
34417
|
+
path: ["fromMs"]
|
|
34418
|
+
});
|
|
34419
|
+
return;
|
|
34420
|
+
}
|
|
34421
|
+
if (v.subject?.kind === "footage" && v.subject.deviceId !== v.deviceId) ctx.addIssue({
|
|
34422
|
+
code: ZodIssueCode.custom,
|
|
34423
|
+
message: `subject.deviceId (${v.subject.deviceId}) must equal deviceId (${v.deviceId})`,
|
|
34424
|
+
path: ["subject", "deviceId"]
|
|
34425
|
+
});
|
|
34426
|
+
const fromMs = v.subject?.kind === "footage" ? v.subject.fromMs : v.fromMs;
|
|
34427
|
+
const toMs = v.subject?.kind === "footage" ? v.subject.toMs : v.toMs;
|
|
34428
|
+
if (typeof fromMs !== "number" || typeof toMs !== "number") ctx.addIssue({
|
|
34429
|
+
code: ZodIssueCode.custom,
|
|
34430
|
+
message: "a footage export needs fromMs and toMs",
|
|
34431
|
+
path: ["fromMs"]
|
|
34432
|
+
});
|
|
34433
|
+
if ((v.subject?.kind === "footage" ? [...v.subject.profiles] : resolveExportProfiles(v)).length < 1) ctx.addIssue({
|
|
33017
34434
|
code: ZodIssueCode.custom,
|
|
33018
34435
|
message: "pass profiles[] (min 1) or legacy profile",
|
|
33019
34436
|
path: ["profiles"]
|
|
@@ -35825,6 +37242,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
|
|
|
35825
37242
|
pressureSensor: pressureSensorCapability,
|
|
35826
37243
|
privacyMask: privacyMaskCapability,
|
|
35827
37244
|
ptzAutotrack: ptzAutotrackCapability,
|
|
37245
|
+
recordingOnboard: recordingOnboardCapability,
|
|
35828
37246
|
recordingSignal: recordingSignalCapability,
|
|
35829
37247
|
sceneMonitor: sceneMonitorCapability,
|
|
35830
37248
|
scriptRunner: scriptRunnerCapability,
|
|
@@ -36986,7 +38404,1336 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
36986
38404
|
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;
|
|
36987
38405
|
new Set(Object.values(DeviceType));
|
|
36988
38406
|
DeviceFeature.BatteryOperated;
|
|
38407
|
+
/**
|
|
38408
|
+
* The vocabulary, in the order a picker offers it.
|
|
38409
|
+
*
|
|
38410
|
+
* Adding a key here is not enough to make it pickable: both glyph tables must
|
|
38411
|
+
* gain it too, and their tests fail until they do. That is the intended
|
|
38412
|
+
* friction — it is the only thing that keeps the two surfaces honest.
|
|
38413
|
+
*/
|
|
38414
|
+
var DISPLAY_ICON_VOCABULARY = [
|
|
38415
|
+
{
|
|
38416
|
+
key: "camera",
|
|
38417
|
+
group: "device",
|
|
38418
|
+
synonyms: [
|
|
38419
|
+
"telecamera",
|
|
38420
|
+
"videocamera",
|
|
38421
|
+
"camera",
|
|
38422
|
+
"cam"
|
|
38423
|
+
]
|
|
38424
|
+
},
|
|
38425
|
+
{
|
|
38426
|
+
key: "network",
|
|
38427
|
+
group: "device",
|
|
38428
|
+
synonyms: [
|
|
38429
|
+
"rete",
|
|
38430
|
+
"network",
|
|
38431
|
+
"lan"
|
|
38432
|
+
]
|
|
38433
|
+
},
|
|
38434
|
+
{
|
|
38435
|
+
key: "toggle-left",
|
|
38436
|
+
group: "device",
|
|
38437
|
+
synonyms: [
|
|
38438
|
+
"interruttore",
|
|
38439
|
+
"switch",
|
|
38440
|
+
"toggle"
|
|
38441
|
+
]
|
|
38442
|
+
},
|
|
38443
|
+
{
|
|
38444
|
+
key: "toggle-right",
|
|
38445
|
+
group: "device",
|
|
38446
|
+
synonyms: [
|
|
38447
|
+
"interruttore",
|
|
38448
|
+
"switch",
|
|
38449
|
+
"toggle",
|
|
38450
|
+
"acceso"
|
|
38451
|
+
]
|
|
38452
|
+
},
|
|
38453
|
+
{
|
|
38454
|
+
key: "circle-dot",
|
|
38455
|
+
group: "device",
|
|
38456
|
+
synonyms: [
|
|
38457
|
+
"pulsante",
|
|
38458
|
+
"bottone",
|
|
38459
|
+
"button",
|
|
38460
|
+
"dot"
|
|
38461
|
+
]
|
|
38462
|
+
},
|
|
38463
|
+
{
|
|
38464
|
+
key: "refresh-cw",
|
|
38465
|
+
group: "device",
|
|
38466
|
+
synonyms: [
|
|
38467
|
+
"aggiorna",
|
|
38468
|
+
"ricarica",
|
|
38469
|
+
"refresh",
|
|
38470
|
+
"reload",
|
|
38471
|
+
"ciclo"
|
|
38472
|
+
]
|
|
38473
|
+
},
|
|
38474
|
+
{
|
|
38475
|
+
key: "box",
|
|
38476
|
+
group: "device",
|
|
38477
|
+
synonyms: [
|
|
38478
|
+
"scatola",
|
|
38479
|
+
"box",
|
|
38480
|
+
"contenitore"
|
|
38481
|
+
]
|
|
38482
|
+
},
|
|
38483
|
+
{
|
|
38484
|
+
key: "scroll-text",
|
|
38485
|
+
group: "device",
|
|
38486
|
+
synonyms: [
|
|
38487
|
+
"registro",
|
|
38488
|
+
"log",
|
|
38489
|
+
"testo"
|
|
38490
|
+
]
|
|
38491
|
+
},
|
|
38492
|
+
{
|
|
38493
|
+
key: "workflow",
|
|
38494
|
+
group: "device",
|
|
38495
|
+
synonyms: [
|
|
38496
|
+
"automazione",
|
|
38497
|
+
"flusso",
|
|
38498
|
+
"workflow",
|
|
38499
|
+
"scenario"
|
|
38500
|
+
]
|
|
38501
|
+
},
|
|
38502
|
+
{
|
|
38503
|
+
key: "lock",
|
|
38504
|
+
group: "device",
|
|
38505
|
+
synonyms: [
|
|
38506
|
+
"serratura",
|
|
38507
|
+
"chiave",
|
|
38508
|
+
"lucchetto",
|
|
38509
|
+
"chiuso",
|
|
38510
|
+
"locked"
|
|
38511
|
+
]
|
|
38512
|
+
},
|
|
38513
|
+
{
|
|
38514
|
+
key: "lock-open",
|
|
38515
|
+
group: "device",
|
|
38516
|
+
synonyms: [
|
|
38517
|
+
"serratura",
|
|
38518
|
+
"aperta",
|
|
38519
|
+
"sbloccato",
|
|
38520
|
+
"unlocked"
|
|
38521
|
+
]
|
|
38522
|
+
},
|
|
38523
|
+
{
|
|
38524
|
+
key: "blinds",
|
|
38525
|
+
group: "device",
|
|
38526
|
+
synonyms: [
|
|
38527
|
+
"tapparella",
|
|
38528
|
+
"persiana",
|
|
38529
|
+
"serranda",
|
|
38530
|
+
"avvolgibile",
|
|
38531
|
+
"shutter",
|
|
38532
|
+
"blind",
|
|
38533
|
+
"veneziana"
|
|
38534
|
+
]
|
|
38535
|
+
},
|
|
38536
|
+
{
|
|
38537
|
+
key: "air-vent",
|
|
38538
|
+
group: "device",
|
|
38539
|
+
synonyms: [
|
|
38540
|
+
"bocchetta",
|
|
38541
|
+
"aria",
|
|
38542
|
+
"ventilazione",
|
|
38543
|
+
"condizionatore",
|
|
38544
|
+
"vent"
|
|
38545
|
+
]
|
|
38546
|
+
},
|
|
38547
|
+
{
|
|
38548
|
+
key: "monitor-play",
|
|
38549
|
+
group: "device",
|
|
38550
|
+
synonyms: [
|
|
38551
|
+
"schermo",
|
|
38552
|
+
"riproduzione",
|
|
38553
|
+
"player",
|
|
38554
|
+
"display"
|
|
38555
|
+
]
|
|
38556
|
+
},
|
|
38557
|
+
{
|
|
38558
|
+
key: "sliders-horizontal",
|
|
38559
|
+
group: "device",
|
|
38560
|
+
synonyms: [
|
|
38561
|
+
"regolazione",
|
|
38562
|
+
"cursori",
|
|
38563
|
+
"impostazioni",
|
|
38564
|
+
"slider"
|
|
38565
|
+
]
|
|
38566
|
+
},
|
|
38567
|
+
{
|
|
38568
|
+
key: "map-pin",
|
|
38569
|
+
group: "device",
|
|
38570
|
+
synonyms: [
|
|
38571
|
+
"posizione",
|
|
38572
|
+
"luogo",
|
|
38573
|
+
"mappa",
|
|
38574
|
+
"pin"
|
|
38575
|
+
]
|
|
38576
|
+
},
|
|
38577
|
+
{
|
|
38578
|
+
key: "bot",
|
|
38579
|
+
group: "device",
|
|
38580
|
+
synonyms: [
|
|
38581
|
+
"robot",
|
|
38582
|
+
"aspirapolvere",
|
|
38583
|
+
"bot"
|
|
38584
|
+
]
|
|
38585
|
+
},
|
|
38586
|
+
{
|
|
38587
|
+
key: "tractor",
|
|
38588
|
+
group: "device",
|
|
38589
|
+
synonyms: [
|
|
38590
|
+
"trattore",
|
|
38591
|
+
"tosaerba",
|
|
38592
|
+
"giardino"
|
|
38593
|
+
]
|
|
38594
|
+
},
|
|
38595
|
+
{
|
|
38596
|
+
key: "image",
|
|
38597
|
+
group: "device",
|
|
38598
|
+
synonyms: [
|
|
38599
|
+
"immagine",
|
|
38600
|
+
"foto",
|
|
38601
|
+
"snapshot"
|
|
38602
|
+
]
|
|
38603
|
+
},
|
|
38604
|
+
{
|
|
38605
|
+
key: "bone",
|
|
38606
|
+
group: "device",
|
|
38607
|
+
synonyms: ["osso", "cane"]
|
|
38608
|
+
},
|
|
38609
|
+
{
|
|
38610
|
+
key: "door-closed",
|
|
38611
|
+
group: "device",
|
|
38612
|
+
synonyms: [
|
|
38613
|
+
"porta",
|
|
38614
|
+
"chiusa",
|
|
38615
|
+
"contatto"
|
|
38616
|
+
]
|
|
38617
|
+
},
|
|
38618
|
+
{
|
|
38619
|
+
key: "door-open",
|
|
38620
|
+
group: "device",
|
|
38621
|
+
synonyms: [
|
|
38622
|
+
"porta",
|
|
38623
|
+
"aperta",
|
|
38624
|
+
"contatto",
|
|
38625
|
+
"ingresso",
|
|
38626
|
+
"uscita"
|
|
38627
|
+
]
|
|
38628
|
+
},
|
|
38629
|
+
{
|
|
38630
|
+
key: "crosshair",
|
|
38631
|
+
group: "device",
|
|
38632
|
+
synonyms: [
|
|
38633
|
+
"mirino",
|
|
38634
|
+
"puntamento",
|
|
38635
|
+
"centro"
|
|
38636
|
+
]
|
|
38637
|
+
},
|
|
38638
|
+
{
|
|
38639
|
+
key: "eye",
|
|
38640
|
+
group: "device",
|
|
38641
|
+
synonyms: [
|
|
38642
|
+
"occhio",
|
|
38643
|
+
"visibile",
|
|
38644
|
+
"visto"
|
|
38645
|
+
]
|
|
38646
|
+
},
|
|
38647
|
+
{
|
|
38648
|
+
key: "eye-off",
|
|
38649
|
+
group: "device",
|
|
38650
|
+
synonyms: [
|
|
38651
|
+
"privacy",
|
|
38652
|
+
"nascosto",
|
|
38653
|
+
"oscurato",
|
|
38654
|
+
"mascherato",
|
|
38655
|
+
"invisibile"
|
|
38656
|
+
]
|
|
38657
|
+
},
|
|
38658
|
+
{
|
|
38659
|
+
key: "smartphone",
|
|
38660
|
+
group: "device",
|
|
38661
|
+
synonyms: [
|
|
38662
|
+
"telefono",
|
|
38663
|
+
"cellulare",
|
|
38664
|
+
"smartphone"
|
|
38665
|
+
]
|
|
38666
|
+
},
|
|
38667
|
+
{
|
|
38668
|
+
key: "phone",
|
|
38669
|
+
group: "device",
|
|
38670
|
+
synonyms: ["telefono", "chiamata"]
|
|
38671
|
+
},
|
|
38672
|
+
{
|
|
38673
|
+
key: "tablet",
|
|
38674
|
+
group: "device",
|
|
38675
|
+
synonyms: ["tablet", "ipad"]
|
|
38676
|
+
},
|
|
38677
|
+
{
|
|
38678
|
+
key: "laptop",
|
|
38679
|
+
group: "device",
|
|
38680
|
+
synonyms: [
|
|
38681
|
+
"portatile",
|
|
38682
|
+
"laptop",
|
|
38683
|
+
"notebook"
|
|
38684
|
+
]
|
|
38685
|
+
},
|
|
38686
|
+
{
|
|
38687
|
+
key: "monitor",
|
|
38688
|
+
group: "device",
|
|
38689
|
+
synonyms: ["monitor", "schermo"]
|
|
38690
|
+
},
|
|
38691
|
+
{
|
|
38692
|
+
key: "tv",
|
|
38693
|
+
group: "device",
|
|
38694
|
+
synonyms: ["televisore", "tv"]
|
|
38695
|
+
},
|
|
38696
|
+
{
|
|
38697
|
+
key: "speaker",
|
|
38698
|
+
group: "device",
|
|
38699
|
+
synonyms: [
|
|
38700
|
+
"altoparlante",
|
|
38701
|
+
"cassa",
|
|
38702
|
+
"speaker"
|
|
38703
|
+
]
|
|
38704
|
+
},
|
|
38705
|
+
{
|
|
38706
|
+
key: "printer",
|
|
38707
|
+
group: "device",
|
|
38708
|
+
synonyms: ["stampante"]
|
|
38709
|
+
},
|
|
38710
|
+
{
|
|
38711
|
+
key: "projector",
|
|
38712
|
+
group: "device",
|
|
38713
|
+
synonyms: ["proiettore"]
|
|
38714
|
+
},
|
|
38715
|
+
{
|
|
38716
|
+
key: "gamepad-2",
|
|
38717
|
+
group: "device",
|
|
38718
|
+
synonyms: [
|
|
38719
|
+
"console",
|
|
38720
|
+
"gioco",
|
|
38721
|
+
"joystick"
|
|
38722
|
+
]
|
|
38723
|
+
},
|
|
38724
|
+
{
|
|
38725
|
+
key: "radio",
|
|
38726
|
+
group: "device",
|
|
38727
|
+
synonyms: ["radio"]
|
|
38728
|
+
},
|
|
38729
|
+
{
|
|
38730
|
+
key: "router",
|
|
38731
|
+
group: "device",
|
|
38732
|
+
synonyms: ["router", "modem"]
|
|
38733
|
+
},
|
|
38734
|
+
{
|
|
38735
|
+
key: "bluetooth",
|
|
38736
|
+
group: "device",
|
|
38737
|
+
synonyms: ["bluetooth"]
|
|
38738
|
+
},
|
|
38739
|
+
{
|
|
38740
|
+
key: "cpu",
|
|
38741
|
+
group: "device",
|
|
38742
|
+
synonyms: [
|
|
38743
|
+
"processore",
|
|
38744
|
+
"cpu",
|
|
38745
|
+
"chip",
|
|
38746
|
+
"dispositivo"
|
|
38747
|
+
]
|
|
38748
|
+
},
|
|
38749
|
+
{
|
|
38750
|
+
key: "hard-drive",
|
|
38751
|
+
group: "device",
|
|
38752
|
+
synonyms: [
|
|
38753
|
+
"disco",
|
|
38754
|
+
"hdd",
|
|
38755
|
+
"archiviazione",
|
|
38756
|
+
"storage"
|
|
38757
|
+
]
|
|
38758
|
+
},
|
|
38759
|
+
{
|
|
38760
|
+
key: "server",
|
|
38761
|
+
group: "device",
|
|
38762
|
+
synonyms: ["server", "nas"]
|
|
38763
|
+
},
|
|
38764
|
+
{
|
|
38765
|
+
key: "mic",
|
|
38766
|
+
group: "device",
|
|
38767
|
+
synonyms: ["microfono", "audio"]
|
|
38768
|
+
},
|
|
38769
|
+
{
|
|
38770
|
+
key: "mic-off",
|
|
38771
|
+
group: "device",
|
|
38772
|
+
synonyms: [
|
|
38773
|
+
"microfono",
|
|
38774
|
+
"spento",
|
|
38775
|
+
"muto"
|
|
38776
|
+
]
|
|
38777
|
+
},
|
|
38778
|
+
{
|
|
38779
|
+
key: "video",
|
|
38780
|
+
group: "device",
|
|
38781
|
+
synonyms: ["video"]
|
|
38782
|
+
},
|
|
38783
|
+
{
|
|
38784
|
+
key: "video-off",
|
|
38785
|
+
group: "device",
|
|
38786
|
+
synonyms: ["video", "spento"]
|
|
38787
|
+
},
|
|
38788
|
+
{
|
|
38789
|
+
key: "music",
|
|
38790
|
+
group: "device",
|
|
38791
|
+
synonyms: ["musica"]
|
|
38792
|
+
},
|
|
38793
|
+
{
|
|
38794
|
+
key: "plug",
|
|
38795
|
+
group: "device",
|
|
38796
|
+
synonyms: [
|
|
38797
|
+
"presa",
|
|
38798
|
+
"spina",
|
|
38799
|
+
"plug"
|
|
38800
|
+
]
|
|
38801
|
+
},
|
|
38802
|
+
{
|
|
38803
|
+
key: "plug-zap",
|
|
38804
|
+
group: "device",
|
|
38805
|
+
synonyms: ["presa", "alimentata"]
|
|
38806
|
+
},
|
|
38807
|
+
{
|
|
38808
|
+
key: "power",
|
|
38809
|
+
group: "device",
|
|
38810
|
+
synonyms: [
|
|
38811
|
+
"accensione",
|
|
38812
|
+
"alimentazione",
|
|
38813
|
+
"power"
|
|
38814
|
+
]
|
|
38815
|
+
},
|
|
38816
|
+
{
|
|
38817
|
+
key: "power-off",
|
|
38818
|
+
group: "device",
|
|
38819
|
+
synonyms: ["spegnimento", "spento"]
|
|
38820
|
+
},
|
|
38821
|
+
{
|
|
38822
|
+
key: "cable",
|
|
38823
|
+
group: "device",
|
|
38824
|
+
synonyms: ["cavo"]
|
|
38825
|
+
},
|
|
38826
|
+
{
|
|
38827
|
+
key: "battery",
|
|
38828
|
+
group: "device",
|
|
38829
|
+
synonyms: ["batteria"]
|
|
38830
|
+
},
|
|
38831
|
+
{
|
|
38832
|
+
key: "battery-charging",
|
|
38833
|
+
group: "device",
|
|
38834
|
+
synonyms: [
|
|
38835
|
+
"batteria",
|
|
38836
|
+
"carica",
|
|
38837
|
+
"ricarica"
|
|
38838
|
+
]
|
|
38839
|
+
},
|
|
38840
|
+
{
|
|
38841
|
+
key: "battery-full",
|
|
38842
|
+
group: "device",
|
|
38843
|
+
synonyms: [
|
|
38844
|
+
"batteria",
|
|
38845
|
+
"carica",
|
|
38846
|
+
"piena"
|
|
38847
|
+
]
|
|
38848
|
+
},
|
|
38849
|
+
{
|
|
38850
|
+
key: "battery-low",
|
|
38851
|
+
group: "device",
|
|
38852
|
+
synonyms: ["batteria", "scarica"]
|
|
38853
|
+
},
|
|
38854
|
+
{
|
|
38855
|
+
key: "key",
|
|
38856
|
+
group: "device",
|
|
38857
|
+
synonyms: ["chiave"]
|
|
38858
|
+
},
|
|
38859
|
+
{
|
|
38860
|
+
key: "key-round",
|
|
38861
|
+
group: "device",
|
|
38862
|
+
synonyms: ["chiave", "tonda"]
|
|
38863
|
+
},
|
|
38864
|
+
{
|
|
38865
|
+
key: "mailbox",
|
|
38866
|
+
group: "device",
|
|
38867
|
+
synonyms: [
|
|
38868
|
+
"cassetta",
|
|
38869
|
+
"postale",
|
|
38870
|
+
"posta"
|
|
38871
|
+
]
|
|
38872
|
+
},
|
|
38873
|
+
{
|
|
38874
|
+
key: "package",
|
|
38875
|
+
group: "device",
|
|
38876
|
+
synonyms: ["pacco", "consegna"]
|
|
38877
|
+
},
|
|
38878
|
+
{
|
|
38879
|
+
key: "shield",
|
|
38880
|
+
group: "device",
|
|
38881
|
+
synonyms: [
|
|
38882
|
+
"scudo",
|
|
38883
|
+
"protezione",
|
|
38884
|
+
"sicurezza",
|
|
38885
|
+
"allarme"
|
|
38886
|
+
]
|
|
38887
|
+
},
|
|
38888
|
+
{
|
|
38889
|
+
key: "shield-check",
|
|
38890
|
+
group: "device",
|
|
38891
|
+
synonyms: [
|
|
38892
|
+
"protetto",
|
|
38893
|
+
"sicuro",
|
|
38894
|
+
"armato"
|
|
38895
|
+
]
|
|
38896
|
+
},
|
|
38897
|
+
{
|
|
38898
|
+
key: "shield-off",
|
|
38899
|
+
group: "device",
|
|
38900
|
+
synonyms: [
|
|
38901
|
+
"protezione",
|
|
38902
|
+
"disattivata",
|
|
38903
|
+
"disarmato"
|
|
38904
|
+
]
|
|
38905
|
+
},
|
|
38906
|
+
{
|
|
38907
|
+
key: "briefcase",
|
|
38908
|
+
group: "device",
|
|
38909
|
+
synonyms: [
|
|
38910
|
+
"ufficio",
|
|
38911
|
+
"lavoro",
|
|
38912
|
+
"valigetta",
|
|
38913
|
+
"studio"
|
|
38914
|
+
]
|
|
38915
|
+
},
|
|
38916
|
+
{
|
|
38917
|
+
key: "lightbulb",
|
|
38918
|
+
group: "lighting",
|
|
38919
|
+
synonyms: [
|
|
38920
|
+
"luce",
|
|
38921
|
+
"lampadina",
|
|
38922
|
+
"lampada",
|
|
38923
|
+
"light"
|
|
38924
|
+
]
|
|
38925
|
+
},
|
|
38926
|
+
{
|
|
38927
|
+
key: "lamp",
|
|
38928
|
+
group: "lighting",
|
|
38929
|
+
synonyms: ["lampada", "abatjour"]
|
|
38930
|
+
},
|
|
38931
|
+
{
|
|
38932
|
+
key: "lamp-ceiling",
|
|
38933
|
+
group: "lighting",
|
|
38934
|
+
synonyms: [
|
|
38935
|
+
"plafoniera",
|
|
38936
|
+
"lampadario",
|
|
38937
|
+
"luce",
|
|
38938
|
+
"soffitto"
|
|
38939
|
+
]
|
|
38940
|
+
},
|
|
38941
|
+
{
|
|
38942
|
+
key: "lamp-desk",
|
|
38943
|
+
group: "lighting",
|
|
38944
|
+
synonyms: [
|
|
38945
|
+
"lampada",
|
|
38946
|
+
"scrivania",
|
|
38947
|
+
"abatjour"
|
|
38948
|
+
]
|
|
38949
|
+
},
|
|
38950
|
+
{
|
|
38951
|
+
key: "lamp-floor",
|
|
38952
|
+
group: "lighting",
|
|
38953
|
+
synonyms: [
|
|
38954
|
+
"piantana",
|
|
38955
|
+
"lampada",
|
|
38956
|
+
"terra"
|
|
38957
|
+
]
|
|
38958
|
+
},
|
|
38959
|
+
{
|
|
38960
|
+
key: "flashlight",
|
|
38961
|
+
group: "lighting",
|
|
38962
|
+
synonyms: [
|
|
38963
|
+
"faretto",
|
|
38964
|
+
"torcia",
|
|
38965
|
+
"spot",
|
|
38966
|
+
"faro"
|
|
38967
|
+
]
|
|
38968
|
+
},
|
|
38969
|
+
{
|
|
38970
|
+
key: "heater",
|
|
38971
|
+
group: "lighting",
|
|
38972
|
+
synonyms: [
|
|
38973
|
+
"riscaldamento",
|
|
38974
|
+
"termosifone",
|
|
38975
|
+
"stufa",
|
|
38976
|
+
"calore"
|
|
38977
|
+
]
|
|
38978
|
+
},
|
|
38979
|
+
{
|
|
38980
|
+
key: "snowflake",
|
|
38981
|
+
group: "lighting",
|
|
38982
|
+
synonyms: [
|
|
38983
|
+
"freddo",
|
|
38984
|
+
"raffrescamento",
|
|
38985
|
+
"aria",
|
|
38986
|
+
"condizionata",
|
|
38987
|
+
"neve"
|
|
38988
|
+
]
|
|
38989
|
+
},
|
|
38990
|
+
{
|
|
38991
|
+
key: "thermometer-sun",
|
|
38992
|
+
group: "lighting",
|
|
38993
|
+
synonyms: [
|
|
38994
|
+
"caldo",
|
|
38995
|
+
"temperatura",
|
|
38996
|
+
"alta"
|
|
38997
|
+
]
|
|
38998
|
+
},
|
|
38999
|
+
{
|
|
39000
|
+
key: "thermometer-snowflake",
|
|
39001
|
+
group: "lighting",
|
|
39002
|
+
synonyms: [
|
|
39003
|
+
"freddo",
|
|
39004
|
+
"temperatura",
|
|
39005
|
+
"bassa"
|
|
39006
|
+
]
|
|
39007
|
+
},
|
|
39008
|
+
{
|
|
39009
|
+
key: "refrigerator",
|
|
39010
|
+
group: "lighting",
|
|
39011
|
+
synonyms: ["frigorifero", "frigo"]
|
|
39012
|
+
},
|
|
39013
|
+
{
|
|
39014
|
+
key: "microwave",
|
|
39015
|
+
group: "lighting",
|
|
39016
|
+
synonyms: ["microonde", "forno"]
|
|
39017
|
+
},
|
|
39018
|
+
{
|
|
39019
|
+
key: "washing-machine",
|
|
39020
|
+
group: "lighting",
|
|
39021
|
+
synonyms: [
|
|
39022
|
+
"lavatrice",
|
|
39023
|
+
"lavastoviglie",
|
|
39024
|
+
"asciugatrice"
|
|
39025
|
+
]
|
|
39026
|
+
},
|
|
39027
|
+
{
|
|
39028
|
+
key: "cooking-pot",
|
|
39029
|
+
group: "lighting",
|
|
39030
|
+
synonyms: [
|
|
39031
|
+
"pentola",
|
|
39032
|
+
"cucina",
|
|
39033
|
+
"fornello"
|
|
39034
|
+
]
|
|
39035
|
+
},
|
|
39036
|
+
{
|
|
39037
|
+
key: "utensils",
|
|
39038
|
+
group: "lighting",
|
|
39039
|
+
synonyms: [
|
|
39040
|
+
"cucina",
|
|
39041
|
+
"posate",
|
|
39042
|
+
"sala",
|
|
39043
|
+
"pranzo"
|
|
39044
|
+
]
|
|
39045
|
+
},
|
|
39046
|
+
{
|
|
39047
|
+
key: "thermometer",
|
|
39048
|
+
group: "sensing",
|
|
39049
|
+
synonyms: [
|
|
39050
|
+
"temperatura",
|
|
39051
|
+
"termometro",
|
|
39052
|
+
"termostato"
|
|
39053
|
+
]
|
|
39054
|
+
},
|
|
39055
|
+
{
|
|
39056
|
+
key: "droplets",
|
|
39057
|
+
group: "sensing",
|
|
39058
|
+
synonyms: [
|
|
39059
|
+
"umidita",
|
|
39060
|
+
"acqua",
|
|
39061
|
+
"allagamento",
|
|
39062
|
+
"perdita"
|
|
39063
|
+
]
|
|
39064
|
+
},
|
|
39065
|
+
{
|
|
39066
|
+
key: "droplet",
|
|
39067
|
+
group: "sensing",
|
|
39068
|
+
synonyms: [
|
|
39069
|
+
"goccia",
|
|
39070
|
+
"acqua",
|
|
39071
|
+
"perdita",
|
|
39072
|
+
"allagamento"
|
|
39073
|
+
]
|
|
39074
|
+
},
|
|
39075
|
+
{
|
|
39076
|
+
key: "gauge",
|
|
39077
|
+
group: "sensing",
|
|
39078
|
+
synonyms: [
|
|
39079
|
+
"misura",
|
|
39080
|
+
"livello",
|
|
39081
|
+
"pressione",
|
|
39082
|
+
"indicatore"
|
|
39083
|
+
]
|
|
39084
|
+
},
|
|
39085
|
+
{
|
|
39086
|
+
key: "gauge2",
|
|
39087
|
+
group: "sensing",
|
|
39088
|
+
synonyms: [
|
|
39089
|
+
"misura",
|
|
39090
|
+
"livello",
|
|
39091
|
+
"pressione",
|
|
39092
|
+
"indicatore"
|
|
39093
|
+
],
|
|
39094
|
+
aliasOf: "gauge"
|
|
39095
|
+
},
|
|
39096
|
+
{
|
|
39097
|
+
key: "sun",
|
|
39098
|
+
group: "sensing",
|
|
39099
|
+
synonyms: [
|
|
39100
|
+
"sole",
|
|
39101
|
+
"luminosita",
|
|
39102
|
+
"giorno",
|
|
39103
|
+
"esterno"
|
|
39104
|
+
]
|
|
39105
|
+
},
|
|
39106
|
+
{
|
|
39107
|
+
key: "sun-medium",
|
|
39108
|
+
group: "sensing",
|
|
39109
|
+
synonyms: ["sole", "luminosita"]
|
|
39110
|
+
},
|
|
39111
|
+
{
|
|
39112
|
+
key: "sunrise",
|
|
39113
|
+
group: "sensing",
|
|
39114
|
+
synonyms: ["alba"]
|
|
39115
|
+
},
|
|
39116
|
+
{
|
|
39117
|
+
key: "moon",
|
|
39118
|
+
group: "sensing",
|
|
39119
|
+
synonyms: [
|
|
39120
|
+
"notte",
|
|
39121
|
+
"luna",
|
|
39122
|
+
"notturna",
|
|
39123
|
+
"nightvision"
|
|
39124
|
+
]
|
|
39125
|
+
},
|
|
39126
|
+
{
|
|
39127
|
+
key: "wind",
|
|
39128
|
+
group: "sensing",
|
|
39129
|
+
synonyms: [
|
|
39130
|
+
"vento",
|
|
39131
|
+
"aria",
|
|
39132
|
+
"gas",
|
|
39133
|
+
"monossido",
|
|
39134
|
+
"co"
|
|
39135
|
+
]
|
|
39136
|
+
},
|
|
39137
|
+
{
|
|
39138
|
+
key: "cloud",
|
|
39139
|
+
group: "sensing",
|
|
39140
|
+
synonyms: ["nuvola", "nuvoloso"]
|
|
39141
|
+
},
|
|
39142
|
+
{
|
|
39143
|
+
key: "cloud-rain",
|
|
39144
|
+
group: "sensing",
|
|
39145
|
+
synonyms: ["pioggia"]
|
|
39146
|
+
},
|
|
39147
|
+
{
|
|
39148
|
+
key: "cloud-sun",
|
|
39149
|
+
group: "sensing",
|
|
39150
|
+
synonyms: [
|
|
39151
|
+
"variabile",
|
|
39152
|
+
"sole",
|
|
39153
|
+
"nuvole"
|
|
39154
|
+
]
|
|
39155
|
+
},
|
|
39156
|
+
{
|
|
39157
|
+
key: "waves",
|
|
39158
|
+
group: "sensing",
|
|
39159
|
+
synonyms: [
|
|
39160
|
+
"onde",
|
|
39161
|
+
"acqua",
|
|
39162
|
+
"vibrazione"
|
|
39163
|
+
]
|
|
39164
|
+
},
|
|
39165
|
+
{
|
|
39166
|
+
key: "sprout",
|
|
39167
|
+
group: "sensing",
|
|
39168
|
+
synonyms: [
|
|
39169
|
+
"irrigazione",
|
|
39170
|
+
"pianta",
|
|
39171
|
+
"germoglio",
|
|
39172
|
+
"orto"
|
|
39173
|
+
]
|
|
39174
|
+
},
|
|
39175
|
+
{
|
|
39176
|
+
key: "leaf",
|
|
39177
|
+
group: "sensing",
|
|
39178
|
+
synonyms: [
|
|
39179
|
+
"foglia",
|
|
39180
|
+
"pianta",
|
|
39181
|
+
"verde"
|
|
39182
|
+
]
|
|
39183
|
+
},
|
|
39184
|
+
{
|
|
39185
|
+
key: "flower",
|
|
39186
|
+
group: "sensing",
|
|
39187
|
+
synonyms: ["fiore", "pianta"]
|
|
39188
|
+
},
|
|
39189
|
+
{
|
|
39190
|
+
key: "zap",
|
|
39191
|
+
group: "sensing",
|
|
39192
|
+
synonyms: [
|
|
39193
|
+
"energia",
|
|
39194
|
+
"corrente",
|
|
39195
|
+
"consumo",
|
|
39196
|
+
"watt",
|
|
39197
|
+
"elettricita"
|
|
39198
|
+
]
|
|
39199
|
+
},
|
|
39200
|
+
{
|
|
39201
|
+
key: "flame",
|
|
39202
|
+
group: "sensing",
|
|
39203
|
+
synonyms: [
|
|
39204
|
+
"fiamma",
|
|
39205
|
+
"gas",
|
|
39206
|
+
"fuoco",
|
|
39207
|
+
"incendio"
|
|
39208
|
+
]
|
|
39209
|
+
},
|
|
39210
|
+
{
|
|
39211
|
+
key: "bell",
|
|
39212
|
+
group: "sensing",
|
|
39213
|
+
synonyms: [
|
|
39214
|
+
"campanello",
|
|
39215
|
+
"suoneria",
|
|
39216
|
+
"notifica",
|
|
39217
|
+
"bell",
|
|
39218
|
+
"chime"
|
|
39219
|
+
]
|
|
39220
|
+
},
|
|
39221
|
+
{
|
|
39222
|
+
key: "bell-ring",
|
|
39223
|
+
group: "sensing",
|
|
39224
|
+
synonyms: [
|
|
39225
|
+
"campanello",
|
|
39226
|
+
"suona",
|
|
39227
|
+
"doorbell",
|
|
39228
|
+
"notifica"
|
|
39229
|
+
]
|
|
39230
|
+
},
|
|
39231
|
+
{
|
|
39232
|
+
key: "bell-off",
|
|
39233
|
+
group: "sensing",
|
|
39234
|
+
synonyms: [
|
|
39235
|
+
"silenzioso",
|
|
39236
|
+
"notifiche",
|
|
39237
|
+
"disattivate",
|
|
39238
|
+
"muto"
|
|
39239
|
+
]
|
|
39240
|
+
},
|
|
39241
|
+
{
|
|
39242
|
+
key: "siren",
|
|
39243
|
+
group: "sensing",
|
|
39244
|
+
synonyms: ["sirena", "allarme"]
|
|
39245
|
+
},
|
|
39246
|
+
{
|
|
39247
|
+
key: "radar",
|
|
39248
|
+
group: "sensing",
|
|
39249
|
+
synonyms: [
|
|
39250
|
+
"movimento",
|
|
39251
|
+
"presenza",
|
|
39252
|
+
"pir",
|
|
39253
|
+
"rilevamento",
|
|
39254
|
+
"radar"
|
|
39255
|
+
]
|
|
39256
|
+
},
|
|
39257
|
+
{
|
|
39258
|
+
key: "activity",
|
|
39259
|
+
group: "sensing",
|
|
39260
|
+
synonyms: [
|
|
39261
|
+
"attivita",
|
|
39262
|
+
"sensore",
|
|
39263
|
+
"generico"
|
|
39264
|
+
]
|
|
39265
|
+
},
|
|
39266
|
+
{
|
|
39267
|
+
key: "hash",
|
|
39268
|
+
group: "sensing",
|
|
39269
|
+
synonyms: [
|
|
39270
|
+
"numero",
|
|
39271
|
+
"contatore",
|
|
39272
|
+
"valore"
|
|
39273
|
+
]
|
|
39274
|
+
},
|
|
39275
|
+
{
|
|
39276
|
+
key: "clock",
|
|
39277
|
+
group: "sensing",
|
|
39278
|
+
synonyms: [
|
|
39279
|
+
"orologio",
|
|
39280
|
+
"ora",
|
|
39281
|
+
"tempo"
|
|
39282
|
+
]
|
|
39283
|
+
},
|
|
39284
|
+
{
|
|
39285
|
+
key: "timer",
|
|
39286
|
+
group: "sensing",
|
|
39287
|
+
synonyms: [
|
|
39288
|
+
"timer",
|
|
39289
|
+
"conto",
|
|
39290
|
+
"rovescia"
|
|
39291
|
+
]
|
|
39292
|
+
},
|
|
39293
|
+
{
|
|
39294
|
+
key: "calendar",
|
|
39295
|
+
group: "sensing",
|
|
39296
|
+
synonyms: [
|
|
39297
|
+
"calendario",
|
|
39298
|
+
"data",
|
|
39299
|
+
"pianificazione"
|
|
39300
|
+
]
|
|
39301
|
+
},
|
|
39302
|
+
{
|
|
39303
|
+
key: "pipette",
|
|
39304
|
+
group: "sensing",
|
|
39305
|
+
synonyms: [
|
|
39306
|
+
"qualita",
|
|
39307
|
+
"aria",
|
|
39308
|
+
"misura",
|
|
39309
|
+
"campione"
|
|
39310
|
+
]
|
|
39311
|
+
},
|
|
39312
|
+
{
|
|
39313
|
+
key: "fan",
|
|
39314
|
+
group: "sensing",
|
|
39315
|
+
synonyms: [
|
|
39316
|
+
"ventilatore",
|
|
39317
|
+
"ventola",
|
|
39318
|
+
"aria"
|
|
39319
|
+
]
|
|
39320
|
+
},
|
|
39321
|
+
{
|
|
39322
|
+
key: "bath",
|
|
39323
|
+
group: "sensing",
|
|
39324
|
+
synonyms: [
|
|
39325
|
+
"bagno",
|
|
39326
|
+
"vasca",
|
|
39327
|
+
"doccia"
|
|
39328
|
+
]
|
|
39329
|
+
},
|
|
39330
|
+
{
|
|
39331
|
+
key: "footprints",
|
|
39332
|
+
group: "sensing",
|
|
39333
|
+
synonyms: [
|
|
39334
|
+
"movimento",
|
|
39335
|
+
"passi",
|
|
39336
|
+
"presenza",
|
|
39337
|
+
"motion"
|
|
39338
|
+
]
|
|
39339
|
+
},
|
|
39340
|
+
{
|
|
39341
|
+
key: "alarm-smoke",
|
|
39342
|
+
group: "sensing",
|
|
39343
|
+
synonyms: [
|
|
39344
|
+
"fumo",
|
|
39345
|
+
"antincendio",
|
|
39346
|
+
"rilevatore"
|
|
39347
|
+
]
|
|
39348
|
+
},
|
|
39349
|
+
{
|
|
39350
|
+
key: "shield-alert",
|
|
39351
|
+
group: "sensing",
|
|
39352
|
+
synonyms: [
|
|
39353
|
+
"manomissione",
|
|
39354
|
+
"allarme",
|
|
39355
|
+
"tamper",
|
|
39356
|
+
"avviso"
|
|
39357
|
+
]
|
|
39358
|
+
},
|
|
39359
|
+
{
|
|
39360
|
+
key: "vibrate",
|
|
39361
|
+
group: "sensing",
|
|
39362
|
+
synonyms: [
|
|
39363
|
+
"vibrazione",
|
|
39364
|
+
"urto",
|
|
39365
|
+
"tamper"
|
|
39366
|
+
]
|
|
39367
|
+
},
|
|
39368
|
+
{
|
|
39369
|
+
key: "wifi",
|
|
39370
|
+
group: "sensing",
|
|
39371
|
+
synonyms: [
|
|
39372
|
+
"wifi",
|
|
39373
|
+
"rete",
|
|
39374
|
+
"segnale"
|
|
39375
|
+
]
|
|
39376
|
+
},
|
|
39377
|
+
{
|
|
39378
|
+
key: "wifi-off",
|
|
39379
|
+
group: "sensing",
|
|
39380
|
+
synonyms: [
|
|
39381
|
+
"wifi",
|
|
39382
|
+
"assente",
|
|
39383
|
+
"offline",
|
|
39384
|
+
"disconnesso"
|
|
39385
|
+
]
|
|
39386
|
+
},
|
|
39387
|
+
{
|
|
39388
|
+
key: "volume-2",
|
|
39389
|
+
group: "sensing",
|
|
39390
|
+
synonyms: [
|
|
39391
|
+
"volume",
|
|
39392
|
+
"audio",
|
|
39393
|
+
"altoparlante",
|
|
39394
|
+
"suono"
|
|
39395
|
+
]
|
|
39396
|
+
},
|
|
39397
|
+
{
|
|
39398
|
+
key: "volume-x",
|
|
39399
|
+
group: "sensing",
|
|
39400
|
+
synonyms: [
|
|
39401
|
+
"muto",
|
|
39402
|
+
"silenziato",
|
|
39403
|
+
"volume",
|
|
39404
|
+
"spento"
|
|
39405
|
+
]
|
|
39406
|
+
},
|
|
39407
|
+
{
|
|
39408
|
+
key: "heart",
|
|
39409
|
+
group: "sensing",
|
|
39410
|
+
synonyms: [
|
|
39411
|
+
"cuore",
|
|
39412
|
+
"preferito",
|
|
39413
|
+
"salute"
|
|
39414
|
+
]
|
|
39415
|
+
},
|
|
39416
|
+
{
|
|
39417
|
+
key: "heart-pulse",
|
|
39418
|
+
group: "sensing",
|
|
39419
|
+
synonyms: [
|
|
39420
|
+
"battito",
|
|
39421
|
+
"salute",
|
|
39422
|
+
"monitoraggio"
|
|
39423
|
+
]
|
|
39424
|
+
},
|
|
39425
|
+
{
|
|
39426
|
+
key: "star",
|
|
39427
|
+
group: "sensing",
|
|
39428
|
+
synonyms: [
|
|
39429
|
+
"preferito",
|
|
39430
|
+
"stella",
|
|
39431
|
+
"importante"
|
|
39432
|
+
]
|
|
39433
|
+
},
|
|
39434
|
+
{
|
|
39435
|
+
key: "sparkles",
|
|
39436
|
+
group: "sensing",
|
|
39437
|
+
synonyms: [
|
|
39438
|
+
"automatico",
|
|
39439
|
+
"intelligente",
|
|
39440
|
+
"ai",
|
|
39441
|
+
"scena"
|
|
39442
|
+
]
|
|
39443
|
+
},
|
|
39444
|
+
{
|
|
39445
|
+
key: "house",
|
|
39446
|
+
group: "place",
|
|
39447
|
+
synonyms: [
|
|
39448
|
+
"casa",
|
|
39449
|
+
"abitazione",
|
|
39450
|
+
"home"
|
|
39451
|
+
]
|
|
39452
|
+
},
|
|
39453
|
+
{
|
|
39454
|
+
key: "sofa",
|
|
39455
|
+
group: "place",
|
|
39456
|
+
synonyms: [
|
|
39457
|
+
"salone",
|
|
39458
|
+
"soggiorno",
|
|
39459
|
+
"divano",
|
|
39460
|
+
"living"
|
|
39461
|
+
]
|
|
39462
|
+
},
|
|
39463
|
+
{
|
|
39464
|
+
key: "bed-double",
|
|
39465
|
+
group: "place",
|
|
39466
|
+
synonyms: [
|
|
39467
|
+
"camera",
|
|
39468
|
+
"letto",
|
|
39469
|
+
"matrimoniale"
|
|
39470
|
+
]
|
|
39471
|
+
},
|
|
39472
|
+
{
|
|
39473
|
+
key: "bed",
|
|
39474
|
+
group: "place",
|
|
39475
|
+
synonyms: [
|
|
39476
|
+
"cameretta",
|
|
39477
|
+
"letto",
|
|
39478
|
+
"singolo"
|
|
39479
|
+
]
|
|
39480
|
+
},
|
|
39481
|
+
{
|
|
39482
|
+
key: "warehouse",
|
|
39483
|
+
group: "place",
|
|
39484
|
+
synonyms: [
|
|
39485
|
+
"magazzino",
|
|
39486
|
+
"capanno",
|
|
39487
|
+
"deposito",
|
|
39488
|
+
"cantina"
|
|
39489
|
+
]
|
|
39490
|
+
},
|
|
39491
|
+
{
|
|
39492
|
+
key: "trees",
|
|
39493
|
+
group: "place",
|
|
39494
|
+
synonyms: [
|
|
39495
|
+
"giardino",
|
|
39496
|
+
"alberi",
|
|
39497
|
+
"esterno",
|
|
39498
|
+
"parco"
|
|
39499
|
+
]
|
|
39500
|
+
},
|
|
39501
|
+
{
|
|
39502
|
+
key: "tree-pine",
|
|
39503
|
+
group: "place",
|
|
39504
|
+
synonyms: [
|
|
39505
|
+
"albero",
|
|
39506
|
+
"pino",
|
|
39507
|
+
"giardino"
|
|
39508
|
+
]
|
|
39509
|
+
},
|
|
39510
|
+
{
|
|
39511
|
+
key: "fence",
|
|
39512
|
+
group: "place",
|
|
39513
|
+
synonyms: [
|
|
39514
|
+
"recinto",
|
|
39515
|
+
"cancello",
|
|
39516
|
+
"staccionata",
|
|
39517
|
+
"perimetro"
|
|
39518
|
+
]
|
|
39519
|
+
},
|
|
39520
|
+
{
|
|
39521
|
+
key: "store",
|
|
39522
|
+
group: "place",
|
|
39523
|
+
synonyms: [
|
|
39524
|
+
"negozio",
|
|
39525
|
+
"ingresso",
|
|
39526
|
+
"vetrina"
|
|
39527
|
+
]
|
|
39528
|
+
},
|
|
39529
|
+
{
|
|
39530
|
+
key: "building",
|
|
39531
|
+
group: "place",
|
|
39532
|
+
synonyms: ["edificio", "palazzo"]
|
|
39533
|
+
},
|
|
39534
|
+
{
|
|
39535
|
+
key: "building-2",
|
|
39536
|
+
group: "place",
|
|
39537
|
+
synonyms: [
|
|
39538
|
+
"edificio",
|
|
39539
|
+
"uffici",
|
|
39540
|
+
"condominio"
|
|
39541
|
+
]
|
|
39542
|
+
},
|
|
39543
|
+
{
|
|
39544
|
+
key: "mountain",
|
|
39545
|
+
group: "place",
|
|
39546
|
+
synonyms: [
|
|
39547
|
+
"montagna",
|
|
39548
|
+
"esterno",
|
|
39549
|
+
"panorama"
|
|
39550
|
+
]
|
|
39551
|
+
},
|
|
39552
|
+
{
|
|
39553
|
+
key: "umbrella",
|
|
39554
|
+
group: "place",
|
|
39555
|
+
synonyms: [
|
|
39556
|
+
"tenda",
|
|
39557
|
+
"markise",
|
|
39558
|
+
"ombrellone",
|
|
39559
|
+
"pergola",
|
|
39560
|
+
"veranda"
|
|
39561
|
+
]
|
|
39562
|
+
},
|
|
39563
|
+
{
|
|
39564
|
+
key: "dumbbell",
|
|
39565
|
+
group: "place",
|
|
39566
|
+
synonyms: ["palestra", "attrezzi"]
|
|
39567
|
+
},
|
|
39568
|
+
{
|
|
39569
|
+
key: "wrench",
|
|
39570
|
+
group: "place",
|
|
39571
|
+
synonyms: [
|
|
39572
|
+
"officina",
|
|
39573
|
+
"manutenzione",
|
|
39574
|
+
"attrezzi",
|
|
39575
|
+
"utenze"
|
|
39576
|
+
]
|
|
39577
|
+
},
|
|
39578
|
+
{
|
|
39579
|
+
key: "hammer",
|
|
39580
|
+
group: "place",
|
|
39581
|
+
synonyms: [
|
|
39582
|
+
"officina",
|
|
39583
|
+
"martello",
|
|
39584
|
+
"lavori"
|
|
39585
|
+
]
|
|
39586
|
+
},
|
|
39587
|
+
{
|
|
39588
|
+
key: "drill",
|
|
39589
|
+
group: "place",
|
|
39590
|
+
synonyms: [
|
|
39591
|
+
"officina",
|
|
39592
|
+
"trapano",
|
|
39593
|
+
"lavori"
|
|
39594
|
+
]
|
|
39595
|
+
},
|
|
39596
|
+
{
|
|
39597
|
+
key: "waves-ladder",
|
|
39598
|
+
group: "place",
|
|
39599
|
+
synonyms: ["piscina", "vasca"]
|
|
39600
|
+
},
|
|
39601
|
+
{
|
|
39602
|
+
key: "circle-parking",
|
|
39603
|
+
group: "place",
|
|
39604
|
+
synonyms: [
|
|
39605
|
+
"parcheggio",
|
|
39606
|
+
"posto",
|
|
39607
|
+
"auto",
|
|
39608
|
+
"garage"
|
|
39609
|
+
]
|
|
39610
|
+
},
|
|
39611
|
+
{
|
|
39612
|
+
key: "route",
|
|
39613
|
+
group: "place",
|
|
39614
|
+
synonyms: [
|
|
39615
|
+
"percorso",
|
|
39616
|
+
"strada",
|
|
39617
|
+
"vialetto",
|
|
39618
|
+
"tragitto"
|
|
39619
|
+
]
|
|
39620
|
+
},
|
|
39621
|
+
{
|
|
39622
|
+
key: "car",
|
|
39623
|
+
group: "life",
|
|
39624
|
+
synonyms: [
|
|
39625
|
+
"auto",
|
|
39626
|
+
"macchina",
|
|
39627
|
+
"veicolo",
|
|
39628
|
+
"parcheggio"
|
|
39629
|
+
]
|
|
39630
|
+
},
|
|
39631
|
+
{
|
|
39632
|
+
key: "bike",
|
|
39633
|
+
group: "life",
|
|
39634
|
+
synonyms: [
|
|
39635
|
+
"bicicletta",
|
|
39636
|
+
"bici",
|
|
39637
|
+
"moto"
|
|
39638
|
+
]
|
|
39639
|
+
},
|
|
39640
|
+
{
|
|
39641
|
+
key: "bus",
|
|
39642
|
+
group: "life",
|
|
39643
|
+
synonyms: ["autobus", "pullman"]
|
|
39644
|
+
},
|
|
39645
|
+
{
|
|
39646
|
+
key: "truck",
|
|
39647
|
+
group: "life",
|
|
39648
|
+
synonyms: [
|
|
39649
|
+
"camion",
|
|
39650
|
+
"furgone",
|
|
39651
|
+
"consegna"
|
|
39652
|
+
]
|
|
39653
|
+
},
|
|
39654
|
+
{
|
|
39655
|
+
key: "plane",
|
|
39656
|
+
group: "life",
|
|
39657
|
+
synonyms: ["aereo"]
|
|
39658
|
+
},
|
|
39659
|
+
{
|
|
39660
|
+
key: "train-front",
|
|
39661
|
+
group: "life",
|
|
39662
|
+
synonyms: ["treno"]
|
|
39663
|
+
},
|
|
39664
|
+
{
|
|
39665
|
+
key: "sailboat",
|
|
39666
|
+
group: "life",
|
|
39667
|
+
synonyms: ["barca", "imbarcazione"]
|
|
39668
|
+
},
|
|
39669
|
+
{
|
|
39670
|
+
key: "dog",
|
|
39671
|
+
group: "life",
|
|
39672
|
+
synonyms: ["cane", "animale"]
|
|
39673
|
+
},
|
|
39674
|
+
{
|
|
39675
|
+
key: "cat",
|
|
39676
|
+
group: "life",
|
|
39677
|
+
synonyms: ["gatto", "animale"]
|
|
39678
|
+
},
|
|
39679
|
+
{
|
|
39680
|
+
key: "bird",
|
|
39681
|
+
group: "life",
|
|
39682
|
+
synonyms: ["uccello", "animale"]
|
|
39683
|
+
},
|
|
39684
|
+
{
|
|
39685
|
+
key: "paw-print",
|
|
39686
|
+
group: "life",
|
|
39687
|
+
synonyms: [
|
|
39688
|
+
"animale",
|
|
39689
|
+
"zampa",
|
|
39690
|
+
"pet"
|
|
39691
|
+
]
|
|
39692
|
+
},
|
|
39693
|
+
{
|
|
39694
|
+
key: "baby",
|
|
39695
|
+
group: "life",
|
|
39696
|
+
synonyms: [
|
|
39697
|
+
"neonato",
|
|
39698
|
+
"bimbo",
|
|
39699
|
+
"cameretta",
|
|
39700
|
+
"babymonitor"
|
|
39701
|
+
]
|
|
39702
|
+
},
|
|
39703
|
+
{
|
|
39704
|
+
key: "user",
|
|
39705
|
+
group: "life",
|
|
39706
|
+
synonyms: ["persona", "utente"]
|
|
39707
|
+
},
|
|
39708
|
+
{
|
|
39709
|
+
key: "users",
|
|
39710
|
+
group: "life",
|
|
39711
|
+
synonyms: [
|
|
39712
|
+
"persone",
|
|
39713
|
+
"gruppo",
|
|
39714
|
+
"famiglia"
|
|
39715
|
+
]
|
|
39716
|
+
},
|
|
39717
|
+
{
|
|
39718
|
+
key: "person-standing",
|
|
39719
|
+
group: "life",
|
|
39720
|
+
synonyms: [
|
|
39721
|
+
"persona",
|
|
39722
|
+
"piedi",
|
|
39723
|
+
"presenza"
|
|
39724
|
+
]
|
|
39725
|
+
}
|
|
39726
|
+
];
|
|
39727
|
+
DISPLAY_ICON_VOCABULARY.map((e) => e.key);
|
|
39728
|
+
DISPLAY_ICON_VOCABULARY.filter((e) => e.aliasOf === void 0).map((e) => e.key);
|
|
39729
|
+
new Map(DISPLAY_ICON_VOCABULARY.map((e) => [e.key, e]));
|
|
36989
39730
|
Object.freeze({
|
|
39731
|
+
"accessories.getStatus": {
|
|
39732
|
+
capName: "accessories",
|
|
39733
|
+
capScope: "device",
|
|
39734
|
+
addonId: null,
|
|
39735
|
+
access: "view"
|
|
39736
|
+
},
|
|
36990
39737
|
"accessories.setChildHidden": {
|
|
36991
39738
|
capName: "accessories",
|
|
36992
39739
|
capScope: "device",
|
|
@@ -37269,6 +40016,12 @@ Object.freeze({
|
|
|
37269
40016
|
addonId: null,
|
|
37270
40017
|
access: "view"
|
|
37271
40018
|
},
|
|
40019
|
+
"airQualitySensor.getStatus": {
|
|
40020
|
+
capName: "air-quality-sensor",
|
|
40021
|
+
capScope: "device",
|
|
40022
|
+
addonId: null,
|
|
40023
|
+
access: "view"
|
|
40024
|
+
},
|
|
37272
40025
|
"alarmPanel.arm": {
|
|
37273
40026
|
capName: "alarm-panel",
|
|
37274
40027
|
capScope: "device",
|
|
@@ -37281,6 +40034,12 @@ Object.freeze({
|
|
|
37281
40034
|
addonId: null,
|
|
37282
40035
|
access: "create"
|
|
37283
40036
|
},
|
|
40037
|
+
"alarmPanel.getStatus": {
|
|
40038
|
+
capName: "alarm-panel",
|
|
40039
|
+
capScope: "device",
|
|
40040
|
+
addonId: null,
|
|
40041
|
+
access: "view"
|
|
40042
|
+
},
|
|
37284
40043
|
"alarmPanel.trigger": {
|
|
37285
40044
|
capName: "alarm-panel",
|
|
37286
40045
|
capScope: "device",
|
|
@@ -37329,6 +40088,30 @@ Object.freeze({
|
|
|
37329
40088
|
addonId: null,
|
|
37330
40089
|
access: "create"
|
|
37331
40090
|
},
|
|
40091
|
+
"ambientLightSensor.getStatus": {
|
|
40092
|
+
capName: "ambient-light-sensor",
|
|
40093
|
+
capScope: "device",
|
|
40094
|
+
addonId: null,
|
|
40095
|
+
access: "view"
|
|
40096
|
+
},
|
|
40097
|
+
"audioAnalysis.applyDeviceSettingsPatch": {
|
|
40098
|
+
capName: "audio-analysis",
|
|
40099
|
+
capScope: "device",
|
|
40100
|
+
addonId: null,
|
|
40101
|
+
access: "create"
|
|
40102
|
+
},
|
|
40103
|
+
"audioAnalysis.getDeviceLiveContribution": {
|
|
40104
|
+
capName: "audio-analysis",
|
|
40105
|
+
capScope: "device",
|
|
40106
|
+
addonId: null,
|
|
40107
|
+
access: "view"
|
|
40108
|
+
},
|
|
40109
|
+
"audioAnalysis.getDeviceSettingsContribution": {
|
|
40110
|
+
capName: "audio-analysis",
|
|
40111
|
+
capScope: "device",
|
|
40112
|
+
addonId: null,
|
|
40113
|
+
access: "view"
|
|
40114
|
+
},
|
|
37332
40115
|
"audioAnalysis.resolveDeviceSettings": {
|
|
37333
40116
|
capName: "audio-analysis",
|
|
37334
40117
|
capScope: "device",
|
|
@@ -37491,6 +40274,12 @@ Object.freeze({
|
|
|
37491
40274
|
addonId: null,
|
|
37492
40275
|
access: "create"
|
|
37493
40276
|
},
|
|
40277
|
+
"automationControl.getStatus": {
|
|
40278
|
+
capName: "automation-control",
|
|
40279
|
+
capScope: "device",
|
|
40280
|
+
addonId: null,
|
|
40281
|
+
access: "view"
|
|
40282
|
+
},
|
|
37494
40283
|
"automationControl.trigger": {
|
|
37495
40284
|
capName: "automation-control",
|
|
37496
40285
|
capScope: "device",
|
|
@@ -37587,12 +40376,30 @@ Object.freeze({
|
|
|
37587
40376
|
addonId: null,
|
|
37588
40377
|
access: "create"
|
|
37589
40378
|
},
|
|
40379
|
+
"battery.getStatus": {
|
|
40380
|
+
capName: "battery",
|
|
40381
|
+
capScope: "device",
|
|
40382
|
+
addonId: null,
|
|
40383
|
+
access: "view"
|
|
40384
|
+
},
|
|
37590
40385
|
"battery.wakeForStream": {
|
|
37591
40386
|
capName: "battery",
|
|
37592
40387
|
capScope: "device",
|
|
37593
40388
|
addonId: null,
|
|
37594
40389
|
access: "create"
|
|
37595
40390
|
},
|
|
40391
|
+
"binary.getStatus": {
|
|
40392
|
+
capName: "binary",
|
|
40393
|
+
capScope: "device",
|
|
40394
|
+
addonId: null,
|
|
40395
|
+
access: "view"
|
|
40396
|
+
},
|
|
40397
|
+
"brightness.getStatus": {
|
|
40398
|
+
capName: "brightness",
|
|
40399
|
+
capScope: "device",
|
|
40400
|
+
addonId: null,
|
|
40401
|
+
access: "view"
|
|
40402
|
+
},
|
|
37596
40403
|
"brightness.setBrightness": {
|
|
37597
40404
|
capName: "brightness",
|
|
37598
40405
|
capScope: "device",
|
|
@@ -37707,6 +40514,30 @@ Object.freeze({
|
|
|
37707
40514
|
addonId: null,
|
|
37708
40515
|
access: "view"
|
|
37709
40516
|
},
|
|
40517
|
+
"cameraCredentials.getStatus": {
|
|
40518
|
+
capName: "camera-credentials",
|
|
40519
|
+
capScope: "device",
|
|
40520
|
+
addonId: null,
|
|
40521
|
+
access: "view"
|
|
40522
|
+
},
|
|
40523
|
+
"cameraGridLayout.applyDeviceSettingsPatch": {
|
|
40524
|
+
capName: "camera-grid-layout",
|
|
40525
|
+
capScope: "device",
|
|
40526
|
+
addonId: null,
|
|
40527
|
+
access: "create"
|
|
40528
|
+
},
|
|
40529
|
+
"cameraGridLayout.getDeviceLiveContribution": {
|
|
40530
|
+
capName: "camera-grid-layout",
|
|
40531
|
+
capScope: "device",
|
|
40532
|
+
addonId: null,
|
|
40533
|
+
access: "view"
|
|
40534
|
+
},
|
|
40535
|
+
"cameraGridLayout.getDeviceSettingsContribution": {
|
|
40536
|
+
capName: "camera-grid-layout",
|
|
40537
|
+
capScope: "device",
|
|
40538
|
+
addonId: null,
|
|
40539
|
+
access: "view"
|
|
40540
|
+
},
|
|
37710
40541
|
"cameraGridLayout.getLayout": {
|
|
37711
40542
|
capName: "camera-grid-layout",
|
|
37712
40543
|
capScope: "device",
|
|
@@ -37719,6 +40550,24 @@ Object.freeze({
|
|
|
37719
40550
|
addonId: null,
|
|
37720
40551
|
access: "create"
|
|
37721
40552
|
},
|
|
40553
|
+
"cameraPipelineConfig.applyDeviceSettingsPatch": {
|
|
40554
|
+
capName: "camera-pipeline-config",
|
|
40555
|
+
capScope: "device",
|
|
40556
|
+
addonId: null,
|
|
40557
|
+
access: "create"
|
|
40558
|
+
},
|
|
40559
|
+
"cameraPipelineConfig.getDeviceLiveContribution": {
|
|
40560
|
+
capName: "camera-pipeline-config",
|
|
40561
|
+
capScope: "device",
|
|
40562
|
+
addonId: null,
|
|
40563
|
+
access: "view"
|
|
40564
|
+
},
|
|
40565
|
+
"cameraPipelineConfig.getDeviceSettingsContribution": {
|
|
40566
|
+
capName: "camera-pipeline-config",
|
|
40567
|
+
capScope: "device",
|
|
40568
|
+
addonId: null,
|
|
40569
|
+
access: "view"
|
|
40570
|
+
},
|
|
37722
40571
|
"cameraStreams.getBrokerStreams": {
|
|
37723
40572
|
capName: "camera-streams",
|
|
37724
40573
|
capScope: "device",
|
|
@@ -37749,6 +40598,18 @@ Object.freeze({
|
|
|
37749
40598
|
addonId: null,
|
|
37750
40599
|
access: "view"
|
|
37751
40600
|
},
|
|
40601
|
+
"carbonMonoxide.getStatus": {
|
|
40602
|
+
capName: "carbon-monoxide",
|
|
40603
|
+
capScope: "device",
|
|
40604
|
+
addonId: null,
|
|
40605
|
+
access: "view"
|
|
40606
|
+
},
|
|
40607
|
+
"climateControl.getStatus": {
|
|
40608
|
+
capName: "climate-control",
|
|
40609
|
+
capScope: "device",
|
|
40610
|
+
addonId: null,
|
|
40611
|
+
access: "view"
|
|
40612
|
+
},
|
|
37752
40613
|
"climateControl.setFanMode": {
|
|
37753
40614
|
capName: "climate-control",
|
|
37754
40615
|
capScope: "device",
|
|
@@ -37797,6 +40658,12 @@ Object.freeze({
|
|
|
37797
40658
|
addonId: null,
|
|
37798
40659
|
access: "create"
|
|
37799
40660
|
},
|
|
40661
|
+
"color.getStatus": {
|
|
40662
|
+
capName: "color",
|
|
40663
|
+
capScope: "device",
|
|
40664
|
+
addonId: null,
|
|
40665
|
+
access: "view"
|
|
40666
|
+
},
|
|
37800
40667
|
"color.setColor": {
|
|
37801
40668
|
capName: "color",
|
|
37802
40669
|
capScope: "device",
|
|
@@ -37815,12 +40682,36 @@ Object.freeze({
|
|
|
37815
40682
|
addonId: null,
|
|
37816
40683
|
access: "create"
|
|
37817
40684
|
},
|
|
40685
|
+
"connectivity.getStatus": {
|
|
40686
|
+
capName: "connectivity",
|
|
40687
|
+
capScope: "device",
|
|
40688
|
+
addonId: null,
|
|
40689
|
+
access: "view"
|
|
40690
|
+
},
|
|
40691
|
+
"consumables.getStatus": {
|
|
40692
|
+
capName: "consumables",
|
|
40693
|
+
capScope: "device",
|
|
40694
|
+
addonId: null,
|
|
40695
|
+
access: "view"
|
|
40696
|
+
},
|
|
37818
40697
|
"consumables.reset": {
|
|
37819
40698
|
capName: "consumables",
|
|
37820
40699
|
capScope: "device",
|
|
37821
40700
|
addonId: null,
|
|
37822
40701
|
access: "delete"
|
|
37823
40702
|
},
|
|
40703
|
+
"contact.getStatus": {
|
|
40704
|
+
capName: "contact",
|
|
40705
|
+
capScope: "device",
|
|
40706
|
+
addonId: null,
|
|
40707
|
+
access: "view"
|
|
40708
|
+
},
|
|
40709
|
+
"control.getStatus": {
|
|
40710
|
+
capName: "control",
|
|
40711
|
+
capScope: "device",
|
|
40712
|
+
addonId: null,
|
|
40713
|
+
access: "view"
|
|
40714
|
+
},
|
|
37824
40715
|
"control.setValue": {
|
|
37825
40716
|
capName: "control",
|
|
37826
40717
|
capScope: "device",
|
|
@@ -37887,6 +40778,12 @@ Object.freeze({
|
|
|
37887
40778
|
addonId: null,
|
|
37888
40779
|
access: "create"
|
|
37889
40780
|
},
|
|
40781
|
+
"cover.getStatus": {
|
|
40782
|
+
capName: "cover",
|
|
40783
|
+
capScope: "device",
|
|
40784
|
+
addonId: null,
|
|
40785
|
+
access: "view"
|
|
40786
|
+
},
|
|
37890
40787
|
"cover.open": {
|
|
37891
40788
|
capName: "cover",
|
|
37892
40789
|
capScope: "device",
|
|
@@ -38019,6 +40916,12 @@ Object.freeze({
|
|
|
38019
40916
|
addonId: null,
|
|
38020
40917
|
access: "view"
|
|
38021
40918
|
},
|
|
40919
|
+
"dayNight.getStatus": {
|
|
40920
|
+
capName: "day-night",
|
|
40921
|
+
capScope: "device",
|
|
40922
|
+
addonId: null,
|
|
40923
|
+
access: "view"
|
|
40924
|
+
},
|
|
38022
40925
|
"dayNight.setSettings": {
|
|
38023
40926
|
capName: "day-night",
|
|
38024
40927
|
capScope: "device",
|
|
@@ -38109,6 +41012,30 @@ Object.freeze({
|
|
|
38109
41012
|
addonId: null,
|
|
38110
41013
|
access: "view"
|
|
38111
41014
|
},
|
|
41015
|
+
"detectionPipeline.applyDeviceSettingsPatch": {
|
|
41016
|
+
capName: "detection-pipeline",
|
|
41017
|
+
capScope: "device",
|
|
41018
|
+
addonId: null,
|
|
41019
|
+
access: "create"
|
|
41020
|
+
},
|
|
41021
|
+
"detectionPipeline.getDeviceLiveContribution": {
|
|
41022
|
+
capName: "detection-pipeline",
|
|
41023
|
+
capScope: "device",
|
|
41024
|
+
addonId: null,
|
|
41025
|
+
access: "view"
|
|
41026
|
+
},
|
|
41027
|
+
"detectionPipeline.getDeviceSettingsContribution": {
|
|
41028
|
+
capName: "detection-pipeline",
|
|
41029
|
+
capScope: "device",
|
|
41030
|
+
addonId: null,
|
|
41031
|
+
access: "view"
|
|
41032
|
+
},
|
|
41033
|
+
"deviceAdminLink.getAdminLink": {
|
|
41034
|
+
capName: "device-admin-link",
|
|
41035
|
+
capScope: "device",
|
|
41036
|
+
addonId: null,
|
|
41037
|
+
access: "view"
|
|
41038
|
+
},
|
|
38112
41039
|
"deviceAdoption.adopt": {
|
|
38113
41040
|
capName: "device-adoption",
|
|
38114
41041
|
capScope: "system",
|
|
@@ -38121,6 +41048,12 @@ Object.freeze({
|
|
|
38121
41048
|
addonId: null,
|
|
38122
41049
|
access: "view"
|
|
38123
41050
|
},
|
|
41051
|
+
"deviceAdoption.getStatus": {
|
|
41052
|
+
capName: "device-adoption",
|
|
41053
|
+
capScope: "system",
|
|
41054
|
+
addonId: null,
|
|
41055
|
+
access: "view"
|
|
41056
|
+
},
|
|
38124
41057
|
"deviceAdoption.listCandidateFilters": {
|
|
38125
41058
|
capName: "device-adoption",
|
|
38126
41059
|
capScope: "system",
|
|
@@ -38157,6 +41090,12 @@ Object.freeze({
|
|
|
38157
41090
|
addonId: null,
|
|
38158
41091
|
access: "create"
|
|
38159
41092
|
},
|
|
41093
|
+
"deviceDiscovery.getStatus": {
|
|
41094
|
+
capName: "device-discovery",
|
|
41095
|
+
capScope: "device",
|
|
41096
|
+
addonId: null,
|
|
41097
|
+
access: "view"
|
|
41098
|
+
},
|
|
38160
41099
|
"deviceDiscovery.listDiscovered": {
|
|
38161
41100
|
capName: "device-discovery",
|
|
38162
41101
|
capScope: "device",
|
|
@@ -38175,12 +41114,30 @@ Object.freeze({
|
|
|
38175
41114
|
addonId: null,
|
|
38176
41115
|
access: "create"
|
|
38177
41116
|
},
|
|
41117
|
+
"deviceExport.applyDeviceSettingsPatch": {
|
|
41118
|
+
capName: "device-export",
|
|
41119
|
+
capScope: "system",
|
|
41120
|
+
addonId: null,
|
|
41121
|
+
access: "create"
|
|
41122
|
+
},
|
|
38178
41123
|
"deviceExport.exposeDevice": {
|
|
38179
41124
|
capName: "device-export",
|
|
38180
41125
|
capScope: "system",
|
|
38181
41126
|
addonId: null,
|
|
38182
41127
|
access: "create"
|
|
38183
41128
|
},
|
|
41129
|
+
"deviceExport.getDeviceLiveContribution": {
|
|
41130
|
+
capName: "device-export",
|
|
41131
|
+
capScope: "system",
|
|
41132
|
+
addonId: null,
|
|
41133
|
+
access: "view"
|
|
41134
|
+
},
|
|
41135
|
+
"deviceExport.getDeviceSettingsContribution": {
|
|
41136
|
+
capName: "device-export",
|
|
41137
|
+
capScope: "system",
|
|
41138
|
+
addonId: null,
|
|
41139
|
+
access: "view"
|
|
41140
|
+
},
|
|
38184
41141
|
"deviceExport.getStatus": {
|
|
38185
41142
|
capName: "device-export",
|
|
38186
41143
|
capScope: "system",
|
|
@@ -38277,6 +41234,12 @@ Object.freeze({
|
|
|
38277
41234
|
addonId: null,
|
|
38278
41235
|
access: "create"
|
|
38279
41236
|
},
|
|
41237
|
+
"deviceManager.applyDeviceSettingsPatch": {
|
|
41238
|
+
capName: "device-manager",
|
|
41239
|
+
capScope: "system",
|
|
41240
|
+
addonId: null,
|
|
41241
|
+
access: "create"
|
|
41242
|
+
},
|
|
38280
41243
|
"deviceManager.applyInitialMeta": {
|
|
38281
41244
|
capName: "device-manager",
|
|
38282
41245
|
capScope: "system",
|
|
@@ -38379,6 +41342,12 @@ Object.freeze({
|
|
|
38379
41342
|
addonId: null,
|
|
38380
41343
|
access: "view"
|
|
38381
41344
|
},
|
|
41345
|
+
"deviceManager.getDeviceLiveContribution": {
|
|
41346
|
+
capName: "device-manager",
|
|
41347
|
+
capScope: "system",
|
|
41348
|
+
addonId: null,
|
|
41349
|
+
access: "view"
|
|
41350
|
+
},
|
|
38382
41351
|
"deviceManager.getDeviceLiveInfoAggregate": {
|
|
38383
41352
|
capName: "device-manager",
|
|
38384
41353
|
capScope: "system",
|
|
@@ -38391,6 +41360,12 @@ Object.freeze({
|
|
|
38391
41360
|
addonId: null,
|
|
38392
41361
|
access: "view"
|
|
38393
41362
|
},
|
|
41363
|
+
"deviceManager.getDeviceSettingsContribution": {
|
|
41364
|
+
capName: "device-manager",
|
|
41365
|
+
capScope: "system",
|
|
41366
|
+
addonId: null,
|
|
41367
|
+
access: "view"
|
|
41368
|
+
},
|
|
38394
41369
|
"deviceManager.getDeviceStatusAggregate": {
|
|
38395
41370
|
capName: "device-manager",
|
|
38396
41371
|
capScope: "system",
|
|
@@ -38883,6 +41858,36 @@ Object.freeze({
|
|
|
38883
41858
|
addonId: null,
|
|
38884
41859
|
access: "create"
|
|
38885
41860
|
},
|
|
41861
|
+
"deviceStatus.getStatus": {
|
|
41862
|
+
capName: "device-status",
|
|
41863
|
+
capScope: "device",
|
|
41864
|
+
addonId: null,
|
|
41865
|
+
access: "view"
|
|
41866
|
+
},
|
|
41867
|
+
"doorbell.applyDeviceSettingsPatch": {
|
|
41868
|
+
capName: "doorbell",
|
|
41869
|
+
capScope: "device",
|
|
41870
|
+
addonId: null,
|
|
41871
|
+
access: "create"
|
|
41872
|
+
},
|
|
41873
|
+
"doorbell.getDeviceLiveContribution": {
|
|
41874
|
+
capName: "doorbell",
|
|
41875
|
+
capScope: "device",
|
|
41876
|
+
addonId: null,
|
|
41877
|
+
access: "view"
|
|
41878
|
+
},
|
|
41879
|
+
"doorbell.getDeviceSettingsContribution": {
|
|
41880
|
+
capName: "doorbell",
|
|
41881
|
+
capScope: "device",
|
|
41882
|
+
addonId: null,
|
|
41883
|
+
access: "view"
|
|
41884
|
+
},
|
|
41885
|
+
"doorbell.getStatus": {
|
|
41886
|
+
capName: "doorbell",
|
|
41887
|
+
capScope: "device",
|
|
41888
|
+
addonId: null,
|
|
41889
|
+
access: "view"
|
|
41890
|
+
},
|
|
38886
41891
|
"embeddingEncoder.encode": {
|
|
38887
41892
|
capName: "embedding-encoder",
|
|
38888
41893
|
capScope: "system",
|
|
@@ -38901,6 +41906,18 @@ Object.freeze({
|
|
|
38901
41906
|
addonId: null,
|
|
38902
41907
|
access: "view"
|
|
38903
41908
|
},
|
|
41909
|
+
"enumSensor.getStatus": {
|
|
41910
|
+
capName: "enum-sensor",
|
|
41911
|
+
capScope: "device",
|
|
41912
|
+
addonId: null,
|
|
41913
|
+
access: "view"
|
|
41914
|
+
},
|
|
41915
|
+
"eventEmitter.getStatus": {
|
|
41916
|
+
capName: "event-emitter",
|
|
41917
|
+
capScope: "device",
|
|
41918
|
+
addonId: null,
|
|
41919
|
+
access: "view"
|
|
41920
|
+
},
|
|
38904
41921
|
"events.getEventClipUrl": {
|
|
38905
41922
|
capName: "events",
|
|
38906
41923
|
capScope: "device",
|
|
@@ -39021,6 +42038,12 @@ Object.freeze({
|
|
|
39021
42038
|
addonId: null,
|
|
39022
42039
|
access: "view"
|
|
39023
42040
|
},
|
|
42041
|
+
"fanControl.getStatus": {
|
|
42042
|
+
capName: "fan-control",
|
|
42043
|
+
capScope: "device",
|
|
42044
|
+
addonId: null,
|
|
42045
|
+
access: "view"
|
|
42046
|
+
},
|
|
39024
42047
|
"fanControl.setDirection": {
|
|
39025
42048
|
capName: "fan-control",
|
|
39026
42049
|
capScope: "device",
|
|
@@ -39045,6 +42068,12 @@ Object.freeze({
|
|
|
39045
42068
|
addonId: null,
|
|
39046
42069
|
access: "create"
|
|
39047
42070
|
},
|
|
42071
|
+
"featureProbe.getStatus": {
|
|
42072
|
+
capName: "feature-probe",
|
|
42073
|
+
capScope: "device",
|
|
42074
|
+
addonId: null,
|
|
42075
|
+
access: "view"
|
|
42076
|
+
},
|
|
39048
42077
|
"filesystemBrowse.browse": {
|
|
39049
42078
|
capName: "filesystem-browse",
|
|
39050
42079
|
capScope: "system",
|
|
@@ -39063,6 +42092,24 @@ Object.freeze({
|
|
|
39063
42092
|
addonId: null,
|
|
39064
42093
|
access: "view"
|
|
39065
42094
|
},
|
|
42095
|
+
"flood.getStatus": {
|
|
42096
|
+
capName: "flood",
|
|
42097
|
+
capScope: "device",
|
|
42098
|
+
addonId: null,
|
|
42099
|
+
access: "view"
|
|
42100
|
+
},
|
|
42101
|
+
"gas.getStatus": {
|
|
42102
|
+
capName: "gas",
|
|
42103
|
+
capScope: "device",
|
|
42104
|
+
addonId: null,
|
|
42105
|
+
access: "view"
|
|
42106
|
+
},
|
|
42107
|
+
"humidifier.getStatus": {
|
|
42108
|
+
capName: "humidifier",
|
|
42109
|
+
capScope: "device",
|
|
42110
|
+
addonId: null,
|
|
42111
|
+
access: "view"
|
|
42112
|
+
},
|
|
39066
42113
|
"humidifier.setMode": {
|
|
39067
42114
|
capName: "humidifier",
|
|
39068
42115
|
capScope: "device",
|
|
@@ -39081,12 +42128,30 @@ Object.freeze({
|
|
|
39081
42128
|
addonId: null,
|
|
39082
42129
|
access: "create"
|
|
39083
42130
|
},
|
|
42131
|
+
"humiditySensor.getStatus": {
|
|
42132
|
+
capName: "humidity-sensor",
|
|
42133
|
+
capScope: "device",
|
|
42134
|
+
addonId: null,
|
|
42135
|
+
access: "view"
|
|
42136
|
+
},
|
|
42137
|
+
"image.getStatus": {
|
|
42138
|
+
capName: "image",
|
|
42139
|
+
capScope: "device",
|
|
42140
|
+
addonId: null,
|
|
42141
|
+
access: "view"
|
|
42142
|
+
},
|
|
39084
42143
|
"imageSettings.getOptions": {
|
|
39085
42144
|
capName: "image-settings",
|
|
39086
42145
|
capScope: "device",
|
|
39087
42146
|
addonId: null,
|
|
39088
42147
|
access: "view"
|
|
39089
42148
|
},
|
|
42149
|
+
"imageSettings.getStatus": {
|
|
42150
|
+
capName: "image-settings",
|
|
42151
|
+
capScope: "device",
|
|
42152
|
+
addonId: null,
|
|
42153
|
+
access: "view"
|
|
42154
|
+
},
|
|
39090
42155
|
"imageSettings.setSettings": {
|
|
39091
42156
|
capName: "image-settings",
|
|
39092
42157
|
capScope: "device",
|
|
@@ -39159,6 +42224,12 @@ Object.freeze({
|
|
|
39159
42224
|
addonId: null,
|
|
39160
42225
|
access: "create"
|
|
39161
42226
|
},
|
|
42227
|
+
"intercom.getStatus": {
|
|
42228
|
+
capName: "intercom",
|
|
42229
|
+
capScope: "device",
|
|
42230
|
+
addonId: null,
|
|
42231
|
+
access: "view"
|
|
42232
|
+
},
|
|
39162
42233
|
"intercom.handleAnswer": {
|
|
39163
42234
|
capName: "intercom",
|
|
39164
42235
|
capScope: "device",
|
|
@@ -39195,6 +42266,12 @@ Object.freeze({
|
|
|
39195
42266
|
addonId: null,
|
|
39196
42267
|
access: "create"
|
|
39197
42268
|
},
|
|
42269
|
+
"lawnMowerControl.getStatus": {
|
|
42270
|
+
capName: "lawn-mower-control",
|
|
42271
|
+
capScope: "device",
|
|
42272
|
+
addonId: null,
|
|
42273
|
+
access: "view"
|
|
42274
|
+
},
|
|
39198
42275
|
"lawnMowerControl.pause": {
|
|
39199
42276
|
capName: "lawn-mower-control",
|
|
39200
42277
|
capScope: "device",
|
|
@@ -39477,6 +42554,12 @@ Object.freeze({
|
|
|
39477
42554
|
addonId: null,
|
|
39478
42555
|
access: "create"
|
|
39479
42556
|
},
|
|
42557
|
+
"lockControl.getStatus": {
|
|
42558
|
+
capName: "lock-control",
|
|
42559
|
+
capScope: "device",
|
|
42560
|
+
addonId: null,
|
|
42561
|
+
access: "view"
|
|
42562
|
+
},
|
|
39480
42563
|
"lockControl.lock": {
|
|
39481
42564
|
capName: "lock-control",
|
|
39482
42565
|
capScope: "device",
|
|
@@ -39525,6 +42608,12 @@ Object.freeze({
|
|
|
39525
42608
|
addonId: null,
|
|
39526
42609
|
access: "view"
|
|
39527
42610
|
},
|
|
42611
|
+
"mediaPlayer.getStatus": {
|
|
42612
|
+
capName: "media-player",
|
|
42613
|
+
capScope: "device",
|
|
42614
|
+
addonId: null,
|
|
42615
|
+
access: "view"
|
|
42616
|
+
},
|
|
39528
42617
|
"mediaPlayer.next": {
|
|
39529
42618
|
capName: "media-player",
|
|
39530
42619
|
capScope: "device",
|
|
@@ -39717,6 +42806,12 @@ Object.freeze({
|
|
|
39717
42806
|
addonId: null,
|
|
39718
42807
|
access: "create"
|
|
39719
42808
|
},
|
|
42809
|
+
"motion.getStatus": {
|
|
42810
|
+
capName: "motion",
|
|
42811
|
+
capScope: "device",
|
|
42812
|
+
addonId: null,
|
|
42813
|
+
access: "view"
|
|
42814
|
+
},
|
|
39720
42815
|
"motion.isDetected": {
|
|
39721
42816
|
capName: "motion",
|
|
39722
42817
|
capScope: "device",
|
|
@@ -39729,6 +42824,24 @@ Object.freeze({
|
|
|
39729
42824
|
addonId: null,
|
|
39730
42825
|
access: "create"
|
|
39731
42826
|
},
|
|
42827
|
+
"motionDetection.applyDeviceSettingsPatch": {
|
|
42828
|
+
capName: "motion-detection",
|
|
42829
|
+
capScope: "device",
|
|
42830
|
+
addonId: null,
|
|
42831
|
+
access: "create"
|
|
42832
|
+
},
|
|
42833
|
+
"motionDetection.getDeviceLiveContribution": {
|
|
42834
|
+
capName: "motion-detection",
|
|
42835
|
+
capScope: "device",
|
|
42836
|
+
addonId: null,
|
|
42837
|
+
access: "view"
|
|
42838
|
+
},
|
|
42839
|
+
"motionDetection.getDeviceSettingsContribution": {
|
|
42840
|
+
capName: "motion-detection",
|
|
42841
|
+
capScope: "device",
|
|
42842
|
+
addonId: null,
|
|
42843
|
+
access: "view"
|
|
42844
|
+
},
|
|
39732
42845
|
"motionDetection.removeCamera": {
|
|
39733
42846
|
capName: "motion-detection",
|
|
39734
42847
|
capScope: "device",
|
|
@@ -39741,6 +42854,12 @@ Object.freeze({
|
|
|
39741
42854
|
addonId: null,
|
|
39742
42855
|
access: "delete"
|
|
39743
42856
|
},
|
|
42857
|
+
"motionTrigger.getStatus": {
|
|
42858
|
+
capName: "motion-trigger",
|
|
42859
|
+
capScope: "device",
|
|
42860
|
+
addonId: null,
|
|
42861
|
+
access: "view"
|
|
42862
|
+
},
|
|
39744
42863
|
"motionTrigger.setMotionTrigger": {
|
|
39745
42864
|
capName: "motion-trigger",
|
|
39746
42865
|
capScope: "device",
|
|
@@ -39753,6 +42872,12 @@ Object.freeze({
|
|
|
39753
42872
|
addonId: null,
|
|
39754
42873
|
access: "view"
|
|
39755
42874
|
},
|
|
42875
|
+
"motionZones.getStatus": {
|
|
42876
|
+
capName: "motion-zones",
|
|
42877
|
+
capScope: "device",
|
|
42878
|
+
addonId: null,
|
|
42879
|
+
access: "view"
|
|
42880
|
+
},
|
|
39756
42881
|
"motionZones.setZone": {
|
|
39757
42882
|
capName: "motion-zones",
|
|
39758
42883
|
capScope: "device",
|
|
@@ -39813,6 +42938,12 @@ Object.freeze({
|
|
|
39813
42938
|
addonId: null,
|
|
39814
42939
|
access: "view"
|
|
39815
42940
|
},
|
|
42941
|
+
"nativeObjectDetection.getStatus": {
|
|
42942
|
+
capName: "native-object-detection",
|
|
42943
|
+
capScope: "device",
|
|
42944
|
+
addonId: null,
|
|
42945
|
+
access: "view"
|
|
42946
|
+
},
|
|
39816
42947
|
"nativeObjectDetection.setEnabled": {
|
|
39817
42948
|
capName: "native-object-detection",
|
|
39818
42949
|
capScope: "device",
|
|
@@ -39831,6 +42962,12 @@ Object.freeze({
|
|
|
39831
42962
|
addonId: null,
|
|
39832
42963
|
access: "view"
|
|
39833
42964
|
},
|
|
42965
|
+
"navigation.getStatus": {
|
|
42966
|
+
capName: "navigation",
|
|
42967
|
+
capScope: "device",
|
|
42968
|
+
addonId: null,
|
|
42969
|
+
access: "view"
|
|
42970
|
+
},
|
|
39834
42971
|
"navigation.goToPoint": {
|
|
39835
42972
|
capName: "navigation",
|
|
39836
42973
|
capScope: "device",
|
|
@@ -39915,6 +43052,12 @@ Object.freeze({
|
|
|
39915
43052
|
addonId: null,
|
|
39916
43053
|
access: "create"
|
|
39917
43054
|
},
|
|
43055
|
+
"networkLink.getStatus": {
|
|
43056
|
+
capName: "network-link",
|
|
43057
|
+
capScope: "device",
|
|
43058
|
+
addonId: null,
|
|
43059
|
+
access: "view"
|
|
43060
|
+
},
|
|
39918
43061
|
"networkQuality.getAllStats": {
|
|
39919
43062
|
capName: "network-quality",
|
|
39920
43063
|
capScope: "system",
|
|
@@ -40173,18 +43316,36 @@ Object.freeze({
|
|
|
40173
43316
|
addonId: null,
|
|
40174
43317
|
access: "create"
|
|
40175
43318
|
},
|
|
43319
|
+
"notifier.getStatus": {
|
|
43320
|
+
capName: "notifier",
|
|
43321
|
+
capScope: "device",
|
|
43322
|
+
addonId: null,
|
|
43323
|
+
access: "view"
|
|
43324
|
+
},
|
|
40176
43325
|
"notifier.send": {
|
|
40177
43326
|
capName: "notifier",
|
|
40178
43327
|
capScope: "device",
|
|
40179
43328
|
addonId: null,
|
|
40180
43329
|
access: "create"
|
|
40181
43330
|
},
|
|
43331
|
+
"numericSensor.getStatus": {
|
|
43332
|
+
capName: "numeric-sensor",
|
|
43333
|
+
capScope: "device",
|
|
43334
|
+
addonId: null,
|
|
43335
|
+
access: "view"
|
|
43336
|
+
},
|
|
40182
43337
|
"oauthIntegration.getDescriptor": {
|
|
40183
43338
|
capName: "oauth-integration",
|
|
40184
43339
|
capScope: "system",
|
|
40185
43340
|
addonId: null,
|
|
40186
43341
|
access: "view"
|
|
40187
43342
|
},
|
|
43343
|
+
"osd.getStatus": {
|
|
43344
|
+
capName: "osd",
|
|
43345
|
+
capScope: "device",
|
|
43346
|
+
addonId: null,
|
|
43347
|
+
access: "view"
|
|
43348
|
+
},
|
|
40188
43349
|
"osd.setOverlay": {
|
|
40189
43350
|
capName: "osd",
|
|
40190
43351
|
capScope: "device",
|
|
@@ -40257,6 +43418,12 @@ Object.freeze({
|
|
|
40257
43418
|
addonId: null,
|
|
40258
43419
|
access: "create"
|
|
40259
43420
|
},
|
|
43421
|
+
"petFeeder.getStatus": {
|
|
43422
|
+
capName: "pet-feeder",
|
|
43423
|
+
capScope: "device",
|
|
43424
|
+
addonId: null,
|
|
43425
|
+
access: "view"
|
|
43426
|
+
},
|
|
40260
43427
|
"petFeeder.markFoodReplenished": {
|
|
40261
43428
|
capName: "pet-feeder",
|
|
40262
43429
|
capScope: "device",
|
|
@@ -40299,6 +43466,12 @@ Object.freeze({
|
|
|
40299
43466
|
addonId: null,
|
|
40300
43467
|
access: "create"
|
|
40301
43468
|
},
|
|
43469
|
+
"pipelineAnalytics.applyDeviceSettingsPatch": {
|
|
43470
|
+
capName: "pipeline-analytics",
|
|
43471
|
+
capScope: "device",
|
|
43472
|
+
addonId: null,
|
|
43473
|
+
access: "create"
|
|
43474
|
+
},
|
|
40302
43475
|
"pipelineAnalytics.cancelRelocateMedia": {
|
|
40303
43476
|
capName: "pipeline-analytics",
|
|
40304
43477
|
capScope: "device",
|
|
@@ -40365,6 +43538,18 @@ Object.freeze({
|
|
|
40365
43538
|
addonId: null,
|
|
40366
43539
|
access: "view"
|
|
40367
43540
|
},
|
|
43541
|
+
"pipelineAnalytics.getDeviceLiveContribution": {
|
|
43542
|
+
capName: "pipeline-analytics",
|
|
43543
|
+
capScope: "device",
|
|
43544
|
+
addonId: null,
|
|
43545
|
+
access: "view"
|
|
43546
|
+
},
|
|
43547
|
+
"pipelineAnalytics.getDeviceSettingsContribution": {
|
|
43548
|
+
capName: "pipeline-analytics",
|
|
43549
|
+
capScope: "device",
|
|
43550
|
+
addonId: null,
|
|
43551
|
+
access: "view"
|
|
43552
|
+
},
|
|
40368
43553
|
"pipelineAnalytics.getEventDensity": {
|
|
40369
43554
|
capName: "pipeline-analytics",
|
|
40370
43555
|
capScope: "device",
|
|
@@ -40575,6 +43760,12 @@ Object.freeze({
|
|
|
40575
43760
|
addonId: null,
|
|
40576
43761
|
access: "view"
|
|
40577
43762
|
},
|
|
43763
|
+
"pipelineAnalytics.ownersWithMedia": {
|
|
43764
|
+
capName: "pipeline-analytics",
|
|
43765
|
+
capScope: "device",
|
|
43766
|
+
addonId: null,
|
|
43767
|
+
access: "view"
|
|
43768
|
+
},
|
|
40578
43769
|
"pipelineAnalytics.pauseForStorageMigration": {
|
|
40579
43770
|
capName: "pipeline-analytics",
|
|
40580
43771
|
capScope: "device",
|
|
@@ -40929,6 +44120,12 @@ Object.freeze({
|
|
|
40929
44120
|
addonId: null,
|
|
40930
44121
|
access: "view"
|
|
40931
44122
|
},
|
|
44123
|
+
"pipelineOrchestrator.applyDeviceSettingsPatch": {
|
|
44124
|
+
capName: "pipeline-orchestrator",
|
|
44125
|
+
capScope: "system",
|
|
44126
|
+
addonId: null,
|
|
44127
|
+
access: "create"
|
|
44128
|
+
},
|
|
40932
44129
|
"pipelineOrchestrator.assignAudio": {
|
|
40933
44130
|
capName: "pipeline-orchestrator",
|
|
40934
44131
|
capScope: "system",
|
|
@@ -41019,6 +44216,18 @@ Object.freeze({
|
|
|
41019
44216
|
addonId: null,
|
|
41020
44217
|
access: "view"
|
|
41021
44218
|
},
|
|
44219
|
+
"pipelineOrchestrator.getDeviceLiveContribution": {
|
|
44220
|
+
capName: "pipeline-orchestrator",
|
|
44221
|
+
capScope: "system",
|
|
44222
|
+
addonId: null,
|
|
44223
|
+
access: "view"
|
|
44224
|
+
},
|
|
44225
|
+
"pipelineOrchestrator.getDeviceSettingsContribution": {
|
|
44226
|
+
capName: "pipeline-orchestrator",
|
|
44227
|
+
capScope: "system",
|
|
44228
|
+
addonId: null,
|
|
44229
|
+
access: "view"
|
|
44230
|
+
},
|
|
41022
44231
|
"pipelineOrchestrator.getGlobalMetrics": {
|
|
41023
44232
|
capName: "pipeline-orchestrator",
|
|
41024
44233
|
capScope: "system",
|
|
@@ -41427,12 +44636,36 @@ Object.freeze({
|
|
|
41427
44636
|
addonId: null,
|
|
41428
44637
|
access: "view"
|
|
41429
44638
|
},
|
|
44639
|
+
"powerMeter.getStatus": {
|
|
44640
|
+
capName: "power-meter",
|
|
44641
|
+
capScope: "device",
|
|
44642
|
+
addonId: null,
|
|
44643
|
+
access: "view"
|
|
44644
|
+
},
|
|
44645
|
+
"presence.getStatus": {
|
|
44646
|
+
capName: "presence",
|
|
44647
|
+
capScope: "device",
|
|
44648
|
+
addonId: null,
|
|
44649
|
+
access: "view"
|
|
44650
|
+
},
|
|
44651
|
+
"pressureSensor.getStatus": {
|
|
44652
|
+
capName: "pressure-sensor",
|
|
44653
|
+
capScope: "device",
|
|
44654
|
+
addonId: null,
|
|
44655
|
+
access: "view"
|
|
44656
|
+
},
|
|
41430
44657
|
"privacyMask.getOptions": {
|
|
41431
44658
|
capName: "privacy-mask",
|
|
41432
44659
|
capScope: "device",
|
|
41433
44660
|
addonId: null,
|
|
41434
44661
|
access: "view"
|
|
41435
44662
|
},
|
|
44663
|
+
"privacyMask.getStatus": {
|
|
44664
|
+
capName: "privacy-mask",
|
|
44665
|
+
capScope: "device",
|
|
44666
|
+
addonId: null,
|
|
44667
|
+
access: "view"
|
|
44668
|
+
},
|
|
41436
44669
|
"privacyMask.setAudioEnabled": {
|
|
41437
44670
|
capName: "privacy-mask",
|
|
41438
44671
|
capScope: "device",
|
|
@@ -41493,6 +44726,12 @@ Object.freeze({
|
|
|
41493
44726
|
addonId: null,
|
|
41494
44727
|
access: "view"
|
|
41495
44728
|
},
|
|
44729
|
+
"ptz.getStatus": {
|
|
44730
|
+
capName: "ptz",
|
|
44731
|
+
capScope: "device",
|
|
44732
|
+
addonId: null,
|
|
44733
|
+
access: "view"
|
|
44734
|
+
},
|
|
41496
44735
|
"ptz.goHome": {
|
|
41497
44736
|
capName: "ptz",
|
|
41498
44737
|
capScope: "device",
|
|
@@ -41571,6 +44810,12 @@ Object.freeze({
|
|
|
41571
44810
|
addonId: null,
|
|
41572
44811
|
access: "create"
|
|
41573
44812
|
},
|
|
44813
|
+
"recording.applyDeviceSettingsPatch": {
|
|
44814
|
+
capName: "recording",
|
|
44815
|
+
capScope: "system",
|
|
44816
|
+
addonId: null,
|
|
44817
|
+
access: "create"
|
|
44818
|
+
},
|
|
41574
44819
|
"recording.cancelRelocateJob": {
|
|
41575
44820
|
capName: "recording",
|
|
41576
44821
|
capScope: "system",
|
|
@@ -41619,6 +44864,18 @@ Object.freeze({
|
|
|
41619
44864
|
addonId: null,
|
|
41620
44865
|
access: "view"
|
|
41621
44866
|
},
|
|
44867
|
+
"recording.getDeviceLiveContribution": {
|
|
44868
|
+
capName: "recording",
|
|
44869
|
+
capScope: "system",
|
|
44870
|
+
addonId: null,
|
|
44871
|
+
access: "view"
|
|
44872
|
+
},
|
|
44873
|
+
"recording.getDeviceSettingsContribution": {
|
|
44874
|
+
capName: "recording",
|
|
44875
|
+
capScope: "system",
|
|
44876
|
+
addonId: null,
|
|
44877
|
+
access: "view"
|
|
44878
|
+
},
|
|
41622
44879
|
"recording.getPlacement": {
|
|
41623
44880
|
capName: "recording",
|
|
41624
44881
|
capScope: "system",
|
|
@@ -41637,6 +44894,12 @@ Object.freeze({
|
|
|
41637
44894
|
addonId: null,
|
|
41638
44895
|
access: "view"
|
|
41639
44896
|
},
|
|
44897
|
+
"recording.getStatus": {
|
|
44898
|
+
capName: "recording",
|
|
44899
|
+
capScope: "system",
|
|
44900
|
+
addonId: null,
|
|
44901
|
+
access: "view"
|
|
44902
|
+
},
|
|
41640
44903
|
"recording.getStorageMigrationMoveStatus": {
|
|
41641
44904
|
capName: "recording",
|
|
41642
44905
|
capScope: "system",
|
|
@@ -41811,6 +45074,24 @@ Object.freeze({
|
|
|
41811
45074
|
addonId: null,
|
|
41812
45075
|
access: "view"
|
|
41813
45076
|
},
|
|
45077
|
+
"recordingOnboard.getOptions": {
|
|
45078
|
+
capName: "recording-onboard",
|
|
45079
|
+
capScope: "device",
|
|
45080
|
+
addonId: null,
|
|
45081
|
+
access: "view"
|
|
45082
|
+
},
|
|
45083
|
+
"recordingOnboard.getStatus": {
|
|
45084
|
+
capName: "recording-onboard",
|
|
45085
|
+
capScope: "device",
|
|
45086
|
+
addonId: null,
|
|
45087
|
+
access: "view"
|
|
45088
|
+
},
|
|
45089
|
+
"recordingOnboard.setSettings": {
|
|
45090
|
+
capName: "recording-onboard",
|
|
45091
|
+
capScope: "device",
|
|
45092
|
+
addonId: null,
|
|
45093
|
+
access: "create"
|
|
45094
|
+
},
|
|
41814
45095
|
"recordingSignal.getStatus": {
|
|
41815
45096
|
capName: "recording-signal",
|
|
41816
45097
|
capScope: "device",
|
|
@@ -41841,6 +45122,12 @@ Object.freeze({
|
|
|
41841
45122
|
addonId: null,
|
|
41842
45123
|
access: "delete"
|
|
41843
45124
|
},
|
|
45125
|
+
"sceneMonitor.getStatus": {
|
|
45126
|
+
capName: "scene-monitor",
|
|
45127
|
+
capScope: "device",
|
|
45128
|
+
addonId: null,
|
|
45129
|
+
access: "view"
|
|
45130
|
+
},
|
|
41844
45131
|
"sceneMonitor.listScenes": {
|
|
41845
45132
|
capName: "scene-monitor",
|
|
41846
45133
|
capScope: "device",
|
|
@@ -41871,6 +45158,12 @@ Object.freeze({
|
|
|
41871
45158
|
addonId: null,
|
|
41872
45159
|
access: "create"
|
|
41873
45160
|
},
|
|
45161
|
+
"scriptRunner.getStatus": {
|
|
45162
|
+
capName: "script-runner",
|
|
45163
|
+
capScope: "device",
|
|
45164
|
+
addonId: null,
|
|
45165
|
+
access: "view"
|
|
45166
|
+
},
|
|
41874
45167
|
"scriptRunner.run": {
|
|
41875
45168
|
capName: "script-runner",
|
|
41876
45169
|
capScope: "device",
|
|
@@ -42003,6 +45296,12 @@ Object.freeze({
|
|
|
42003
45296
|
addonId: null,
|
|
42004
45297
|
access: "create"
|
|
42005
45298
|
},
|
|
45299
|
+
"smoke.getStatus": {
|
|
45300
|
+
capName: "smoke",
|
|
45301
|
+
capScope: "device",
|
|
45302
|
+
addonId: null,
|
|
45303
|
+
access: "view"
|
|
45304
|
+
},
|
|
42006
45305
|
"smtpProvider.getStatus": {
|
|
42007
45306
|
capName: "smtp-provider",
|
|
42008
45307
|
capScope: "system",
|
|
@@ -42021,12 +45320,30 @@ Object.freeze({
|
|
|
42021
45320
|
addonId: null,
|
|
42022
45321
|
access: "view"
|
|
42023
45322
|
},
|
|
45323
|
+
"snapshot.applyDeviceSettingsPatch": {
|
|
45324
|
+
capName: "snapshot",
|
|
45325
|
+
capScope: "device",
|
|
45326
|
+
addonId: null,
|
|
45327
|
+
access: "create"
|
|
45328
|
+
},
|
|
42024
45329
|
"snapshot.getDebugState": {
|
|
42025
45330
|
capName: "snapshot",
|
|
42026
45331
|
capScope: "device",
|
|
42027
45332
|
addonId: null,
|
|
42028
45333
|
access: "view"
|
|
42029
45334
|
},
|
|
45335
|
+
"snapshot.getDeviceLiveContribution": {
|
|
45336
|
+
capName: "snapshot",
|
|
45337
|
+
capScope: "device",
|
|
45338
|
+
addonId: null,
|
|
45339
|
+
access: "view"
|
|
45340
|
+
},
|
|
45341
|
+
"snapshot.getDeviceSettingsContribution": {
|
|
45342
|
+
capName: "snapshot",
|
|
45343
|
+
capScope: "device",
|
|
45344
|
+
addonId: null,
|
|
45345
|
+
access: "view"
|
|
45346
|
+
},
|
|
42030
45347
|
"snapshot.getSnapshot": {
|
|
42031
45348
|
capName: "snapshot",
|
|
42032
45349
|
capScope: "device",
|
|
@@ -42045,6 +45362,12 @@ Object.freeze({
|
|
|
42045
45362
|
addonId: null,
|
|
42046
45363
|
access: "view"
|
|
42047
45364
|
},
|
|
45365
|
+
"snapshot.getStatus": {
|
|
45366
|
+
capName: "snapshot",
|
|
45367
|
+
capScope: "device",
|
|
45368
|
+
addonId: null,
|
|
45369
|
+
access: "view"
|
|
45370
|
+
},
|
|
42048
45371
|
"snapshot.invalidateCache": {
|
|
42049
45372
|
capName: "snapshot",
|
|
42050
45373
|
capScope: "device",
|
|
@@ -42381,6 +45704,12 @@ Object.freeze({
|
|
|
42381
45704
|
addonId: null,
|
|
42382
45705
|
access: "create"
|
|
42383
45706
|
},
|
|
45707
|
+
"streamBroker.applyDeviceSettingsPatch": {
|
|
45708
|
+
capName: "stream-broker",
|
|
45709
|
+
capScope: "system",
|
|
45710
|
+
addonId: null,
|
|
45711
|
+
access: "create"
|
|
45712
|
+
},
|
|
42384
45713
|
"streamBroker.assignProfile": {
|
|
42385
45714
|
capName: "stream-broker",
|
|
42386
45715
|
capScope: "system",
|
|
@@ -42417,6 +45746,18 @@ Object.freeze({
|
|
|
42417
45746
|
addonId: null,
|
|
42418
45747
|
access: "view"
|
|
42419
45748
|
},
|
|
45749
|
+
"streamBroker.getDeviceLiveContribution": {
|
|
45750
|
+
capName: "stream-broker",
|
|
45751
|
+
capScope: "system",
|
|
45752
|
+
addonId: null,
|
|
45753
|
+
access: "view"
|
|
45754
|
+
},
|
|
45755
|
+
"streamBroker.getDeviceSettingsContribution": {
|
|
45756
|
+
capName: "stream-broker",
|
|
45757
|
+
capScope: "system",
|
|
45758
|
+
addonId: null,
|
|
45759
|
+
access: "view"
|
|
45760
|
+
},
|
|
42420
45761
|
"streamBroker.getPreBufferInfo": {
|
|
42421
45762
|
capName: "stream-broker",
|
|
42422
45763
|
capScope: "system",
|
|
@@ -42615,6 +45956,12 @@ Object.freeze({
|
|
|
42615
45956
|
addonId: null,
|
|
42616
45957
|
access: "view"
|
|
42617
45958
|
},
|
|
45959
|
+
"streamParams.getStatus": {
|
|
45960
|
+
capName: "stream-params",
|
|
45961
|
+
capScope: "device",
|
|
45962
|
+
addonId: null,
|
|
45963
|
+
access: "view"
|
|
45964
|
+
},
|
|
42618
45965
|
"streamParams.setProfile": {
|
|
42619
45966
|
capName: "stream-params",
|
|
42620
45967
|
capScope: "device",
|
|
@@ -42627,6 +45974,12 @@ Object.freeze({
|
|
|
42627
45974
|
addonId: null,
|
|
42628
45975
|
access: "view"
|
|
42629
45976
|
},
|
|
45977
|
+
"switch.getStatus": {
|
|
45978
|
+
capName: "switch",
|
|
45979
|
+
capScope: "device",
|
|
45980
|
+
addonId: null,
|
|
45981
|
+
access: "view"
|
|
45982
|
+
},
|
|
42630
45983
|
"switch.setState": {
|
|
42631
45984
|
capName: "switch",
|
|
42632
45985
|
capScope: "device",
|
|
@@ -42723,6 +46076,18 @@ Object.freeze({
|
|
|
42723
46076
|
addonId: null,
|
|
42724
46077
|
access: "create"
|
|
42725
46078
|
},
|
|
46079
|
+
"tamper.getStatus": {
|
|
46080
|
+
capName: "tamper",
|
|
46081
|
+
capScope: "device",
|
|
46082
|
+
addonId: null,
|
|
46083
|
+
access: "view"
|
|
46084
|
+
},
|
|
46085
|
+
"temperatureSensor.getStatus": {
|
|
46086
|
+
capName: "temperature-sensor",
|
|
46087
|
+
capScope: "device",
|
|
46088
|
+
addonId: null,
|
|
46089
|
+
access: "view"
|
|
46090
|
+
},
|
|
42726
46091
|
"terminalSession.adoptLegacyMonitor": {
|
|
42727
46092
|
capName: "terminal-session",
|
|
42728
46093
|
capScope: "system",
|
|
@@ -42819,6 +46184,12 @@ Object.freeze({
|
|
|
42819
46184
|
addonId: null,
|
|
42820
46185
|
access: "view"
|
|
42821
46186
|
},
|
|
46187
|
+
"update.getStatus": {
|
|
46188
|
+
capName: "update",
|
|
46189
|
+
capScope: "device",
|
|
46190
|
+
addonId: null,
|
|
46191
|
+
access: "view"
|
|
46192
|
+
},
|
|
42822
46193
|
"update.installUpdate": {
|
|
42823
46194
|
capName: "update",
|
|
42824
46195
|
capScope: "device",
|
|
@@ -43041,6 +46412,12 @@ Object.freeze({
|
|
|
43041
46412
|
addonId: null,
|
|
43042
46413
|
access: "create"
|
|
43043
46414
|
},
|
|
46415
|
+
"vacuumControl.getStatus": {
|
|
46416
|
+
capName: "vacuum-control",
|
|
46417
|
+
capScope: "device",
|
|
46418
|
+
addonId: null,
|
|
46419
|
+
access: "view"
|
|
46420
|
+
},
|
|
43044
46421
|
"vacuumControl.locate": {
|
|
43045
46422
|
capName: "vacuum-control",
|
|
43046
46423
|
capScope: "device",
|
|
@@ -43083,6 +46460,12 @@ Object.freeze({
|
|
|
43083
46460
|
addonId: null,
|
|
43084
46461
|
access: "create"
|
|
43085
46462
|
},
|
|
46463
|
+
"valve.getStatus": {
|
|
46464
|
+
capName: "valve",
|
|
46465
|
+
capScope: "device",
|
|
46466
|
+
addonId: null,
|
|
46467
|
+
access: "view"
|
|
46468
|
+
},
|
|
43086
46469
|
"valve.open": {
|
|
43087
46470
|
capName: "valve",
|
|
43088
46471
|
capScope: "device",
|
|
@@ -43155,6 +46538,18 @@ Object.freeze({
|
|
|
43155
46538
|
addonId: null,
|
|
43156
46539
|
access: "create"
|
|
43157
46540
|
},
|
|
46541
|
+
"vibration.getStatus": {
|
|
46542
|
+
capName: "vibration",
|
|
46543
|
+
capScope: "device",
|
|
46544
|
+
addonId: null,
|
|
46545
|
+
access: "view"
|
|
46546
|
+
},
|
|
46547
|
+
"videoclips.dialClipStream": {
|
|
46548
|
+
capName: "videoclips",
|
|
46549
|
+
capScope: "device",
|
|
46550
|
+
addonId: null,
|
|
46551
|
+
access: "view"
|
|
46552
|
+
},
|
|
43158
46553
|
"videoclips.getClipPlayback": {
|
|
43159
46554
|
capName: "videoclips",
|
|
43160
46555
|
capScope: "device",
|
|
@@ -43167,6 +46562,18 @@ Object.freeze({
|
|
|
43167
46562
|
addonId: null,
|
|
43168
46563
|
access: "view"
|
|
43169
46564
|
},
|
|
46565
|
+
"videoclips.listSources": {
|
|
46566
|
+
capName: "videoclips",
|
|
46567
|
+
capScope: "device",
|
|
46568
|
+
addonId: null,
|
|
46569
|
+
access: "view"
|
|
46570
|
+
},
|
|
46571
|
+
"videoclips.readClipBytes": {
|
|
46572
|
+
capName: "videoclips",
|
|
46573
|
+
capScope: "device",
|
|
46574
|
+
addonId: null,
|
|
46575
|
+
access: "view"
|
|
46576
|
+
},
|
|
43170
46577
|
"viewerUi.getStaticDir": {
|
|
43171
46578
|
capName: "viewer-ui",
|
|
43172
46579
|
capScope: "system",
|
|
@@ -43179,6 +46586,12 @@ Object.freeze({
|
|
|
43179
46586
|
addonId: null,
|
|
43180
46587
|
access: "view"
|
|
43181
46588
|
},
|
|
46589
|
+
"waterHeater.getStatus": {
|
|
46590
|
+
capName: "water-heater",
|
|
46591
|
+
capScope: "device",
|
|
46592
|
+
addonId: null,
|
|
46593
|
+
access: "view"
|
|
46594
|
+
},
|
|
43182
46595
|
"waterHeater.setAway": {
|
|
43183
46596
|
capName: "water-heater",
|
|
43184
46597
|
capScope: "device",
|
|
@@ -43197,6 +46610,12 @@ Object.freeze({
|
|
|
43197
46610
|
addonId: null,
|
|
43198
46611
|
access: "create"
|
|
43199
46612
|
},
|
|
46613
|
+
"weather.getStatus": {
|
|
46614
|
+
capName: "weather",
|
|
46615
|
+
capScope: "device",
|
|
46616
|
+
addonId: null,
|
|
46617
|
+
access: "view"
|
|
46618
|
+
},
|
|
43200
46619
|
"webrtcSession.addIceCandidate": {
|
|
43201
46620
|
capName: "webrtc-session",
|
|
43202
46621
|
capScope: "device",
|
|
@@ -43221,6 +46640,12 @@ Object.freeze({
|
|
|
43221
46640
|
addonId: null,
|
|
43222
46641
|
access: "view"
|
|
43223
46642
|
},
|
|
46643
|
+
"webrtcSession.getSessionDebug": {
|
|
46644
|
+
capName: "webrtc-session",
|
|
46645
|
+
capScope: "device",
|
|
46646
|
+
addonId: null,
|
|
46647
|
+
access: "view"
|
|
46648
|
+
},
|
|
43224
46649
|
"webrtcSession.getSessionState": {
|
|
43225
46650
|
capName: "webrtc-session",
|
|
43226
46651
|
capScope: "device",
|
|
@@ -43553,6 +46978,11 @@ Object.freeze({
|
|
|
43553
46978
|
form: "single",
|
|
43554
46979
|
optional: true
|
|
43555
46980
|
}],
|
|
46981
|
+
"deviceAdminLink.getAdminLink": [{
|
|
46982
|
+
name: "deviceId",
|
|
46983
|
+
form: "single",
|
|
46984
|
+
optional: false
|
|
46985
|
+
}],
|
|
43556
46986
|
"deviceAdoption.release": [{
|
|
43557
46987
|
name: "camDeviceId",
|
|
43558
46988
|
form: "single",
|
|
@@ -44463,6 +47893,11 @@ Object.freeze({
|
|
|
44463
47893
|
form: "single",
|
|
44464
47894
|
optional: false
|
|
44465
47895
|
}],
|
|
47896
|
+
"pipelineAnalytics.ownersWithMedia": [{
|
|
47897
|
+
name: "deviceId",
|
|
47898
|
+
form: "single",
|
|
47899
|
+
optional: false
|
|
47900
|
+
}],
|
|
44466
47901
|
"pipelineAnalytics.proposeRetrainAnnotations": [{
|
|
44467
47902
|
name: "deviceId",
|
|
44468
47903
|
form: "single",
|
|
@@ -44933,6 +48368,16 @@ Object.freeze({
|
|
|
44933
48368
|
form: "single",
|
|
44934
48369
|
optional: true
|
|
44935
48370
|
}],
|
|
48371
|
+
"recordingOnboard.getOptions": [{
|
|
48372
|
+
name: "deviceId",
|
|
48373
|
+
form: "single",
|
|
48374
|
+
optional: false
|
|
48375
|
+
}],
|
|
48376
|
+
"recordingOnboard.setSettings": [{
|
|
48377
|
+
name: "deviceId",
|
|
48378
|
+
form: "single",
|
|
48379
|
+
optional: false
|
|
48380
|
+
}],
|
|
44936
48381
|
"recordingSignal.getStatus": [{
|
|
44937
48382
|
name: "deviceId",
|
|
44938
48383
|
form: "single",
|
|
@@ -45159,6 +48604,11 @@ Object.freeze({
|
|
|
45159
48604
|
form: "single",
|
|
45160
48605
|
optional: false
|
|
45161
48606
|
}],
|
|
48607
|
+
"videoclips.dialClipStream": [{
|
|
48608
|
+
name: "deviceId",
|
|
48609
|
+
form: "single",
|
|
48610
|
+
optional: false
|
|
48611
|
+
}],
|
|
45162
48612
|
"videoclips.getClipPlayback": [{
|
|
45163
48613
|
name: "deviceId",
|
|
45164
48614
|
form: "single",
|
|
@@ -45169,6 +48619,16 @@ Object.freeze({
|
|
|
45169
48619
|
form: "single",
|
|
45170
48620
|
optional: false
|
|
45171
48621
|
}],
|
|
48622
|
+
"videoclips.listSources": [{
|
|
48623
|
+
name: "deviceId",
|
|
48624
|
+
form: "single",
|
|
48625
|
+
optional: false
|
|
48626
|
+
}],
|
|
48627
|
+
"videoclips.readClipBytes": [{
|
|
48628
|
+
name: "deviceId",
|
|
48629
|
+
form: "single",
|
|
48630
|
+
optional: false
|
|
48631
|
+
}],
|
|
45172
48632
|
"waterHeater.setAway": [{
|
|
45173
48633
|
name: "deviceId",
|
|
45174
48634
|
form: "single",
|
|
@@ -45204,6 +48664,11 @@ Object.freeze({
|
|
|
45204
48664
|
form: "single",
|
|
45205
48665
|
optional: false
|
|
45206
48666
|
}],
|
|
48667
|
+
"webrtcSession.getSessionDebug": [{
|
|
48668
|
+
name: "deviceId",
|
|
48669
|
+
form: "single",
|
|
48670
|
+
optional: false
|
|
48671
|
+
}],
|
|
45207
48672
|
"webrtcSession.getSessionState": [{
|
|
45208
48673
|
name: "deviceId",
|
|
45209
48674
|
form: "single",
|
|
@@ -46377,11 +49842,49 @@ var RademacherHubDevice = class extends BaseDevice {
|
|
|
46377
49842
|
super(ctx, rademacherHubSchema, { type: DeviceType.Hub });
|
|
46378
49843
|
this.online = false;
|
|
46379
49844
|
this.registerDeviceDiscovery();
|
|
49845
|
+
this.registerAdminLinkCap();
|
|
46380
49846
|
this.updateSourceInfo({
|
|
46381
49847
|
id: this.stableId,
|
|
46382
49848
|
system: "rademacher"
|
|
46383
49849
|
});
|
|
46384
49850
|
}
|
|
49851
|
+
/**
|
|
49852
|
+
* device-admin-link — the hub's own management page.
|
|
49853
|
+
*
|
|
49854
|
+
* The HomePilot serves its web UI on plain HTTP at the hub's own address.
|
|
49855
|
+
* `basePath` is the operator's API base-path override ("" for v1, "/hp" for
|
|
49856
|
+
* v2) and is carried through, because a hub reached under a prefix serves its
|
|
49857
|
+
* page under the same prefix.
|
|
49858
|
+
*
|
|
49859
|
+
* Only the hub gets the cap. Its children are radio actuators with no web
|
|
49860
|
+
* server anywhere, and the address they would borrow lives on the hub, which
|
|
49861
|
+
* is where the operator edits it.
|
|
49862
|
+
*/
|
|
49863
|
+
registerAdminLinkCap() {
|
|
49864
|
+
this.ctx.registerNativeCap(deviceAdminLinkCapability, { getAdminLink: async ({ deviceId }) => {
|
|
49865
|
+
if (deviceId !== this.id) throw new Error(`RademacherHubDevice: deviceId mismatch, expected ${this.id}, got ${deviceId}`);
|
|
49866
|
+
const connection = this.config.values.connection;
|
|
49867
|
+
const built = buildDeviceAdminUrl({
|
|
49868
|
+
host: connection.host,
|
|
49869
|
+
port: null,
|
|
49870
|
+
scheme: "http",
|
|
49871
|
+
path: connection.basePath === "" ? "/" : `${connection.basePath}/`
|
|
49872
|
+
});
|
|
49873
|
+
if (!built.ok) {
|
|
49874
|
+
this.ctx.logger.debug("rademacher admin link unavailable", {
|
|
49875
|
+
tags: { deviceId: this.id },
|
|
49876
|
+
meta: { reason: built.reason }
|
|
49877
|
+
});
|
|
49878
|
+
return null;
|
|
49879
|
+
}
|
|
49880
|
+
return {
|
|
49881
|
+
url: built.url,
|
|
49882
|
+
label: "HomePilot web UI",
|
|
49883
|
+
target: "integration",
|
|
49884
|
+
host: connection.host
|
|
49885
|
+
};
|
|
49886
|
+
} });
|
|
49887
|
+
}
|
|
46385
49888
|
/** Adopt a pre-built manager (created by `onCreateDevice` so the live client
|
|
46386
49889
|
* connects once). Published on the resolver so children pick it up. */
|
|
46387
49890
|
adoptManager(manager) {
|