@camstack/addon-agent-ui 1.2.347 → 1.2.349
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/CHANGELOG.md +12 -0
- package/dist/.bundle-modules.json +7 -7
- package/dist/addon.js +325 -50
- package/dist/assets/{MotionZonesSettings-B7-Fixls.js → MotionZonesSettings-quRpYBiU.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-DQjOSShv.js → PrivacyMaskSettings-Cq9IU0qs.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-CpiLKbGe.js → SceneMonitorEditor-Coy4dm3w.js} +1 -1
- package/dist/assets/{index-D2TZmpoQ.js → index-Bngw8_Yl.js} +16 -16
- package/dist/index.html +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.349] - 2026-11-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Rebuilt against updated dependencies.
|
|
15
|
+
|
|
16
|
+
## [1.2.348] - 2026-11-08
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Rebuilt against updated dependencies.
|
|
21
|
+
|
|
10
22
|
## [1.2.347] - 2026-11-08
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
@@ -367,14 +367,14 @@
|
|
|
367
367
|
"server/server/packages/addon-agent-ui/src/updates-panel.tsx": "ec6b5b3701322bbb68dbe581703409cd373418cf9227ce829ea0945e70e60d54",
|
|
368
368
|
"server/server/packages/addon-agent-ui/src/use-pairing-status.ts": "46193a1e83a64250dfff75d417448d74ba0159465e710861b996e903b03e427a",
|
|
369
369
|
"server/server/packages/sdk/dist/index.js": "89409bfb9632c46ed63ee4882c70b7994c1ff90c67d27a5d4b86b0990c41a131",
|
|
370
|
-
"server/server/packages/types/dist/client.mjs": "
|
|
371
|
-
"server/server/packages/types/dist/composition-
|
|
370
|
+
"server/server/packages/types/dist/client.mjs": "2b0d99cac9848d23d5c34ab56fff1eefda7ba00945c0fb6b86dd168405a21a91",
|
|
371
|
+
"server/server/packages/types/dist/composition-CXCX78IR.mjs": "3c34431544a6b88404f5f7c855c84ebbf2e5736ad300d4d4dfa2417759f30d46",
|
|
372
372
|
"server/server/packages/types/dist/convert-BAzH15HU.mjs": "baa969d99051a3b05b2620e8b8e776a6f5539ac74ee9f9dbee1cc6b6a3a892a0",
|
|
373
|
-
"server/server/packages/types/dist/device-type-
|
|
373
|
+
"server/server/packages/types/dist/device-type-CbDhVwFP.mjs": "94089ac4e225fbd1a8109ff7d5db680acff824c0745d60a1ccf52b4e2717451d",
|
|
374
374
|
"server/server/packages/types/dist/event-category-78tcBemo.mjs": "767f1bb576027d9ee79ec036367001330203d84842c2c2111fa465fa434045e2",
|
|
375
|
-
"server/server/packages/types/dist/index.mjs": "
|
|
375
|
+
"server/server/packages/types/dist/index.mjs": "b2b587eb81d6aa49b93f08908766fa76d6516b23c0110a5b6ba55f33956611d7",
|
|
376
376
|
"server/server/packages/types/dist/node-color.mjs": "942e663074f0a4ed2f8643848ec139eba4b5fd16d0d01c8a23e08a4819a8843b",
|
|
377
|
-
"server/server/packages/types/dist/sleep-
|
|
377
|
+
"server/server/packages/types/dist/sleep-CqvzKIq2.mjs": "1c57cc25803331fab405ae466ef5adfeda60e57c7dcda5fe9083d87ddf3105dc",
|
|
378
378
|
"server/server/packages/ui-library/src/composites/adaptive-downlink.ts": "6e9698e97721461b72a3750f930dd7214d049be865491bfb5a64b87c032b0e22",
|
|
379
379
|
"server/server/packages/ui-library/src/composites/camera-stream-player.tsx": "c04809229b4e091fed09e2bcfe643d6b36cb3a39e294229b49f8a095e4e6ebd3",
|
|
380
380
|
"server/server/packages/ui-library/src/composites/cap-settings/CameraGridLayoutEditor.tsx": "fae93899c4903183e1b78db39e58bd52a3dd4dc2c11ce85d64e6d2a2bab7516d",
|
|
@@ -394,7 +394,7 @@
|
|
|
394
394
|
"server/server/packages/ui-library/src/composites/data-table-pager.tsx": "3ffe4b0adfe333c36b65e8808fdb8c0d1c2162c3e0582b590e094c9fe1a6cc68",
|
|
395
395
|
"server/server/packages/ui-library/src/composites/data-table-pagination.ts": "ed1192495b99ba34a2b621aea4b3016971cd203adff568b31e076b5e6c0e4882",
|
|
396
396
|
"server/server/packages/ui-library/src/composites/data-table-selection.tsx": "a0ede042754de79674a224b7ac74b81e2f27f57ad2d3622811f7938fc736eaa7",
|
|
397
|
-
"server/server/packages/ui-library/src/composites/data-table.tsx": "
|
|
397
|
+
"server/server/packages/ui-library/src/composites/data-table.tsx": "f79e4de10939a561cce752c226c4a6a8fecec0ec8652186c9ec4c7b0a1ba6890",
|
|
398
398
|
"server/server/packages/ui-library/src/composites/detection-colors.ts": "6a1da1e4ed0aafdc0e90377f911e8492a849f831cfc695f0db4a30c56c20c349",
|
|
399
399
|
"server/server/packages/ui-library/src/composites/dev-shell.tsx": "c3c4102dca52080b6eab45141a2fabff11afb9fdf8919516dbf7f2b8d2c17a61",
|
|
400
400
|
"server/server/packages/ui-library/src/composites/device-controls/ac-panel.tsx": "19649d3703843779006728547128f7fe4af73d9ba9e0fe4620a202895796c6e9",
|
|
@@ -486,7 +486,7 @@
|
|
|
486
486
|
"server/server/packages/ui-library/src/contexts/vod-playback.tsx": "c07bc3ecc786182ef2f27d13bec3f520288e0b47367edd7ddf1c2e7b4a2bbaee",
|
|
487
487
|
"server/server/packages/ui-library/src/contexts/widget-registry.tsx": "71a2815c645abe96821a9e9e5668e321187527d569414f5ca197d615856cc17a",
|
|
488
488
|
"server/server/packages/ui-library/src/contexts/zone-editing.tsx": "275f680304ab09dd747fd9e859dc5aa0535df33eb2d33eb669a6de66bdd848d0",
|
|
489
|
-
"server/server/packages/ui-library/src/generated/system-hooks.ts": "
|
|
489
|
+
"server/server/packages/ui-library/src/generated/system-hooks.ts": "a2f919762a833d62521e99931f2e51f80a5efa6288ee50e1133f1169a6972918",
|
|
490
490
|
"server/server/packages/ui-library/src/hooks/turn-server-cache.ts": "fab22f069c3761e0d268449e056d9c499b6ea63b879684a40023735b34bad061",
|
|
491
491
|
"server/server/packages/ui-library/src/hooks/use-device-capability.ts": "b74ede60be8039c45f6c712a407ee838ab208610435870ddabaa8598cb7cd7f3",
|
|
492
492
|
"server/server/packages/ui-library/src/hooks/use-device-detections.ts": "61cc54cf2e7f53864e5aa533c05679e4c204c4004a0296f63ab169b003888611",
|
package/dist/addon.js
CHANGED
|
@@ -803,7 +803,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
803
803
|
return EventCategory;
|
|
804
804
|
}({});
|
|
805
805
|
//#endregion
|
|
806
|
-
//#region ../types/dist/device-type-
|
|
806
|
+
//#region ../types/dist/device-type-CbDhVwFP.mjs
|
|
807
807
|
/**
|
|
808
808
|
import { errMsg } from '@camstack/types'
|
|
809
809
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -972,6 +972,13 @@ var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
|
972
972
|
* (`featuresOfBoundCaps`). Gates the quick-run control in the viewer.
|
|
973
973
|
*/
|
|
974
974
|
DeviceFeature["TimedRun"] = "timed-run";
|
|
975
|
+
/**
|
|
976
|
+
* Device binds a plain `switch` cap. Derived from the bindings
|
|
977
|
+
* (`featuresOfBoundCaps`) for a composed device, whose switch is bound by the
|
|
978
|
+
* composition and not by a driver: an appliance (a heater behind a smart
|
|
979
|
+
* plug) has no control of its own, so a surface reads this as its on/off.
|
|
980
|
+
*/
|
|
981
|
+
DeviceFeature["Switch"] = "switch";
|
|
975
982
|
/** Light supports rgb-triplet color via `color` cap. */
|
|
976
983
|
DeviceFeature["LightColorRgb"] = "light-color-rgb";
|
|
977
984
|
/** Light supports HSV color via `color` cap. */
|
|
@@ -5873,7 +5880,7 @@ var ZodIssueCode = {
|
|
|
5873
5880
|
var ZodFirstPartyTypeKind;
|
|
5874
5881
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5875
5882
|
//#endregion
|
|
5876
|
-
//#region ../types/dist/sleep-
|
|
5883
|
+
//#region ../types/dist/sleep-CqvzKIq2.mjs
|
|
5877
5884
|
/**
|
|
5878
5885
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5879
5886
|
* window to float samples (D455).
|
|
@@ -16300,7 +16307,7 @@ object({
|
|
|
16300
16307
|
params: record(string(), union([string(), number()])).optional()
|
|
16301
16308
|
});
|
|
16302
16309
|
//#endregion
|
|
16303
|
-
//#region ../types/dist/composition-
|
|
16310
|
+
//#region ../types/dist/composition-CXCX78IR.mjs
|
|
16304
16311
|
/**
|
|
16305
16312
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
16306
16313
|
*
|
|
@@ -47840,6 +47847,90 @@ DeviceType.Control, method(object({
|
|
|
47840
47847
|
kind: "mutation",
|
|
47841
47848
|
auth: "protected"
|
|
47842
47849
|
});
|
|
47850
|
+
var IrCodecError = class extends Error {
|
|
47851
|
+
code;
|
|
47852
|
+
constructor(code, message) {
|
|
47853
|
+
super(message);
|
|
47854
|
+
this.name = "IrCodecError";
|
|
47855
|
+
this.code = code;
|
|
47856
|
+
}
|
|
47857
|
+
};
|
|
47858
|
+
/**
|
|
47859
|
+
* A pulse train in signed microseconds: mark (+) first, then space (-), strictly
|
|
47860
|
+
* alternating, no zeros, 2..1024 entries.
|
|
47861
|
+
*/
|
|
47862
|
+
var IrTimingsSchema = array(number().int()).min(2).max(1024).superRefine((timings, ctx) => {
|
|
47863
|
+
timings.forEach((value, index) => {
|
|
47864
|
+
const expectMark = index % 2 === 0;
|
|
47865
|
+
if (value === 0 || value > 0 !== expectMark) ctx.addIssue({
|
|
47866
|
+
code: "custom",
|
|
47867
|
+
message: `timing ${index} must be a non-zero ${expectMark ? "mark (positive)" : "space (negative)"}`,
|
|
47868
|
+
path: [index]
|
|
47869
|
+
});
|
|
47870
|
+
});
|
|
47871
|
+
});
|
|
47872
|
+
/** Per-command transmit options. Every key is optional; a transmitter applies what it can. */
|
|
47873
|
+
var TransmitSettingsSchema = object({
|
|
47874
|
+
carrierHz: number().int().positive().optional(),
|
|
47875
|
+
dutyCyclePercent: number().int().min(1).max(100).optional(),
|
|
47876
|
+
repeat: number().int().min(0).max(20).optional(),
|
|
47877
|
+
repeatDelayMs: number().int().min(0).optional()
|
|
47878
|
+
});
|
|
47879
|
+
/** One Pronto frequency-word unit, in microseconds, per unit of the word. */
|
|
47880
|
+
var PRONTO_CLOCK_US = .241246;
|
|
47881
|
+
var LEARNED_FORM = 0;
|
|
47882
|
+
/**
|
|
47883
|
+
* Decode a learned (0000 form) Pronto hex string. The intro burst is followed by
|
|
47884
|
+
* the repeat burst once; a different form is refused with `pronto-unsupported-form`.
|
|
47885
|
+
*/
|
|
47886
|
+
function decodePronto(code) {
|
|
47887
|
+
const words = code.trim().split(/\s+/).filter((w) => w.length > 0);
|
|
47888
|
+
if (words.length < 4) throw new IrCodecError("pronto-malformed", `Pronto code needs at least 4 words, got ${words.length}`);
|
|
47889
|
+
const values = words.map((w) => /^[0-9a-fA-F]{1,4}$/.test(w) ? parseInt(w, 16) : NaN);
|
|
47890
|
+
if (values.some((v) => Number.isNaN(v))) throw new IrCodecError("pronto-malformed", "Pronto code must be 4-digit hexadecimal words");
|
|
47891
|
+
const [form, freqWord, introCount, repeatCount] = values;
|
|
47892
|
+
if (form === void 0 || freqWord === void 0 || introCount === void 0 || repeatCount === void 0) throw new IrCodecError("pronto-malformed", "Pronto header incomplete");
|
|
47893
|
+
if (form !== LEARNED_FORM) throw new IrCodecError("pronto-unsupported-form", `Pronto form ${words[0] ?? ""} is not supported, only the learned 0000 form`);
|
|
47894
|
+
if (freqWord === 0) throw new IrCodecError("pronto-malformed", "Pronto frequency word is zero");
|
|
47895
|
+
const pairs = introCount + repeatCount;
|
|
47896
|
+
if (pairs === 0 || values.length !== 4 + pairs * 2) throw new IrCodecError("pronto-malformed", `Pronto declares ${pairs} burst pairs but carries ${(values.length - 4) / 2} word pairs`);
|
|
47897
|
+
const unitUs = freqWord * PRONTO_CLOCK_US;
|
|
47898
|
+
const timings = values.slice(4).map((count, index) => {
|
|
47899
|
+
const us = Math.max(1, Math.round(count * unitUs));
|
|
47900
|
+
return index % 2 === 0 ? us : -us;
|
|
47901
|
+
});
|
|
47902
|
+
const checked = IrTimingsSchema.safeParse(timings);
|
|
47903
|
+
if (!checked.success) throw new IrCodecError("invalid-timings", `Pronto decoded to invalid timings: ${checked.error.message}`);
|
|
47904
|
+
return {
|
|
47905
|
+
timings: checked.data,
|
|
47906
|
+
carrierHz: Math.round(1e6 / unitUs)
|
|
47907
|
+
};
|
|
47908
|
+
}
|
|
47909
|
+
/** What a remote command stores: a raw pulse train, or a learned Pronto code. */
|
|
47910
|
+
var IrPayloadSchema = discriminatedUnion("kind", [object({
|
|
47911
|
+
kind: literal("raw"),
|
|
47912
|
+
timings: array(number().int()),
|
|
47913
|
+
settings: TransmitSettingsSchema.optional()
|
|
47914
|
+
}), object({
|
|
47915
|
+
kind: literal("pronto"),
|
|
47916
|
+
code: string()
|
|
47917
|
+
})]);
|
|
47918
|
+
/** Resolve a payload to what `ir-transmitter.send` takes. Throws `IrCodecError`. */
|
|
47919
|
+
function toTimings(payload) {
|
|
47920
|
+
if (payload.kind === "pronto") {
|
|
47921
|
+
const decoded = decodePronto(payload.code);
|
|
47922
|
+
return {
|
|
47923
|
+
timings: decoded.timings,
|
|
47924
|
+
settings: { carrierHz: decoded.carrierHz }
|
|
47925
|
+
};
|
|
47926
|
+
}
|
|
47927
|
+
const checked = IrTimingsSchema.safeParse(payload.timings);
|
|
47928
|
+
if (!checked.success) throw new IrCodecError("invalid-timings", checked.error.message);
|
|
47929
|
+
return {
|
|
47930
|
+
timings: checked.data,
|
|
47931
|
+
settings: payload.settings ?? {}
|
|
47932
|
+
};
|
|
47933
|
+
}
|
|
47843
47934
|
/**
|
|
47844
47935
|
* The role vocabulary of a remote (D963): what a button MEANS, independent of
|
|
47845
47936
|
* how it is wired. A layout template places buttons by role; a device binds a
|
|
@@ -47927,11 +48018,18 @@ var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one o
|
|
|
47927
48018
|
* a voice command or `playMedia` names.
|
|
47928
48019
|
* - `sequence`: a name and explicit steps — bound roles pressed in order
|
|
47929
48020
|
* (an input key, a menu macro such as `source, down, down, ok`).
|
|
48021
|
+
* - `ir`: a name and one infrared code of its own (raw timings or a Pronto
|
|
48022
|
+
* string), sent through a named transmitter (planning#464). It presses no
|
|
48023
|
+
* button: a source the remote's layout has no key for.
|
|
47930
48024
|
*
|
|
47931
48025
|
* The name and the number resolve to the SAME declared source. A number no
|
|
47932
48026
|
* `digits` source declares is not a channel: it is refused, never guessed.
|
|
47933
48027
|
*/
|
|
47934
|
-
var REMOTE_SOURCE_KINDS = [
|
|
48028
|
+
var REMOTE_SOURCE_KINDS = [
|
|
48029
|
+
"digits",
|
|
48030
|
+
"sequence",
|
|
48031
|
+
"ir"
|
|
48032
|
+
];
|
|
47935
48033
|
_enum(REMOTE_SOURCE_KINDS);
|
|
47936
48034
|
var REMOTE_SOURCE_ID_RE = /^[a-z0-9][a-z0-9_-]{0,47}$/;
|
|
47937
48035
|
var CHANNEL_NUMBER_RE = new RegExp(`^[0-9]{1,6}$`);
|
|
@@ -47941,19 +48039,50 @@ var sourceBase = {
|
|
|
47941
48039
|
/** What the operator calls it, and what a voice command or a picker names. */
|
|
47942
48040
|
name: string().trim().min(1).max(60)
|
|
47943
48041
|
};
|
|
47944
|
-
var RemoteSourceSchema = discriminatedUnion("kind", [
|
|
47945
|
-
|
|
47946
|
-
|
|
47947
|
-
|
|
47948
|
-
|
|
47949
|
-
|
|
47950
|
-
|
|
47951
|
-
|
|
47952
|
-
|
|
48042
|
+
var RemoteSourceSchema = discriminatedUnion("kind", [
|
|
48043
|
+
object({
|
|
48044
|
+
...sourceBase,
|
|
48045
|
+
kind: literal("digits"),
|
|
48046
|
+
number: string().regex(CHANNEL_NUMBER_RE, { message: "up to 6 digits" })
|
|
48047
|
+
}),
|
|
48048
|
+
object({
|
|
48049
|
+
...sourceBase,
|
|
48050
|
+
kind: literal("sequence"),
|
|
48051
|
+
steps: array(RemoteRoleSchema).min(1).max(16)
|
|
48052
|
+
}),
|
|
48053
|
+
object({
|
|
48054
|
+
...sourceBase,
|
|
48055
|
+
kind: literal("ir"),
|
|
48056
|
+
/** The `ir-transmitter` device the code is sent through. */
|
|
48057
|
+
transmitterId: number().int().nonnegative(),
|
|
48058
|
+
/** The code itself; whether a transmitter could send it is `irSourceProblem`. */
|
|
48059
|
+
payload: IrPayloadSchema
|
|
48060
|
+
})
|
|
48061
|
+
]);
|
|
47953
48062
|
var REMOTE_STEP_DELAY_MAX_MS = 3e3;
|
|
47954
|
-
/**
|
|
48063
|
+
/**
|
|
48064
|
+
* The roles a source presses, in order. A `digits` number is its digit roles;
|
|
48065
|
+
* an `ir` source presses none — it carries its own code.
|
|
48066
|
+
*/
|
|
47955
48067
|
function remoteSourceSteps(source) {
|
|
47956
|
-
|
|
48068
|
+
switch (source.kind) {
|
|
48069
|
+
case "sequence": return source.steps;
|
|
48070
|
+
case "digits": return Array.from(source.number, (digit) => `digit_${digit}`);
|
|
48071
|
+
case "ir": return [];
|
|
48072
|
+
}
|
|
48073
|
+
}
|
|
48074
|
+
/**
|
|
48075
|
+
* Why an `ir` source's code could not be sent (the codec's own words), or null
|
|
48076
|
+
* when it resolves to a pulse train. The schema refuses on it; the editor shows it.
|
|
48077
|
+
*/
|
|
48078
|
+
function irSourceProblem(source) {
|
|
48079
|
+
try {
|
|
48080
|
+
toTimings(source.payload);
|
|
48081
|
+
return null;
|
|
48082
|
+
} catch (err) {
|
|
48083
|
+
if (err instanceof IrCodecError) return err.message;
|
|
48084
|
+
throw err;
|
|
48085
|
+
}
|
|
47957
48086
|
}
|
|
47958
48087
|
/**
|
|
47959
48088
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
@@ -53470,8 +53599,9 @@ DeviceType.Thermostat;
|
|
|
53470
53599
|
*
|
|
53471
53600
|
* "Heat for 30 minutes", "run the fountain pump for 2 minutes": the device has
|
|
53472
53601
|
* no timer of its own, so the hub keeps one. `runFor` turns the device on and
|
|
53473
|
-
* records a deadline; `cancel`
|
|
53474
|
-
* forgets it; at the deadline
|
|
53602
|
+
* records a deadline; `cancel` switches the device off and forgets the deadline,
|
|
53603
|
+
* and the device being switched off by hand just forgets it; at the deadline
|
|
53604
|
+
* the hub issues the off once.
|
|
53475
53605
|
*
|
|
53476
53606
|
* The deadline is DURABLE: it is a row in the records store, so a hub restart
|
|
53477
53607
|
* neither loses a pending off nor leaves the device running past its time. At
|
|
@@ -53608,6 +53738,56 @@ var StateLookSchema = object({
|
|
|
53608
53738
|
background: _enum(HERO_BACKGROUNDS),
|
|
53609
53739
|
animation: HeroAnimationSchema
|
|
53610
53740
|
});
|
|
53741
|
+
/** The scene as the model carries it. `null` = the device did not say (never a guess). */
|
|
53742
|
+
var HeroSceneSchema = object({
|
|
53743
|
+
id: _enum([
|
|
53744
|
+
"ac",
|
|
53745
|
+
"thermostat",
|
|
53746
|
+
"heater",
|
|
53747
|
+
"fan",
|
|
53748
|
+
"washer",
|
|
53749
|
+
"dryer",
|
|
53750
|
+
"dishwasher",
|
|
53751
|
+
"pet-fountain",
|
|
53752
|
+
"pet-feeder",
|
|
53753
|
+
"vacuum",
|
|
53754
|
+
"lawn-mower",
|
|
53755
|
+
"tv",
|
|
53756
|
+
"speaker",
|
|
53757
|
+
"camera",
|
|
53758
|
+
"light",
|
|
53759
|
+
"cover",
|
|
53760
|
+
"lock",
|
|
53761
|
+
"alarm",
|
|
53762
|
+
"siren",
|
|
53763
|
+
"sensor-sparkline",
|
|
53764
|
+
"contact",
|
|
53765
|
+
"presence",
|
|
53766
|
+
"weather",
|
|
53767
|
+
"plug",
|
|
53768
|
+
"button",
|
|
53769
|
+
"update",
|
|
53770
|
+
"control-dial",
|
|
53771
|
+
"event-stream",
|
|
53772
|
+
"gateway",
|
|
53773
|
+
"generic"
|
|
53774
|
+
]),
|
|
53775
|
+
mode: _enum([
|
|
53776
|
+
"cool",
|
|
53777
|
+
"heat",
|
|
53778
|
+
"dry",
|
|
53779
|
+
"fan",
|
|
53780
|
+
"auto",
|
|
53781
|
+
"playing",
|
|
53782
|
+
"paused",
|
|
53783
|
+
"open",
|
|
53784
|
+
"closed"
|
|
53785
|
+
]).nullable(),
|
|
53786
|
+
/** 0..1: volume, cover position, cycle progress. */
|
|
53787
|
+
level: number().min(0).max(1).nullable()
|
|
53788
|
+
});
|
|
53789
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Heater, DeviceType.Dryer, DeviceType.Gateway;
|
|
53790
|
+
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
53611
53791
|
/** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
|
|
53612
53792
|
var HeroTextSchema = object({
|
|
53613
53793
|
en: string(),
|
|
@@ -53759,6 +53939,32 @@ var HeroStateBackdropSchema = object({
|
|
|
53759
53939
|
token: _enum(HERO_BACKGROUNDS),
|
|
53760
53940
|
animation: _enum(HERO_ANIMATIONS)
|
|
53761
53941
|
});
|
|
53942
|
+
var HeroFactSchema = object({
|
|
53943
|
+
id: _enum([
|
|
53944
|
+
"power",
|
|
53945
|
+
"energy",
|
|
53946
|
+
"battery",
|
|
53947
|
+
"link",
|
|
53948
|
+
"phase",
|
|
53949
|
+
"remaining",
|
|
53950
|
+
"volume",
|
|
53951
|
+
"source",
|
|
53952
|
+
"consumables"
|
|
53953
|
+
]),
|
|
53954
|
+
icon: IconKeySchema.nullable(),
|
|
53955
|
+
text: HeroTextSchema,
|
|
53956
|
+
tone: HeroToneSchema
|
|
53957
|
+
});
|
|
53958
|
+
/** A labelled value beside the scene. */
|
|
53959
|
+
var HeroSideTileSchema = object({
|
|
53960
|
+
label: HeroTextSchema,
|
|
53961
|
+
value: HeroTextSchema,
|
|
53962
|
+
tone: HeroToneSchema.optional()
|
|
53963
|
+
});
|
|
53964
|
+
var HeroSideTilesSchema = object({
|
|
53965
|
+
left: array(HeroSideTileSchema).max(2),
|
|
53966
|
+
right: array(HeroSideTileSchema).max(2)
|
|
53967
|
+
});
|
|
53762
53968
|
var HeroBackgroundSchema = discriminatedUnion("kind", [object({
|
|
53763
53969
|
kind: literal("artwork"),
|
|
53764
53970
|
url: string(),
|
|
@@ -53782,6 +53988,11 @@ var HeroModelSchema = object({
|
|
|
53782
53988
|
stateAssumed: boolean(),
|
|
53783
53989
|
look: StateLookSchema,
|
|
53784
53990
|
background: HeroBackgroundSchema,
|
|
53991
|
+
/** Which animated picture the hero (and the list row) draws; its motion is `look.animation`. */
|
|
53992
|
+
scene: HeroSceneSchema,
|
|
53993
|
+
/** The top line's inline facts, in order; never wraps, so a client may cut the tail. */
|
|
53994
|
+
facts: array(HeroFactSchema),
|
|
53995
|
+
sideTiles: HeroSideTilesSchema,
|
|
53785
53996
|
/** The status line under the main icon. */
|
|
53786
53997
|
title: HeroTextSchema,
|
|
53787
53998
|
badges: array(HeroBadgeSchema),
|
|
@@ -53797,6 +54008,42 @@ var HeroModelSchema = object({
|
|
|
53797
54008
|
provenance: array(HeroProvenanceSchema)
|
|
53798
54009
|
});
|
|
53799
54010
|
/**
|
|
54011
|
+
* The ROW of a device in a list (D1023 P2): four fixed zones, all taken from
|
|
54012
|
+
* the hero model and nothing else.
|
|
54013
|
+
*
|
|
54014
|
+
* 1. mini-scene the same scene id as the hero, drawn 44x44 with the state look;
|
|
54015
|
+
* 2. name + ONE status line the facts joined by " · " by the client;
|
|
54016
|
+
* 3. the type's primary inline control;
|
|
54017
|
+
* 4. a trailing fact (power, else battery, else consumables).
|
|
54018
|
+
*
|
|
54019
|
+
* Selection rules, all taken from the model:
|
|
54020
|
+
* - ONE control is ever shown: the device's own power; else the FIRST control of
|
|
54021
|
+
* the FIRST row that has any (a later row's control is never promoted). When
|
|
54022
|
+
* the model lists that part in `gated`, the row has no inline control: the
|
|
54023
|
+
* projection never falls through to another control the caller could use.
|
|
54024
|
+
* - the status line is the head (what plays, else the state) plus at most
|
|
54025
|
+
* {@link ROW_STATUS_FACTS_MAX} facts, the trailing fact excluded;
|
|
54026
|
+
* - the trailing fact is the first of power, battery, consumables.
|
|
54027
|
+
*
|
|
54028
|
+
* `projectDeviceRow` is pure and computes nothing: it selects from a
|
|
54029
|
+
* {@link HeroModel}. A row that needed a value the hero lacks would mean the
|
|
54030
|
+
* model is missing it, so the fix goes in the model.
|
|
54031
|
+
*/
|
|
54032
|
+
var HeroRowModelSchema = object({
|
|
54033
|
+
deviceId: number().int(),
|
|
54034
|
+
name: string(),
|
|
54035
|
+
type: string(),
|
|
54036
|
+
state: HeroStateSchema,
|
|
54037
|
+
stateAssumed: boolean(),
|
|
54038
|
+
look: StateLookSchema,
|
|
54039
|
+
scene: HeroSceneSchema,
|
|
54040
|
+
/** Joined by " · ": the state (or what is playing), then the facts. */
|
|
54041
|
+
statusLine: array(HeroTextSchema),
|
|
54042
|
+
/** `null` = the type has no inline control, or the caller may not act on it. */
|
|
54043
|
+
primary: HeroControlSchema.nullable(),
|
|
54044
|
+
trailing: HeroFactSchema.nullable()
|
|
54045
|
+
});
|
|
54046
|
+
/**
|
|
53800
54047
|
* hero-layout — what a device's hero says (D1023). Read-only: the FIXED
|
|
53801
54048
|
* layouts live in `@camstack/types` and nothing here stores or edits one.
|
|
53802
54049
|
* `getHeroModel` builds the pure model from the device's bindings and its
|
|
@@ -53812,6 +54059,11 @@ var heroInput = object({
|
|
|
53812
54059
|
*/
|
|
53813
54060
|
canActuate: boolean().optional()
|
|
53814
54061
|
});
|
|
54062
|
+
var rowModelsInput = object({
|
|
54063
|
+
deviceIds: array(number().int().nonnegative()).max(500),
|
|
54064
|
+
/** As `canActuate` of `getHeroModel`; absent = may. */
|
|
54065
|
+
canActuate: boolean().optional()
|
|
54066
|
+
});
|
|
53815
54067
|
var HeroLayoutRowsSchema = object({
|
|
53816
54068
|
type: string(),
|
|
53817
54069
|
rows: array(HeroRowIdSchema)
|
|
@@ -53819,6 +54071,9 @@ var HeroLayoutRowsSchema = object({
|
|
|
53819
54071
|
method(heroInput, HeroModelSchema.nullable(), {
|
|
53820
54072
|
auth: "protected",
|
|
53821
54073
|
role: "viewer"
|
|
54074
|
+
}), method(rowModelsInput, array(HeroRowModelSchema.nullable()), {
|
|
54075
|
+
auth: "protected",
|
|
54076
|
+
role: "viewer"
|
|
53822
54077
|
}), method(object({ deviceType: string() }), HeroLayoutRowsSchema, {
|
|
53823
54078
|
auth: "protected",
|
|
53824
54079
|
role: "viewer"
|
|
@@ -54342,13 +54597,27 @@ var IntegrationInstancePatchSchema = object({
|
|
|
54342
54597
|
method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }), method(object({
|
|
54343
54598
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54344
54599
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54345
|
-
kind: string().min(1).optional()
|
|
54600
|
+
kind: string().min(1).optional(),
|
|
54601
|
+
/** The wizard mode of the integration type (`getAvailableTypes`); a form may differ per mode. */
|
|
54602
|
+
mode: _enum([
|
|
54603
|
+
"standalone",
|
|
54604
|
+
"account",
|
|
54605
|
+
"broker"
|
|
54606
|
+
]).optional(),
|
|
54607
|
+
/** The discovery candidate the wizard was seeded from (`stableId`), if any. */
|
|
54608
|
+
candidateStableId: string().min(1).optional()
|
|
54346
54609
|
}), ConfigSchemaPassthrough$2, { auth: "admin" }), method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }), method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }), method(object({
|
|
54347
54610
|
id: IntegrationInstanceIdSchema.optional(),
|
|
54348
54611
|
name: string().min(1),
|
|
54349
54612
|
kind: string().min(1).optional(),
|
|
54350
54613
|
/** Form values written with the record (a link picked in the create dialog). */
|
|
54351
|
-
values: record(string(), unknown()).optional()
|
|
54614
|
+
values: record(string(), unknown()).optional(),
|
|
54615
|
+
/**
|
|
54616
|
+
* The discovery candidate (`stableId`) the instance is set up from. Kept on the integration
|
|
54617
|
+
* registry row of an instance that brings devices, so discovery stops offering a candidate
|
|
54618
|
+
* that already has its integration (D984). Ignored by an instance with no registry row.
|
|
54619
|
+
*/
|
|
54620
|
+
originStableId: string().min(1).optional()
|
|
54352
54621
|
}), object({ id: string() }), {
|
|
54353
54622
|
kind: "mutation",
|
|
54354
54623
|
auth: "admin"
|
|
@@ -54475,27 +54744,6 @@ DeviceType.Camera, method(object({ deviceId: number() }), object({
|
|
|
54475
54744
|
deviceId: number(),
|
|
54476
54745
|
status: IntercomStatusSchema
|
|
54477
54746
|
});
|
|
54478
|
-
/**
|
|
54479
|
-
* A pulse train in signed microseconds: mark (+) first, then space (-), strictly
|
|
54480
|
-
* alternating, no zeros, 2..1024 entries.
|
|
54481
|
-
*/
|
|
54482
|
-
var IrTimingsSchema = array(number().int()).min(2).max(1024).superRefine((timings, ctx) => {
|
|
54483
|
-
timings.forEach((value, index) => {
|
|
54484
|
-
const expectMark = index % 2 === 0;
|
|
54485
|
-
if (value === 0 || value > 0 !== expectMark) ctx.addIssue({
|
|
54486
|
-
code: "custom",
|
|
54487
|
-
message: `timing ${index} must be a non-zero ${expectMark ? "mark (positive)" : "space (negative)"}`,
|
|
54488
|
-
path: [index]
|
|
54489
|
-
});
|
|
54490
|
-
});
|
|
54491
|
-
});
|
|
54492
|
-
/** Per-command transmit options. Every key is optional; a transmitter applies what it can. */
|
|
54493
|
-
var TransmitSettingsSchema = object({
|
|
54494
|
-
carrierHz: number().int().positive().optional(),
|
|
54495
|
-
dutyCyclePercent: number().int().min(1).max(100).optional(),
|
|
54496
|
-
repeat: number().int().min(0).max(20).optional(),
|
|
54497
|
-
repeatDelayMs: number().int().min(0).optional()
|
|
54498
|
-
});
|
|
54499
54747
|
/** The setting keys a transmitter may declare as settable (`getInfo.settable`). */
|
|
54500
54748
|
var TransmitSettingKeySchema = _enum([
|
|
54501
54749
|
"carrierHz",
|
|
@@ -70818,6 +71066,10 @@ function refineSources(config, ctx) {
|
|
|
70818
71066
|
if (numbers.has(source.number)) fail(`channel ${source.number} is declared twice`, "number");
|
|
70819
71067
|
numbers.add(source.number);
|
|
70820
71068
|
}
|
|
71069
|
+
if (source.kind === "ir") {
|
|
71070
|
+
const problem = irSourceProblem(source);
|
|
71071
|
+
if (problem !== null) fail(problem, "payload");
|
|
71072
|
+
}
|
|
70821
71073
|
for (const role of remoteSourceSteps(source)) if (isRemoteWidgetRole(role)) fail(`"${source.name}" presses ${role}, a layout widget, not a command`, "steps");
|
|
70822
71074
|
else if (config.bindings[role] === void 0) fail(`"${source.name}" presses ${role}, which is not bound`, "steps");
|
|
70823
71075
|
});
|
|
@@ -70839,6 +71091,14 @@ function refineSources(config, ctx) {
|
|
|
70839
71091
|
}
|
|
70840
71092
|
});
|
|
70841
71093
|
}
|
|
71094
|
+
/** A stored source that was left out of a read, and why (the UI shows it). */
|
|
71095
|
+
var RemoteRefusedSourceSchema = object({
|
|
71096
|
+
/** Position in the stored list. */
|
|
71097
|
+
index: number().int().nonnegative(),
|
|
71098
|
+
id: string().nullable(),
|
|
71099
|
+
name: string().nullable(),
|
|
71100
|
+
reason: string()
|
|
71101
|
+
});
|
|
70842
71102
|
var RemoteConfigSchema = object({
|
|
70843
71103
|
layoutTemplateId: string().min(1).nullable(),
|
|
70844
71104
|
/**
|
|
@@ -70856,7 +71116,13 @@ var RemoteConfigSchema = object({
|
|
|
70856
71116
|
sources: array(RemoteSourceSchema).max(200).default([]),
|
|
70857
71117
|
/** The pause between two steps of a source: the television's receiver, not a repeat. */
|
|
70858
71118
|
stepDelayMs: number().int().min(100).max(REMOTE_STEP_DELAY_MAX_MS).default(500),
|
|
70859
|
-
power: object({ mode: RemotePowerModeSchema })
|
|
71119
|
+
power: object({ mode: RemotePowerModeSchema }),
|
|
71120
|
+
/**
|
|
71121
|
+
* READ-ONLY: sources a stored configuration carries that no longer parse,
|
|
71122
|
+
* left out of `sources` with the reason (`readStoredRemoteConfig`). Never
|
|
71123
|
+
* stored: the store strips it on write.
|
|
71124
|
+
*/
|
|
71125
|
+
refusedSources: array(RemoteRefusedSourceSchema).optional()
|
|
70860
71126
|
}).superRefine((config, ctx) => {
|
|
70861
71127
|
const roles = Object.keys(config.bindings);
|
|
70862
71128
|
if (roles.length > 200) ctx.addIssue({
|
|
@@ -70897,6 +71163,11 @@ var RemoteConfigSchema = object({
|
|
|
70897
71163
|
}
|
|
70898
71164
|
refineSources(config, ctx);
|
|
70899
71165
|
});
|
|
71166
|
+
looseObject({ sources: array(unknown()) });
|
|
71167
|
+
object({
|
|
71168
|
+
id: string(),
|
|
71169
|
+
name: string()
|
|
71170
|
+
}).partial();
|
|
70900
71171
|
object({
|
|
70901
71172
|
power: object({
|
|
70902
71173
|
state: _enum(["on", "off"]),
|
|
@@ -75971,14 +76242,6 @@ btn("power", 0, 0, {
|
|
|
75971
76242
|
3,
|
|
75972
76243
|
4
|
|
75973
76244
|
].map((n) => btn(`app${n}`, 0, n - 1, { role: `custom:app_slot_${n}` })), btn("back", 0, 0, { role: "back" }), btn("home", 0, 1, { role: "home" }), btn("menu", 0, 2, { role: "menu" });
|
|
75974
|
-
discriminatedUnion("kind", [object({
|
|
75975
|
-
kind: literal("raw"),
|
|
75976
|
-
timings: array(number().int()),
|
|
75977
|
-
settings: TransmitSettingsSchema.optional()
|
|
75978
|
-
}), object({
|
|
75979
|
-
kind: literal("pronto"),
|
|
75980
|
-
code: string()
|
|
75981
|
-
})]);
|
|
75982
76245
|
/**
|
|
75983
76246
|
* Propose a running threshold from a device's own power history (planning#405).
|
|
75984
76247
|
*
|
|
@@ -79793,6 +80056,13 @@ Object.freeze({
|
|
|
79793
80056
|
access: "view",
|
|
79794
80057
|
role: "viewer"
|
|
79795
80058
|
},
|
|
80059
|
+
"heroLayout.getRowModels": {
|
|
80060
|
+
capName: "hero-layout",
|
|
80061
|
+
capScope: "system",
|
|
80062
|
+
addonId: null,
|
|
80063
|
+
access: "view",
|
|
80064
|
+
role: "viewer"
|
|
80065
|
+
},
|
|
79796
80066
|
"humidifier.getStatus": {
|
|
79797
80067
|
capName: "humidifier",
|
|
79798
80068
|
capScope: "device",
|
|
@@ -87754,6 +88024,11 @@ Object.freeze({
|
|
|
87754
88024
|
form: "single",
|
|
87755
88025
|
optional: false
|
|
87756
88026
|
}],
|
|
88027
|
+
"heroLayout.getRowModels": [{
|
|
88028
|
+
name: "deviceIds",
|
|
88029
|
+
form: "array",
|
|
88030
|
+
optional: false
|
|
88031
|
+
}],
|
|
87757
88032
|
"humidifier.getStatus": [{
|
|
87758
88033
|
name: "deviceId",
|
|
87759
88034
|
form: "single",
|
|
@@ -91063,7 +91338,7 @@ var AgentUIAddon = class extends BaseAddon {
|
|
|
91063
91338
|
capability: adminUiCapability,
|
|
91064
91339
|
provider: {
|
|
91065
91340
|
getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
|
|
91066
|
-
getVersion: async () => ({ version: "1.2.
|
|
91341
|
+
getVersion: async () => ({ version: "1.2.349" })
|
|
91067
91342
|
}
|
|
91068
91343
|
}];
|
|
91069
91344
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{T as a,_ as o,c as s,d as c,l,p as u,t as d}from"./index-
|
|
1
|
+
import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{T as a,_ as o,c as s,d as c,l,p as u,t as d}from"./index-Bngw8_Yl.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=a(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),m=n(i(),1),h=n(e(),1),g=110,_=`motion-zones`,v=0,y=[1,2,3],b=1,x=`rounded-md border border-border bg-surface px-2 py-1 text-[11px] font-medium text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,S=`rounded-md border border-primary/50 bg-primary/15 px-2.5 py-1 text-[11px] font-medium text-primary hover:bg-primary/25 disabled:opacity-40 transition-colors`;function C(e,t,n){let r=t*n,i=Array.from({length:r});for(let t=0;t<r;t+=1)i[t]=e[t]===!0;return i}function w(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(e[n]!==t[n])return!1;return!0}function T(e,t){return Math.ceil(e/t)}function E(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function D(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+E(n,t,i)]=!0)}return o}function O(e,t,n,r){let i=T(t,r),a=T(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=E(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+E(n,t,i)]===!0}return o}function k({deviceId:e}){let n=t(),i=c(o().trpcClient,e),[a,E]=(0,m.useState)(null),[k,A]=(0,m.useState)(!1),[j,M]=(0,m.useState)(null),[N,P]=(0,m.useState)(null),[F,I]=(0,m.useState)(b),[L,R]=(0,m.useState)(!1),[z,B]=(0,m.useState)(!1),V=(0,m.useRef)(!1);(0,m.useEffect)(()=>{if(!i)return;let e=!1;return V.current=!1,E(null),A(!1),M(null),P(null),I(b),B(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(E(t),V.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);V.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=C(r?r.shape.cells:[],t.grid.width,t.grid.height);P(a),M(D(a,t.grid.width,t.grid.height,b))}catch(t){if(e)return;l(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=a?a.grid.width:0,U=a?a.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&a?O(j,H,U,F):null,[j,a,H,U,F]),q=(0,m.useMemo)(()=>K!==null&&N!==null&&!w(K,N),[K,N]),J=(0,m.useMemo)(()=>K?K.reduce((e,t)=>t?e+1:e,0):0,[K]),Y=H*U,X=W*G,Z=(0,m.useCallback)(e=>{X>0&&M(Array.from({length:X},()=>e))},[X]),ee=(0,m.useCallback)(()=>{M(e=>e&&e.map(e=>!e))},[]),te=(0,m.useCallback)(()=>{N&&a&&M(D(N,H,U,F))},[N,a,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!a||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,a,H,U]),Q=(0,m.useMemo)(()=>a&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[a,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!a)){R(!0);try{let e=O(j,a.grid.width,a.grid.height,F),t={kind:`grid`,gridWidth:a.grid.width,gridHeight:a.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:v,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=C(e?e.shape.cells:[],a.grid.width,a.grid.height);P(t),M(D(t,a.grid.width,a.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,a,F]);d((0,m.useMemo)(()=>z&&!k&&a&&j?{id:_,order:g,node:(0,h.jsx)(f,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:W,height:G},selectedId:v,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[z,k,a,j,Q,$,W,G]));let ie=!k&&a!==null&&j!==null;return i?(0,h.jsx)(u,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,h.jsx)(p,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsx)(`div`,{className:`flex flex-col gap-3`,children:k?(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.motionZones.intro`,fallback:`Toggle <0>Edit grid</0> to paint the region directly on the live frame, then <1>Save</1> to push the mask to the camera. Pick a bigger <2>Cell size</2> for quicker, broad-stroke painting — it's resampled to the camera's native {{width}}×{{height}} grid on save (×1 is the finest).`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`e`),(0,h.jsx)(`strong`,{className:`text-foreground`},`s`),(0,h.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:a.grid.width,height:a.grid.height}})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:()=>B(e=>!e),disabled:L,"aria-pressed":z,className:z?S:x,children:z?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!0),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>Z(!1),disabled:L,className:x,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,h.jsx)(`button`,{type:`button`,onClick:ee,disabled:L,className:x,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,h.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,h.jsx)(`span`,{className:`${s} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),y.map(e=>(0,h.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:L,"aria-pressed":F===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:H,height:U}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:T(H,e),height:T(U,e),factor:e}),className:F===e?S:x,children:[`×`,e]},e))]}),(0,h.jsxs)(`span`,{className:`${s} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:a.grid.width,height:a.grid.height}),F===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:W,height:G})]}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:te,disabled:L||!q,className:`rounded-md border border-border bg-surface px-2 py-1 text-[11px] text-foreground-subtle hover:bg-surface-hover disabled:opacity-40 transition-colors`,children:n(`uiLibrary.motionZones.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:L||!q,className:S,children:L?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,h.jsx)(`p`,{className:`${s} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
|