@camstack/addon-agent-ui 1.2.288 → 1.2.290
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/addon.js +485 -11
- package/dist/assets/{MotionZonesSettings-6yXs0YUy.js → MotionZonesSettings-CK7u0RMD.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-D94_J9cQ.js → PrivacyMaskSettings-G--iBRlb.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-DCNzhy47.js → SceneMonitorEditor-ctKyKigc.js} +1 -1
- package/dist/assets/index-B5T1OT95.js +112 -0
- package/dist/assets/index-omoTnqTC.css +2 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/assets/index-C2HpUYT-.js +0 -112
- package/dist/assets/index-So27-K6L.css +0 -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.290] - 2026-10-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Rebuilt against updated dependencies.
|
|
15
|
+
|
|
16
|
+
## [1.2.289] - 2026-10-28
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The agent page no longer asks the hub for addon versions while unpaired, hides rename and reset where they cannot work, offers the pairing lockout reset only while locked out, and shows the framework Update button only when a newer release exists
|
|
21
|
+
|
|
10
22
|
## [1.2.288] - 2026-10-28
|
|
11
23
|
|
|
12
24
|
### Fixed
|
package/dist/addon.js
CHANGED
|
@@ -5448,7 +5448,7 @@ var ZodIssueCode = {
|
|
|
5448
5448
|
var ZodFirstPartyTypeKind;
|
|
5449
5449
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5450
5450
|
//#endregion
|
|
5451
|
-
//#region ../types/dist/sleep-
|
|
5451
|
+
//#region ../types/dist/sleep-DJws6ho7.mjs
|
|
5452
5452
|
/**
|
|
5453
5453
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5454
5454
|
* window to float samples (D455).
|
|
@@ -8659,6 +8659,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8659
8659
|
"recording-onboard",
|
|
8660
8660
|
"recording-signal",
|
|
8661
8661
|
"records",
|
|
8662
|
+
"remote",
|
|
8662
8663
|
"running-state",
|
|
8663
8664
|
"scene-monitor",
|
|
8664
8665
|
"script-runner",
|
|
@@ -8682,6 +8683,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8682
8683
|
"system",
|
|
8683
8684
|
"tamper",
|
|
8684
8685
|
"temperature-sensor",
|
|
8686
|
+
"template",
|
|
8685
8687
|
"terminal-session",
|
|
8686
8688
|
"toast",
|
|
8687
8689
|
"training",
|
|
@@ -9622,6 +9624,11 @@ Object.freeze({
|
|
|
9622
9624
|
"standby"
|
|
9623
9625
|
]
|
|
9624
9626
|
},
|
|
9627
|
+
{
|
|
9628
|
+
"path": "stateSource",
|
|
9629
|
+
"valueType": "enum",
|
|
9630
|
+
"enumValues": ["reported", "assumed"]
|
|
9631
|
+
},
|
|
9625
9632
|
{
|
|
9626
9633
|
"path": "volumeLevel",
|
|
9627
9634
|
"valueType": "number"
|
|
@@ -9897,6 +9904,18 @@ Object.freeze({
|
|
|
9897
9904
|
{
|
|
9898
9905
|
"path": "volume",
|
|
9899
9906
|
"valueType": "number"
|
|
9907
|
+
},
|
|
9908
|
+
{
|
|
9909
|
+
"path": "lastFeedGrams",
|
|
9910
|
+
"valueType": "number"
|
|
9911
|
+
},
|
|
9912
|
+
{
|
|
9913
|
+
"path": "lastFeedHopper1",
|
|
9914
|
+
"valueType": "number"
|
|
9915
|
+
},
|
|
9916
|
+
{
|
|
9917
|
+
"path": "lastFeedHopper2",
|
|
9918
|
+
"valueType": "number"
|
|
9900
9919
|
}
|
|
9901
9920
|
],
|
|
9902
9921
|
"power-meter": [
|
|
@@ -16198,9 +16217,21 @@ var AlertSeveritySchema = _enum([
|
|
|
16198
16217
|
"error",
|
|
16199
16218
|
"critical"
|
|
16200
16219
|
]);
|
|
16220
|
+
/**
|
|
16221
|
+
* An action that RUNS something instead of going somewhere: the admin UI calls
|
|
16222
|
+
* the addon's custom action (`addons.custom`) with exactly these fields. The
|
|
16223
|
+
* producer owns the meaning; the UI knows nothing about the addon or the kind.
|
|
16224
|
+
*/
|
|
16225
|
+
var AlertActionInvokeSchema = object({
|
|
16226
|
+
addonId: string().min(1),
|
|
16227
|
+
action: string().min(1),
|
|
16228
|
+
input: record(string(), unknown()).optional()
|
|
16229
|
+
});
|
|
16230
|
+
/** An action has `href` (a link) OR `invoke` (a button that runs a custom action). */
|
|
16201
16231
|
var AlertActionSchema = object({
|
|
16202
16232
|
label: string(),
|
|
16203
16233
|
href: string().optional(),
|
|
16234
|
+
invoke: AlertActionInvokeSchema.optional(),
|
|
16204
16235
|
/**
|
|
16205
16236
|
* How `href` is followed. Absent / `app`: an admin-ui route (client-side).
|
|
16206
16237
|
* `server`: a route the HUB serves (an addon HTTP route such as an OAuth
|
|
@@ -57351,6 +57382,318 @@ DeviceType.Light, method(object({
|
|
|
57351
57382
|
level: number().min(0).max(100),
|
|
57352
57383
|
lastChangedAt: number()
|
|
57353
57384
|
});
|
|
57385
|
+
/**
|
|
57386
|
+
* The role vocabulary of a remote (D963): what a button MEANS, independent of
|
|
57387
|
+
* how it is wired. A layout template places buttons by role; a device binds a
|
|
57388
|
+
* role to an action; an exporter asks for a role by name. Roles are shared so
|
|
57389
|
+
* one layout fits every appliance and one voice command fits every binding.
|
|
57390
|
+
*
|
|
57391
|
+
* A role outside this list is `custom:<key>` — the operator's own button (a
|
|
57392
|
+
* SmartIR source, a channel preset). The key is a lowercase slug.
|
|
57393
|
+
*/
|
|
57394
|
+
var REMOTE_KNOWN_ROLES = [
|
|
57395
|
+
"power",
|
|
57396
|
+
"power_on",
|
|
57397
|
+
"power_off",
|
|
57398
|
+
"volume_up",
|
|
57399
|
+
"volume_down",
|
|
57400
|
+
"mute",
|
|
57401
|
+
"channel_up",
|
|
57402
|
+
"channel_down",
|
|
57403
|
+
"up",
|
|
57404
|
+
"down",
|
|
57405
|
+
"left",
|
|
57406
|
+
"right",
|
|
57407
|
+
"ok",
|
|
57408
|
+
"back",
|
|
57409
|
+
"home",
|
|
57410
|
+
"menu",
|
|
57411
|
+
"info",
|
|
57412
|
+
"guide",
|
|
57413
|
+
"exit",
|
|
57414
|
+
"input",
|
|
57415
|
+
"play",
|
|
57416
|
+
"pause",
|
|
57417
|
+
"play_pause",
|
|
57418
|
+
"stop",
|
|
57419
|
+
"next",
|
|
57420
|
+
"previous",
|
|
57421
|
+
"fast_forward",
|
|
57422
|
+
"rewind",
|
|
57423
|
+
"digit_0",
|
|
57424
|
+
"digit_1",
|
|
57425
|
+
"digit_2",
|
|
57426
|
+
"digit_3",
|
|
57427
|
+
"digit_4",
|
|
57428
|
+
"digit_5",
|
|
57429
|
+
"digit_6",
|
|
57430
|
+
"digit_7",
|
|
57431
|
+
"digit_8",
|
|
57432
|
+
"digit_9"
|
|
57433
|
+
];
|
|
57434
|
+
var CUSTOM_KEY_RE = /^[a-z0-9][a-z0-9_]{0,47}$/;
|
|
57435
|
+
var KNOWN_ROLES = new Set(REMOTE_KNOWN_ROLES);
|
|
57436
|
+
function isRemoteKnownRole(value) {
|
|
57437
|
+
return KNOWN_ROLES.has(value);
|
|
57438
|
+
}
|
|
57439
|
+
function isRemoteCustomKey(key) {
|
|
57440
|
+
return CUSTOM_KEY_RE.test(key);
|
|
57441
|
+
}
|
|
57442
|
+
/** The key of a `custom:<key>` role, or null for any other string. */
|
|
57443
|
+
function customKeyOfRole(role) {
|
|
57444
|
+
if (!role.startsWith("custom:")) return null;
|
|
57445
|
+
const key = role.slice(7);
|
|
57446
|
+
return isRemoteCustomKey(key) ? key : null;
|
|
57447
|
+
}
|
|
57448
|
+
function isRemoteRole(value) {
|
|
57449
|
+
return isRemoteKnownRole(value) || customKeyOfRole(value) !== null;
|
|
57450
|
+
}
|
|
57451
|
+
var RemoteRoleSchema = string().refine(isRemoteRole, { message: "a role is one of the known roles or custom:<key> (lowercase letters, digits, _)" });
|
|
57452
|
+
var RemoteBindingSchema = object({ action: AutomationCapStepSchema.extend({ deviceId: number().int().nonnegative() }) });
|
|
57453
|
+
var RemoteCustomButtonSchema = object({
|
|
57454
|
+
/** The slug after `custom:` in the role. */
|
|
57455
|
+
key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
|
|
57456
|
+
/** What the operator calls it, and what a voice command may name. */
|
|
57457
|
+
name: string().min(1).max(60)
|
|
57458
|
+
});
|
|
57459
|
+
var RemotePowerModeSchema = _enum(["toggle", "discrete"]);
|
|
57460
|
+
var RemoteConfigSchema = object({
|
|
57461
|
+
layoutTemplateId: string().min(1).nullable(),
|
|
57462
|
+
/**
|
|
57463
|
+
* The transmitter device new bindings and an import send through. A binding
|
|
57464
|
+
* names its own device, so one command may override it.
|
|
57465
|
+
*/
|
|
57466
|
+
defaultTransmitterId: number().int().nonnegative().nullable().default(null),
|
|
57467
|
+
/** Keyed by role. A role with no entry falls back to the native cap. */
|
|
57468
|
+
bindings: record(string(), RemoteBindingSchema),
|
|
57469
|
+
customButtons: array(RemoteCustomButtonSchema).max(100),
|
|
57470
|
+
power: object({ mode: RemotePowerModeSchema })
|
|
57471
|
+
}).superRefine((config, ctx) => {
|
|
57472
|
+
const roles = Object.keys(config.bindings);
|
|
57473
|
+
if (roles.length > 200) ctx.addIssue({
|
|
57474
|
+
code: "custom",
|
|
57475
|
+
message: "too many bindings",
|
|
57476
|
+
path: ["bindings"]
|
|
57477
|
+
});
|
|
57478
|
+
for (const role of roles) if (!isRemoteRole(role)) ctx.addIssue({
|
|
57479
|
+
code: "custom",
|
|
57480
|
+
message: `"${role}" is not a role`,
|
|
57481
|
+
path: ["bindings", role]
|
|
57482
|
+
});
|
|
57483
|
+
const keys = /* @__PURE__ */ new Set();
|
|
57484
|
+
config.customButtons.forEach((button, index) => {
|
|
57485
|
+
if (keys.has(button.key)) ctx.addIssue({
|
|
57486
|
+
code: "custom",
|
|
57487
|
+
message: `custom button "${button.key}" is listed twice`,
|
|
57488
|
+
path: [
|
|
57489
|
+
"customButtons",
|
|
57490
|
+
index,
|
|
57491
|
+
"key"
|
|
57492
|
+
]
|
|
57493
|
+
});
|
|
57494
|
+
keys.add(button.key);
|
|
57495
|
+
});
|
|
57496
|
+
for (const role of roles) {
|
|
57497
|
+
const key = customKeyOfRole(role);
|
|
57498
|
+
if (key !== null && !keys.has(key)) ctx.addIssue({
|
|
57499
|
+
code: "custom",
|
|
57500
|
+
message: `${role} is bound but has no entry in customButtons`,
|
|
57501
|
+
path: ["bindings", role]
|
|
57502
|
+
});
|
|
57503
|
+
}
|
|
57504
|
+
});
|
|
57505
|
+
object({
|
|
57506
|
+
power: object({
|
|
57507
|
+
state: _enum(["on", "off"]),
|
|
57508
|
+
at: number()
|
|
57509
|
+
}).nullable(),
|
|
57510
|
+
muted: object({
|
|
57511
|
+
value: boolean(),
|
|
57512
|
+
at: number()
|
|
57513
|
+
}).nullable()
|
|
57514
|
+
});
|
|
57515
|
+
var RemoteLayoutButtonSchema = object({
|
|
57516
|
+
id: string().min(1).max(40),
|
|
57517
|
+
/** 0-based grid position. */
|
|
57518
|
+
row: number().int().min(0).max(24),
|
|
57519
|
+
col: number().int().min(0).max(24),
|
|
57520
|
+
rowSpan: number().int().min(1).max(24),
|
|
57521
|
+
colSpan: number().int().min(1).max(24),
|
|
57522
|
+
icon: string().min(1).max(60).optional(),
|
|
57523
|
+
label: string().min(1).max(40).optional(),
|
|
57524
|
+
role: RemoteRoleSchema
|
|
57525
|
+
});
|
|
57526
|
+
object({
|
|
57527
|
+
columns: number().int().min(1).max(24),
|
|
57528
|
+
rows: number().int().min(1).max(24),
|
|
57529
|
+
buttons: array(RemoteLayoutButtonSchema).max(200)
|
|
57530
|
+
}).superRefine((grid, ctx) => {
|
|
57531
|
+
const ids = /* @__PURE__ */ new Set();
|
|
57532
|
+
const taken = /* @__PURE__ */ new Map();
|
|
57533
|
+
grid.buttons.forEach((button, index) => {
|
|
57534
|
+
if (ids.has(button.id)) ctx.addIssue({
|
|
57535
|
+
code: "custom",
|
|
57536
|
+
message: `button id "${button.id}" is used twice`,
|
|
57537
|
+
path: [
|
|
57538
|
+
"buttons",
|
|
57539
|
+
index,
|
|
57540
|
+
"id"
|
|
57541
|
+
]
|
|
57542
|
+
});
|
|
57543
|
+
ids.add(button.id);
|
|
57544
|
+
if (button.row + button.rowSpan > grid.rows || button.col + button.colSpan > grid.columns) {
|
|
57545
|
+
ctx.addIssue({
|
|
57546
|
+
code: "custom",
|
|
57547
|
+
message: `button "${button.id}" leaves the grid`,
|
|
57548
|
+
path: ["buttons", index]
|
|
57549
|
+
});
|
|
57550
|
+
return;
|
|
57551
|
+
}
|
|
57552
|
+
for (let r = button.row; r < button.row + button.rowSpan; r += 1) for (let c = button.col; c < button.col + button.colSpan; c += 1) {
|
|
57553
|
+
const cell = `${r}:${c}`;
|
|
57554
|
+
const other = taken.get(cell);
|
|
57555
|
+
if (other !== void 0) {
|
|
57556
|
+
ctx.addIssue({
|
|
57557
|
+
code: "custom",
|
|
57558
|
+
message: `buttons "${other}" and "${button.id}" overlap`,
|
|
57559
|
+
path: ["buttons", index]
|
|
57560
|
+
});
|
|
57561
|
+
return;
|
|
57562
|
+
}
|
|
57563
|
+
taken.set(cell, button.id);
|
|
57564
|
+
}
|
|
57565
|
+
});
|
|
57566
|
+
});
|
|
57567
|
+
var RemoteBoundCommandSchema = object({
|
|
57568
|
+
role: RemoteRoleSchema,
|
|
57569
|
+
/** The operator's name for a custom button; absent for a known role. */
|
|
57570
|
+
label: string().optional(),
|
|
57571
|
+
custom: boolean()
|
|
57572
|
+
});
|
|
57573
|
+
var RemoteCommandListSchema = object({
|
|
57574
|
+
layoutTemplateId: string().nullable(),
|
|
57575
|
+
power: object({ mode: RemotePowerModeSchema }),
|
|
57576
|
+
/** Only the roles that are BOUND — the ones a press can reach. */
|
|
57577
|
+
commands: array(RemoteBoundCommandSchema)
|
|
57578
|
+
});
|
|
57579
|
+
var RemoteRefusalReasonSchema = _enum([
|
|
57580
|
+
"no-remote",
|
|
57581
|
+
"unbound-role",
|
|
57582
|
+
"action-failed"
|
|
57583
|
+
]);
|
|
57584
|
+
var RemoteSendResultSchema = discriminatedUnion("status", [
|
|
57585
|
+
object({
|
|
57586
|
+
status: literal("sent"),
|
|
57587
|
+
/** How many times the action ran. */
|
|
57588
|
+
sends: number().int().min(1)
|
|
57589
|
+
}),
|
|
57590
|
+
object({
|
|
57591
|
+
status: literal("skipped"),
|
|
57592
|
+
reason: literal("already-in-state")
|
|
57593
|
+
}),
|
|
57594
|
+
object({
|
|
57595
|
+
status: literal("refused"),
|
|
57596
|
+
reason: RemoteRefusalReasonSchema,
|
|
57597
|
+
detail: string().optional()
|
|
57598
|
+
})
|
|
57599
|
+
]);
|
|
57600
|
+
var RemoteMediaCommandSchema = _enum([
|
|
57601
|
+
"play",
|
|
57602
|
+
"pause",
|
|
57603
|
+
"stop",
|
|
57604
|
+
"next",
|
|
57605
|
+
"previous",
|
|
57606
|
+
"setMute",
|
|
57607
|
+
"selectSource"
|
|
57608
|
+
]);
|
|
57609
|
+
/** A send result, or `not-routed`: the role is not bound, so the native cap answers. */
|
|
57610
|
+
var RemoteMediaCommandResultSchema = discriminatedUnion("status", [...RemoteSendResultSchema.options, object({ status: literal("not-routed") })]);
|
|
57611
|
+
var RemoteSetConfigResultSchema = discriminatedUnion("status", [object({ status: literal("saved") }), object({
|
|
57612
|
+
status: literal("refused"),
|
|
57613
|
+
reason: _enum(["unknown-template", "not-a-media-player"]),
|
|
57614
|
+
detail: string()
|
|
57615
|
+
})]);
|
|
57616
|
+
var deviceInput = object({ deviceId: number().int().nonnegative() });
|
|
57617
|
+
DeviceType.MediaPlayer;
|
|
57618
|
+
method(deviceInput, RemoteConfigSchema, { auth: "admin" }), method(deviceInput.extend({ config: RemoteConfigSchema }), RemoteSetConfigResultSchema, {
|
|
57619
|
+
kind: "mutation",
|
|
57620
|
+
auth: "admin"
|
|
57621
|
+
}), method(deviceInput, RemoteCommandListSchema, { auth: "protected" }), method(deviceInput.extend({
|
|
57622
|
+
role: RemoteRoleSchema,
|
|
57623
|
+
/** Sends in a row (volume steps). */
|
|
57624
|
+
repeat: number().int().min(1).max(50).default(1)
|
|
57625
|
+
}), RemoteSendResultSchema, {
|
|
57626
|
+
kind: "mutation",
|
|
57627
|
+
auth: "protected"
|
|
57628
|
+
}), method(deviceInput.extend({
|
|
57629
|
+
command: RemoteMediaCommandSchema,
|
|
57630
|
+
muted: boolean().optional(),
|
|
57631
|
+
source: string().optional(),
|
|
57632
|
+
repeat: number().int().min(1).max(50).default(1)
|
|
57633
|
+
}), RemoteMediaCommandResultSchema, {
|
|
57634
|
+
kind: "mutation",
|
|
57635
|
+
auth: "protected"
|
|
57636
|
+
});
|
|
57637
|
+
var TemplateKindSchema = _enum(["remote"]);
|
|
57638
|
+
/**
|
|
57639
|
+
* template — the generic store of reusable, named operator data (D963). A
|
|
57640
|
+
* record is `{ id, kind, name, payload }`; the kinds (today only `remote`, the
|
|
57641
|
+
* button grid of a remote) and the schema each payload must satisfy are
|
|
57642
|
+
* declared by the system in `template-kinds.ts`. Nothing here is specific to a
|
|
57643
|
+
* kind: the engine validates a payload against its kind's schema and, on a
|
|
57644
|
+
* delete, asks the kind's owner whether anything still points at the record.
|
|
57645
|
+
*
|
|
57646
|
+
* Every refusal is a typed result — a UI says "unknown kind" or names the
|
|
57647
|
+
* devices that still use a template, without parsing text.
|
|
57648
|
+
*/
|
|
57649
|
+
var TemplateRecordSchema = object({
|
|
57650
|
+
id: string().min(1),
|
|
57651
|
+
kind: TemplateKindSchema,
|
|
57652
|
+
name: string().min(1).max(120),
|
|
57653
|
+
/** Shaped by the kind; `checkTemplatePayload(kind, payload)` reads it. */
|
|
57654
|
+
payload: unknown(),
|
|
57655
|
+
createdAt: number(),
|
|
57656
|
+
updatedAt: number()
|
|
57657
|
+
});
|
|
57658
|
+
var TemplateWriteResultSchema = discriminatedUnion("status", [object({
|
|
57659
|
+
status: literal("saved"),
|
|
57660
|
+
template: TemplateRecordSchema
|
|
57661
|
+
}), object({
|
|
57662
|
+
status: literal("refused"),
|
|
57663
|
+
reason: _enum(["invalid-payload", "not-found"]),
|
|
57664
|
+
detail: string()
|
|
57665
|
+
})]);
|
|
57666
|
+
/** A device still pointing at a template. */
|
|
57667
|
+
var TemplateUserSchema = object({
|
|
57668
|
+
deviceId: number().int().nonnegative(),
|
|
57669
|
+
name: string()
|
|
57670
|
+
});
|
|
57671
|
+
var TemplateDeleteResultSchema = discriminatedUnion("status", [
|
|
57672
|
+
object({ status: literal("deleted") }),
|
|
57673
|
+
object({ status: literal("not-found") }),
|
|
57674
|
+
object({
|
|
57675
|
+
status: literal("in-use"),
|
|
57676
|
+
devices: array(TemplateUserSchema).min(1)
|
|
57677
|
+
})
|
|
57678
|
+
]);
|
|
57679
|
+
method(object({ kind: TemplateKindSchema.optional() }), array(TemplateRecordSchema), { auth: "protected" }), method(object({ id: string().min(1) }), TemplateRecordSchema.nullable(), { auth: "protected" }), method(object({
|
|
57680
|
+
kind: TemplateKindSchema,
|
|
57681
|
+
name: string().min(1).max(120),
|
|
57682
|
+
payload: unknown()
|
|
57683
|
+
}), TemplateWriteResultSchema, {
|
|
57684
|
+
kind: "mutation",
|
|
57685
|
+
auth: "admin"
|
|
57686
|
+
}), method(object({
|
|
57687
|
+
id: string().min(1),
|
|
57688
|
+
name: string().min(1).max(120).optional(),
|
|
57689
|
+
payload: unknown().optional()
|
|
57690
|
+
}), TemplateWriteResultSchema, {
|
|
57691
|
+
kind: "mutation",
|
|
57692
|
+
auth: "admin"
|
|
57693
|
+
}), method(object({ id: string().min(1) }), TemplateDeleteResultSchema, {
|
|
57694
|
+
kind: "mutation",
|
|
57695
|
+
auth: "admin"
|
|
57696
|
+
});
|
|
57354
57697
|
DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
57355
57698
|
kind: "mutation",
|
|
57356
57699
|
auth: "protected"
|
|
@@ -60100,9 +60443,35 @@ var MediaInfoSchema = object({
|
|
|
60100
60443
|
/** Optional cover-art / thumbnail URL. */
|
|
60101
60444
|
imageUrl: string().optional()
|
|
60102
60445
|
});
|
|
60446
|
+
/**
|
|
60447
|
+
* Where `state`, `volumeLevel`, `isMuted` and `source` come from. `reported`:
|
|
60448
|
+
* the device said so. `assumed`: the driver has no feedback (infrared) and
|
|
60449
|
+
* tracks what it last SENT; it moves only after a send was accepted, and a
|
|
60450
|
+
* human at the real remote makes it wrong. A UI shows an assumed value as a
|
|
60451
|
+
* guess, never as a reading.
|
|
60452
|
+
*/
|
|
60453
|
+
var MediaPlayerStateSourceSchema = _enum(["reported", "assumed"]);
|
|
60454
|
+
/** The fields of a media-player status a driver can only guess. */
|
|
60455
|
+
var MediaPlayerAssumedFieldSchema = _enum([
|
|
60456
|
+
"state",
|
|
60457
|
+
"volumeLevel",
|
|
60458
|
+
"isMuted",
|
|
60459
|
+
"source"
|
|
60460
|
+
]);
|
|
60103
60461
|
object({
|
|
60104
60462
|
/** Playback lifecycle state. */
|
|
60105
60463
|
state: MediaPlayerStateSchema,
|
|
60464
|
+
/**
|
|
60465
|
+
* Whether the state fields are readings or the driver's own bookkeeping.
|
|
60466
|
+
* A slice written before this field existed parses as `reported`.
|
|
60467
|
+
*/
|
|
60468
|
+
stateSource: MediaPlayerStateSourceSchema.default("reported"),
|
|
60469
|
+
/**
|
|
60470
|
+
* Which fields are the driver's own guess. `stateSource` is `assumed` when
|
|
60471
|
+
* this is not empty; a field not listed here is a reading even then (power
|
|
60472
|
+
* from a running-state source, mute guessed from the last send).
|
|
60473
|
+
*/
|
|
60474
|
+
assumedFields: array(MediaPlayerAssumedFieldSchema).default([]),
|
|
60106
60475
|
/** Volume as 0..100 inclusive. Null when the device has no volume
|
|
60107
60476
|
* surface. Pair with `DeviceFeature.MediaPlayerVolume`. */
|
|
60108
60477
|
volumeLevel: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
@@ -61543,6 +61912,14 @@ object({
|
|
|
61543
61912
|
feedSound: boolean(),
|
|
61544
61913
|
/** Speaker / prompt volume level (device-scaled integer). */
|
|
61545
61914
|
volume: number().meta(readingMeta("none")),
|
|
61915
|
+
/** Portion (grams) of the last manual feed sent from a single-hopper
|
|
61916
|
+
* stepper — the default the next manual feed starts from. Persisted per
|
|
61917
|
+
* device by the provider; null until a manual feed has been sent. */
|
|
61918
|
+
lastFeedGrams: gramsPortion.nullable(),
|
|
61919
|
+
/** Hopper-1 portion of the last manual feed (dual-hopper); null until sent. */
|
|
61920
|
+
lastFeedHopper1: gramsPortion.nullable(),
|
|
61921
|
+
/** Hopper-2 portion of the last manual feed (dual-hopper); null until sent. */
|
|
61922
|
+
lastFeedHopper2: gramsPortion.nullable(),
|
|
61546
61923
|
/** Ms epoch when the slice was last refreshed from the cloud. */
|
|
61547
61924
|
lastFetchedAt: number()
|
|
61548
61925
|
});
|
|
@@ -66184,9 +66561,17 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
66184
66561
|
kind: "mutation",
|
|
66185
66562
|
auth: "admin"
|
|
66186
66563
|
});
|
|
66564
|
+
discriminatedUnion("kind", [object({
|
|
66565
|
+
kind: literal("raw"),
|
|
66566
|
+
timings: array(number().int()),
|
|
66567
|
+
settings: TransmitSettingsSchema.optional()
|
|
66568
|
+
}), object({
|
|
66569
|
+
kind: literal("pronto"),
|
|
66570
|
+
code: string()
|
|
66571
|
+
})]);
|
|
66187
66572
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
66188
66573
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
66189
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
66574
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter, 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.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Camera, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
66190
66575
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
66191
66576
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
66192
66577
|
new Set(CAP_NAMES);
|
|
@@ -73928,6 +74313,41 @@ Object.freeze({
|
|
|
73928
74313
|
access: "create",
|
|
73929
74314
|
role: "admin"
|
|
73930
74315
|
},
|
|
74316
|
+
"remote.getConfig": {
|
|
74317
|
+
capName: "remote",
|
|
74318
|
+
capScope: "system",
|
|
74319
|
+
addonId: null,
|
|
74320
|
+
access: "view",
|
|
74321
|
+
role: "admin"
|
|
74322
|
+
},
|
|
74323
|
+
"remote.listCommands": {
|
|
74324
|
+
capName: "remote",
|
|
74325
|
+
capScope: "system",
|
|
74326
|
+
addonId: null,
|
|
74327
|
+
access: "view",
|
|
74328
|
+
role: "admin"
|
|
74329
|
+
},
|
|
74330
|
+
"remote.sendCommand": {
|
|
74331
|
+
capName: "remote",
|
|
74332
|
+
capScope: "system",
|
|
74333
|
+
addonId: null,
|
|
74334
|
+
access: "create",
|
|
74335
|
+
role: "admin"
|
|
74336
|
+
},
|
|
74337
|
+
"remote.sendMediaCommand": {
|
|
74338
|
+
capName: "remote",
|
|
74339
|
+
capScope: "system",
|
|
74340
|
+
addonId: null,
|
|
74341
|
+
access: "create",
|
|
74342
|
+
role: "admin"
|
|
74343
|
+
},
|
|
74344
|
+
"remote.setConfig": {
|
|
74345
|
+
capName: "remote",
|
|
74346
|
+
capScope: "system",
|
|
74347
|
+
addonId: null,
|
|
74348
|
+
access: "create",
|
|
74349
|
+
role: "admin"
|
|
74350
|
+
},
|
|
73931
74351
|
"runningState.getStatus": {
|
|
73932
74352
|
capName: "running-state",
|
|
73933
74353
|
capScope: "device",
|
|
@@ -75118,6 +75538,41 @@ Object.freeze({
|
|
|
75118
75538
|
access: "view",
|
|
75119
75539
|
role: "viewer"
|
|
75120
75540
|
},
|
|
75541
|
+
"template.create": {
|
|
75542
|
+
capName: "template",
|
|
75543
|
+
capScope: "system",
|
|
75544
|
+
addonId: null,
|
|
75545
|
+
access: "create",
|
|
75546
|
+
role: "admin"
|
|
75547
|
+
},
|
|
75548
|
+
"template.delete": {
|
|
75549
|
+
capName: "template",
|
|
75550
|
+
capScope: "system",
|
|
75551
|
+
addonId: null,
|
|
75552
|
+
access: "delete",
|
|
75553
|
+
role: "admin"
|
|
75554
|
+
},
|
|
75555
|
+
"template.get": {
|
|
75556
|
+
capName: "template",
|
|
75557
|
+
capScope: "system",
|
|
75558
|
+
addonId: null,
|
|
75559
|
+
access: "view",
|
|
75560
|
+
role: "admin"
|
|
75561
|
+
},
|
|
75562
|
+
"template.list": {
|
|
75563
|
+
capName: "template",
|
|
75564
|
+
capScope: "system",
|
|
75565
|
+
addonId: null,
|
|
75566
|
+
access: "view",
|
|
75567
|
+
role: "admin"
|
|
75568
|
+
},
|
|
75569
|
+
"template.update": {
|
|
75570
|
+
capName: "template",
|
|
75571
|
+
capScope: "system",
|
|
75572
|
+
addonId: null,
|
|
75573
|
+
access: "create",
|
|
75574
|
+
role: "admin"
|
|
75575
|
+
},
|
|
75121
75576
|
"terminalSession.adoptLegacyMonitor": {
|
|
75122
75577
|
capName: "terminal-session",
|
|
75123
75578
|
capScope: "system",
|
|
@@ -79949,6 +80404,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
79949
80404
|
"recording-archive": system(STORAGE),
|
|
79950
80405
|
"recording-export": system("recording exports — requested from the timeline, not an automation step"),
|
|
79951
80406
|
records: system(INTERNAL),
|
|
80407
|
+
remote: system("the per-device remote setup — authored in the Customize remote editor, sent through the remote UI"),
|
|
79952
80408
|
"server-management": system(CLUSTER),
|
|
79953
80409
|
"settings-store": system(INTERNAL),
|
|
79954
80410
|
"smtp-provider": system(INTERNAL),
|
|
@@ -79961,6 +80417,7 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
79961
80417
|
"storage-provider": system(STORAGE),
|
|
79962
80418
|
"stream-broker": system(PIPELINE),
|
|
79963
80419
|
system: system(CLUSTER),
|
|
80420
|
+
template: system("operator-authored templates — edited in their own editor, never actuated"),
|
|
79964
80421
|
"terminal-session": system(CLUSTER),
|
|
79965
80422
|
toast: system(UI_PLUMBING),
|
|
79966
80423
|
training: system(ML),
|
|
@@ -82387,6 +82844,31 @@ Object.freeze({
|
|
|
82387
82844
|
form: "single",
|
|
82388
82845
|
optional: true
|
|
82389
82846
|
}],
|
|
82847
|
+
"remote.getConfig": [{
|
|
82848
|
+
name: "deviceId",
|
|
82849
|
+
form: "single",
|
|
82850
|
+
optional: false
|
|
82851
|
+
}],
|
|
82852
|
+
"remote.listCommands": [{
|
|
82853
|
+
name: "deviceId",
|
|
82854
|
+
form: "single",
|
|
82855
|
+
optional: false
|
|
82856
|
+
}],
|
|
82857
|
+
"remote.sendCommand": [{
|
|
82858
|
+
name: "deviceId",
|
|
82859
|
+
form: "single",
|
|
82860
|
+
optional: false
|
|
82861
|
+
}],
|
|
82862
|
+
"remote.sendMediaCommand": [{
|
|
82863
|
+
name: "deviceId",
|
|
82864
|
+
form: "single",
|
|
82865
|
+
optional: false
|
|
82866
|
+
}],
|
|
82867
|
+
"remote.setConfig": [{
|
|
82868
|
+
name: "deviceId",
|
|
82869
|
+
form: "single",
|
|
82870
|
+
optional: false
|
|
82871
|
+
}],
|
|
82390
82872
|
"runningState.getStatus": [{
|
|
82391
82873
|
name: "deviceId",
|
|
82392
82874
|
form: "single",
|
|
@@ -84195,14 +84677,6 @@ DEFAULT_NATIVE_LEASE_SETTINGS.admission;
|
|
|
84195
84677
|
String.raw`(?<=^|["'(\s>,\[{])`;
|
|
84196
84678
|
var MB = 1024 * 1024;
|
|
84197
84679
|
1024 * MB, 3072 * MB;
|
|
84198
|
-
discriminatedUnion("kind", [object({
|
|
84199
|
-
kind: literal("raw"),
|
|
84200
|
-
timings: array(number().int()),
|
|
84201
|
-
settings: TransmitSettingsSchema.optional()
|
|
84202
|
-
}), object({
|
|
84203
|
-
kind: literal("pronto"),
|
|
84204
|
-
code: string()
|
|
84205
|
-
})]);
|
|
84206
84680
|
object({
|
|
84207
84681
|
manufacturer: string().optional(),
|
|
84208
84682
|
supportedModels: array(string()).optional(),
|
|
@@ -84232,7 +84706,7 @@ var AgentUIAddon = class extends BaseAddon {
|
|
|
84232
84706
|
capability: adminUiCapability,
|
|
84233
84707
|
provider: {
|
|
84234
84708
|
getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
|
|
84235
|
-
getVersion: async () => ({ version: "1.2.
|
|
84709
|
+
getVersion: async () => ({ version: "1.2.290" })
|
|
84236
84710
|
}
|
|
84237
84711
|
}];
|
|
84238
84712
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{c as a,d as o,g as s,l as c,p as l,t as u,w 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{c as a,d as o,g as s,l as c,p as l,t as u,w as d}from"./index-B5T1OT95.js";import{MaskShapeCanvas as f}from"./MaskShapeCanvas-BTvl6Rin.js";var p=d(`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=o(s().trpcClient,e),[d,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;c(t)?A(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let H=d?d.grid.width:0,U=d?d.grid.height:0,W=T(H,F),G=T(U,F),K=(0,m.useMemo)(()=>j&&d?O(j,H,U,F):null,[j,d,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&&d&&M(D(N,H,U,F))},[N,d,H,U,F]),ne=(0,m.useCallback)(e=>{e===F||!d||M(t=>{if(!t)return I(e),t;let n=D(O(t,H,U,F),H,U,e);return I(e),n})},[F,d,H,U]),Q=(0,m.useMemo)(()=>d&&j?[{id:v,shape:{kind:`grid`,gridWidth:W,gridHeight:G,cells:[...j]}}]:[],[d,j,W,G]),$=(0,m.useCallback)((e,t)=>{t.kind===`grid`&&M(t.cells)},[]),re=(0,m.useCallback)(async()=>{if(!(!i||!j||!d)){R(!0);try{let e=O(j,d.grid.width,d.grid.height,F),t={kind:`grid`,gridWidth:d.grid.width,gridHeight:d.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:[],d.grid.width,d.grid.height);P(t),M(D(t,d.grid.width,d.grid.height,F))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{R(!1)}}},[i,j,d,F]);u((0,m.useMemo)(()=>z&&!k&&d&&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,d,j,Q,$,W,G]));let ie=!k&&d!==null&&j!==null;return i?(0,h.jsx)(l,{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:`${a} 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:`${a} 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:d.grid.width,height:d.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:`${a} 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:`${a} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:J,total:Y,width:d.grid.width,height:d.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:`${a} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{k as MotionZonesSettings};
|