@camstack/addon-agent-ui 1.2.277 → 1.2.279
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 +281 -31
- package/dist/assets/MotionZonesSettings-DevC_W7s.js +1 -0
- package/dist/assets/{PrivacyMaskSettings-B9TXHomV.js → PrivacyMaskSettings-VBrgPqBA.js} +1 -1
- package/dist/assets/SceneMonitorEditor-Ci4RVoX1.js +1 -0
- package/dist/assets/index-BN-6thGi.js +112 -0
- package/dist/assets/index-CwckhWMU.css +2 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/assets/MotionZonesSettings-uUbAkmjZ.js +0 -1
- package/dist/assets/SceneMonitorEditor-CEf8URN2.js +0 -1
- package/dist/assets/index-C2DdtTPO.css +0 -2
- package/dist/assets/index-DD00yN8O.js +0 -112
package/dist/addon.js
CHANGED
|
@@ -5443,7 +5443,7 @@ var ZodIssueCode = {
|
|
|
5443
5443
|
var ZodFirstPartyTypeKind;
|
|
5444
5444
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5445
5445
|
//#endregion
|
|
5446
|
-
//#region ../types/dist/sleep
|
|
5446
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
5447
5447
|
/**
|
|
5448
5448
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5449
5449
|
* window to float samples (D455).
|
|
@@ -8583,6 +8583,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8583
8583
|
"filesystem-browse",
|
|
8584
8584
|
"flood",
|
|
8585
8585
|
"gas",
|
|
8586
|
+
"group-active",
|
|
8586
8587
|
"group-commands",
|
|
8587
8588
|
"group-status",
|
|
8588
8589
|
"heating-valve",
|
|
@@ -9404,6 +9405,10 @@ Object.freeze({
|
|
|
9404
9405
|
"path": "detected",
|
|
9405
9406
|
"valueType": "boolean"
|
|
9406
9407
|
}],
|
|
9408
|
+
"group-active": [{
|
|
9409
|
+
"path": "active",
|
|
9410
|
+
"valueType": "boolean"
|
|
9411
|
+
}],
|
|
9407
9412
|
"group-status": [
|
|
9408
9413
|
{
|
|
9409
9414
|
"path": "included",
|
|
@@ -10759,7 +10764,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
10759
10764
|
return DeviceRole;
|
|
10760
10765
|
}({});
|
|
10761
10766
|
//#endregion
|
|
10762
|
-
//#region ../types/dist/composition-
|
|
10767
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
10763
10768
|
/**
|
|
10764
10769
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
10765
10770
|
*
|
|
@@ -11074,6 +11079,13 @@ var table = {
|
|
|
11074
11079
|
apply: () => {
|
|
11075
11080
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11076
11081
|
}
|
|
11082
|
+
},
|
|
11083
|
+
schmitt: {
|
|
11084
|
+
minArgs: 3,
|
|
11085
|
+
maxArgs: 5,
|
|
11086
|
+
apply: () => {
|
|
11087
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11088
|
+
}
|
|
11077
11089
|
}
|
|
11078
11090
|
};
|
|
11079
11091
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11584,7 +11596,7 @@ var RESET_PERIODS = [
|
|
|
11584
11596
|
5 * MINUTE_MS;
|
|
11585
11597
|
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11586
11598
|
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11587
|
-
var
|
|
11599
|
+
var rollingWindowObject = object({
|
|
11588
11600
|
kind: literal("rolling"),
|
|
11589
11601
|
span: _enum(ROLLING_SPANS),
|
|
11590
11602
|
customMinutes: number().int().min(5).optional()
|
|
@@ -11601,7 +11613,9 @@ var StatisticWindowSchema = discriminatedUnion("kind", [object({
|
|
|
11601
11613
|
path: ["customMinutes"],
|
|
11602
11614
|
input: payload.value
|
|
11603
11615
|
});
|
|
11604
|
-
})
|
|
11616
|
+
});
|
|
11617
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11618
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11605
11619
|
kind: literal("calendar"),
|
|
11606
11620
|
period: _enum(CALENDAR_PERIODS)
|
|
11607
11621
|
})]);
|
|
@@ -11631,7 +11645,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11631
11645
|
"occupancy",
|
|
11632
11646
|
"binary",
|
|
11633
11647
|
"switch",
|
|
11634
|
-
"light"
|
|
11648
|
+
"light",
|
|
11649
|
+
"fan",
|
|
11650
|
+
"cover",
|
|
11651
|
+
"valve",
|
|
11652
|
+
"lock",
|
|
11653
|
+
"media-player",
|
|
11654
|
+
"climate"
|
|
11635
11655
|
];
|
|
11636
11656
|
var NUMERIC_AGGREGATES = [
|
|
11637
11657
|
"mean",
|
|
@@ -11690,6 +11710,42 @@ var HelperSpecSchema = discriminatedUnion("kind", [
|
|
|
11690
11710
|
reset: _enum(RESET_PERIODS),
|
|
11691
11711
|
output: _enum(["energy", "meter"]),
|
|
11692
11712
|
source: HelperMemberSchema
|
|
11713
|
+
}),
|
|
11714
|
+
object({
|
|
11715
|
+
kind: literal("threshold"),
|
|
11716
|
+
source: HelperMemberSchema,
|
|
11717
|
+
high: number(),
|
|
11718
|
+
low: number(),
|
|
11719
|
+
onForMs: number().int().nonnegative(),
|
|
11720
|
+
offForMs: number().int().nonnegative()
|
|
11721
|
+
}),
|
|
11722
|
+
object({
|
|
11723
|
+
kind: literal("arithmetic"),
|
|
11724
|
+
op: _enum([
|
|
11725
|
+
"sum",
|
|
11726
|
+
"difference",
|
|
11727
|
+
"ratio"
|
|
11728
|
+
]),
|
|
11729
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
11730
|
+
}),
|
|
11731
|
+
object({
|
|
11732
|
+
kind: literal("expression"),
|
|
11733
|
+
expr: string().min(1),
|
|
11734
|
+
inputs: array(object({
|
|
11735
|
+
name: string().min(1),
|
|
11736
|
+
member: HelperMemberSchema
|
|
11737
|
+
})).min(1).max(64),
|
|
11738
|
+
output: _enum(["number", "boolean"])
|
|
11739
|
+
}),
|
|
11740
|
+
object({
|
|
11741
|
+
kind: literal("derivative"),
|
|
11742
|
+
window: RollingWindowSchema,
|
|
11743
|
+
per: _enum([
|
|
11744
|
+
"second",
|
|
11745
|
+
"minute",
|
|
11746
|
+
"hour"
|
|
11747
|
+
]),
|
|
11748
|
+
source: HelperMemberSchema
|
|
11693
11749
|
})
|
|
11694
11750
|
]);
|
|
11695
11751
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
@@ -11752,6 +11808,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11752
11808
|
"all"
|
|
11753
11809
|
]),
|
|
11754
11810
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
11811
|
+
/**
|
|
11812
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
11813
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
11814
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
11815
|
+
*/
|
|
11816
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11755
11817
|
/** `value` is the reading; the others are the member counts. */
|
|
11756
11818
|
publish: _enum([
|
|
11757
11819
|
"value",
|
|
@@ -16944,7 +17006,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
16944
17006
|
*/
|
|
16945
17007
|
interactiveConnectPath: string().min(1).optional(),
|
|
16946
17008
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
16947
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17009
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17010
|
+
/**
|
|
17011
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17012
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17013
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17014
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17015
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17016
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17017
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17018
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17019
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17020
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17021
|
+
* offers it as the default path; the fields the session fills are
|
|
17022
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17023
|
+
*/
|
|
17024
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
16948
17025
|
}))
|
|
16949
17026
|
});
|
|
16950
17027
|
var ListInputSchema = object({
|
|
@@ -18178,13 +18255,17 @@ Object.freeze({
|
|
|
18178
18255
|
latchedAt: {
|
|
18179
18256
|
kind: "fixed",
|
|
18180
18257
|
result: ["number", "null"]
|
|
18258
|
+
},
|
|
18259
|
+
schmitt: {
|
|
18260
|
+
kind: "fixed",
|
|
18261
|
+
result: ["boolean", "null"]
|
|
18181
18262
|
}
|
|
18182
18263
|
});
|
|
18183
18264
|
/**
|
|
18184
18265
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18185
18266
|
* Data, not a form default written into the block.
|
|
18186
18267
|
*/
|
|
18187
|
-
var NOW$
|
|
18268
|
+
var NOW$2 = {
|
|
18188
18269
|
fieldPath: "lastFetchedAt",
|
|
18189
18270
|
expr: "now"
|
|
18190
18271
|
};
|
|
@@ -18198,6 +18279,8 @@ function numeric(input) {
|
|
|
18198
18279
|
family: "numeric",
|
|
18199
18280
|
deviceType: DeviceType.Sensor,
|
|
18200
18281
|
memberDeviceType: DeviceType.Sensor,
|
|
18282
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18283
|
+
activeWhen: void 0,
|
|
18201
18284
|
commandCaps: [],
|
|
18202
18285
|
baseUnit: baseUnitOf(input.quantity),
|
|
18203
18286
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18213,6 +18296,8 @@ function binary(input) {
|
|
|
18213
18296
|
family: "binary",
|
|
18214
18297
|
deviceType: DeviceType.Sensor,
|
|
18215
18298
|
memberDeviceType: DeviceType.Sensor,
|
|
18299
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18300
|
+
activeWhen: void 0,
|
|
18216
18301
|
commandCaps: [],
|
|
18217
18302
|
quantity: "none",
|
|
18218
18303
|
baseUnit: void 0,
|
|
@@ -18230,7 +18315,7 @@ var GROUP_TEMPLATES = [
|
|
|
18230
18315
|
quantity: "temperature",
|
|
18231
18316
|
defaultAggregate: "mean",
|
|
18232
18317
|
empty: "null",
|
|
18233
|
-
companions: [NOW$
|
|
18318
|
+
companions: [NOW$2]
|
|
18234
18319
|
}),
|
|
18235
18320
|
numeric({
|
|
18236
18321
|
id: "humidity",
|
|
@@ -18241,7 +18326,7 @@ var GROUP_TEMPLATES = [
|
|
|
18241
18326
|
quantity: "humidity",
|
|
18242
18327
|
defaultAggregate: "mean",
|
|
18243
18328
|
empty: "null",
|
|
18244
|
-
companions: [NOW$
|
|
18329
|
+
companions: [NOW$2]
|
|
18245
18330
|
}),
|
|
18246
18331
|
numeric({
|
|
18247
18332
|
id: "pressure",
|
|
@@ -18252,7 +18337,7 @@ var GROUP_TEMPLATES = [
|
|
|
18252
18337
|
quantity: "pressure",
|
|
18253
18338
|
defaultAggregate: "mean",
|
|
18254
18339
|
empty: "null",
|
|
18255
|
-
companions: [NOW$
|
|
18340
|
+
companions: [NOW$2]
|
|
18256
18341
|
}),
|
|
18257
18342
|
numeric({
|
|
18258
18343
|
id: "illuminance",
|
|
@@ -18263,7 +18348,7 @@ var GROUP_TEMPLATES = [
|
|
|
18263
18348
|
quantity: "illuminance",
|
|
18264
18349
|
defaultAggregate: "mean",
|
|
18265
18350
|
empty: "null",
|
|
18266
|
-
companions: [NOW$
|
|
18351
|
+
companions: [NOW$2]
|
|
18267
18352
|
}),
|
|
18268
18353
|
numeric({
|
|
18269
18354
|
id: "power",
|
|
@@ -18274,7 +18359,7 @@ var GROUP_TEMPLATES = [
|
|
|
18274
18359
|
quantity: "power",
|
|
18275
18360
|
defaultAggregate: "sum",
|
|
18276
18361
|
empty: "omit",
|
|
18277
|
-
companions: [NOW$
|
|
18362
|
+
companions: [NOW$2]
|
|
18278
18363
|
}),
|
|
18279
18364
|
numeric({
|
|
18280
18365
|
id: "energy",
|
|
@@ -18285,7 +18370,7 @@ var GROUP_TEMPLATES = [
|
|
|
18285
18370
|
quantity: "energy",
|
|
18286
18371
|
defaultAggregate: "sum",
|
|
18287
18372
|
empty: "omit",
|
|
18288
|
-
companions: [NOW$
|
|
18373
|
+
companions: [NOW$2]
|
|
18289
18374
|
}),
|
|
18290
18375
|
numeric({
|
|
18291
18376
|
id: "battery",
|
|
@@ -18320,7 +18405,7 @@ var GROUP_TEMPLATES = [
|
|
|
18320
18405
|
quantity: "device",
|
|
18321
18406
|
defaultAggregate: "mean",
|
|
18322
18407
|
empty: "null",
|
|
18323
|
-
companions: [NOW$
|
|
18408
|
+
companions: [NOW$2]
|
|
18324
18409
|
}),
|
|
18325
18410
|
binary({
|
|
18326
18411
|
id: "motion",
|
|
@@ -18414,6 +18499,11 @@ var GROUP_TEMPLATES = [
|
|
|
18414
18499
|
summary: "On when at least one switch is on, or when all of them are. Offline and unknown members are left out of that decision, never read as off, and still receive the command.",
|
|
18415
18500
|
deviceType: DeviceType.Switch,
|
|
18416
18501
|
outputCap: "switch",
|
|
18502
|
+
outputField: "on",
|
|
18503
|
+
member: {
|
|
18504
|
+
cap: "switch",
|
|
18505
|
+
fieldPath: "on"
|
|
18506
|
+
},
|
|
18417
18507
|
commandCaps: ["switch"]
|
|
18418
18508
|
}),
|
|
18419
18509
|
actuator({
|
|
@@ -18421,29 +18511,130 @@ var GROUP_TEMPLATES = [
|
|
|
18421
18511
|
summary: "On when at least one light is on, or when all of them are. Brightness and colour are offered only when every member has them. A command is not sent to a member that lacks it.",
|
|
18422
18512
|
deviceType: DeviceType.Light,
|
|
18423
18513
|
outputCap: "switch",
|
|
18514
|
+
outputField: "on",
|
|
18515
|
+
member: {
|
|
18516
|
+
cap: "switch",
|
|
18517
|
+
fieldPath: "on"
|
|
18518
|
+
},
|
|
18424
18519
|
commandCaps: [
|
|
18425
18520
|
"switch",
|
|
18426
18521
|
"brightness",
|
|
18427
18522
|
"color"
|
|
18428
18523
|
]
|
|
18524
|
+
}),
|
|
18525
|
+
actuator({
|
|
18526
|
+
id: "fan",
|
|
18527
|
+
summary: "On when at least one fan is on, or when all of them are. Speed, preset, direction and oscillation are offered only when every fan has them. A command is not sent to a fan that lacks it.",
|
|
18528
|
+
deviceType: DeviceType.Fan,
|
|
18529
|
+
outputCap: "switch",
|
|
18530
|
+
outputField: "on",
|
|
18531
|
+
member: {
|
|
18532
|
+
cap: "switch",
|
|
18533
|
+
fieldPath: "on"
|
|
18534
|
+
},
|
|
18535
|
+
commandCaps: ["switch", "fan-control"]
|
|
18536
|
+
}),
|
|
18537
|
+
actuator({
|
|
18538
|
+
id: "cover",
|
|
18539
|
+
summary: "Open when at least one cover is open or part-open, or only when all of them are. Position and tilt are offered only when every cover has them. A cover that reports nothing is left out, never read as closed.",
|
|
18540
|
+
deviceType: DeviceType.Cover,
|
|
18541
|
+
outputCap: "group-active",
|
|
18542
|
+
outputField: "active",
|
|
18543
|
+
member: {
|
|
18544
|
+
cap: "cover",
|
|
18545
|
+
fieldPath: "state"
|
|
18546
|
+
},
|
|
18547
|
+
activeWhen: [
|
|
18548
|
+
"open",
|
|
18549
|
+
"opening",
|
|
18550
|
+
"stopped"
|
|
18551
|
+
],
|
|
18552
|
+
commandCaps: ["cover"]
|
|
18553
|
+
}),
|
|
18554
|
+
actuator({
|
|
18555
|
+
id: "valve",
|
|
18556
|
+
summary: "Open when at least one valve is open or part-open, or only when all of them are. Position is offered only when every valve has it. A valve that reports nothing is left out, never read as closed.",
|
|
18557
|
+
deviceType: DeviceType.Valve,
|
|
18558
|
+
outputCap: "group-active",
|
|
18559
|
+
outputField: "active",
|
|
18560
|
+
member: {
|
|
18561
|
+
cap: "valve",
|
|
18562
|
+
fieldPath: "state"
|
|
18563
|
+
},
|
|
18564
|
+
activeWhen: [
|
|
18565
|
+
"open",
|
|
18566
|
+
"opening",
|
|
18567
|
+
"stopped"
|
|
18568
|
+
],
|
|
18569
|
+
commandCaps: ["valve"]
|
|
18570
|
+
}),
|
|
18571
|
+
actuator({
|
|
18572
|
+
id: "lock",
|
|
18573
|
+
summary: "Locked when at least one lock is locked, or only when all of them are. A jammed or moving lock is not locked. Locking is one tap; unlocking the group needs a confirmation every time, and nothing is sent without it.",
|
|
18574
|
+
deviceType: DeviceType.Lock,
|
|
18575
|
+
outputCap: "group-active",
|
|
18576
|
+
outputField: "active",
|
|
18577
|
+
member: {
|
|
18578
|
+
cap: "lock-control",
|
|
18579
|
+
fieldPath: "state"
|
|
18580
|
+
},
|
|
18581
|
+
activeWhen: ["locked"],
|
|
18582
|
+
commandCaps: ["lock-control"]
|
|
18583
|
+
}),
|
|
18584
|
+
actuator({
|
|
18585
|
+
id: "media-player",
|
|
18586
|
+
summary: "Playing when at least one player is playing, or only when all of them are. Volume is offered only when every player has it. A player that reports nothing is left out, never read as stopped.",
|
|
18587
|
+
deviceType: DeviceType.MediaPlayer,
|
|
18588
|
+
outputCap: "group-active",
|
|
18589
|
+
outputField: "active",
|
|
18590
|
+
member: {
|
|
18591
|
+
cap: "media-player",
|
|
18592
|
+
fieldPath: "state"
|
|
18593
|
+
},
|
|
18594
|
+
activeWhen: ["playing", "buffering"],
|
|
18595
|
+
commandCaps: ["media-player"]
|
|
18596
|
+
}),
|
|
18597
|
+
actuator({
|
|
18598
|
+
id: "climate",
|
|
18599
|
+
summary: "Running when at least one thermostat or air conditioner is not off, or only when all of them are. Target temperature and fan are offered only when every device has them. A device that reports nothing is left out, never read as off.",
|
|
18600
|
+
deviceType: DeviceType.Thermostat,
|
|
18601
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
18602
|
+
outputCap: "group-active",
|
|
18603
|
+
outputField: "active",
|
|
18604
|
+
member: {
|
|
18605
|
+
cap: "climate-control",
|
|
18606
|
+
fieldPath: "mode"
|
|
18607
|
+
},
|
|
18608
|
+
activeWhen: [
|
|
18609
|
+
"heat",
|
|
18610
|
+
"cool",
|
|
18611
|
+
"auto",
|
|
18612
|
+
"heat_cool",
|
|
18613
|
+
"fan_only",
|
|
18614
|
+
"dry"
|
|
18615
|
+
],
|
|
18616
|
+
commandCaps: ["climate-control"]
|
|
18429
18617
|
})
|
|
18430
18618
|
];
|
|
18431
18619
|
function actuator(input) {
|
|
18432
18620
|
return {
|
|
18433
|
-
|
|
18621
|
+
id: input.id,
|
|
18622
|
+
summary: input.summary,
|
|
18623
|
+
deviceType: input.deviceType,
|
|
18624
|
+
outputCap: input.outputCap,
|
|
18625
|
+
outputField: input.outputField,
|
|
18626
|
+
commandCaps: input.commandCaps,
|
|
18434
18627
|
family: "actuator",
|
|
18435
18628
|
role: void 0,
|
|
18436
18629
|
memberDeviceType: input.deviceType,
|
|
18437
|
-
|
|
18630
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
18631
|
+
activeWhen: input.activeWhen,
|
|
18438
18632
|
quantity: "none",
|
|
18439
18633
|
baseUnit: void 0,
|
|
18440
18634
|
aggregates: BINARY_AGGREGATES,
|
|
18441
18635
|
defaultAggregate: "any",
|
|
18442
18636
|
empty: "offline",
|
|
18443
|
-
accept: [
|
|
18444
|
-
cap: "switch",
|
|
18445
|
-
fieldPath: "on"
|
|
18446
|
-
}],
|
|
18637
|
+
accept: [input.member],
|
|
18447
18638
|
companions: [CHANGED]
|
|
18448
18639
|
};
|
|
18449
18640
|
}
|
|
@@ -46107,7 +46298,20 @@ var NcRuleInputSchema = object({
|
|
|
46107
46298
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46108
46299
|
* path does not run Zod defaults.
|
|
46109
46300
|
*/
|
|
46110
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
46301
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
46302
|
+
/**
|
|
46303
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
46304
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
46305
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
46306
|
+
* same mark the Library writes, and records whose notification it was and
|
|
46307
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
46308
|
+
*
|
|
46309
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
46310
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
46311
|
+
* authored before this field meant. An editor turns it off by writing
|
|
46312
|
+
* `false`, so it needs no `clear` key.
|
|
46313
|
+
*/
|
|
46314
|
+
askVerdict: boolean().optional()
|
|
46111
46315
|
});
|
|
46112
46316
|
/**
|
|
46113
46317
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56564,7 +56768,7 @@ var BatteryStatusSchema = object({
|
|
|
56564
56768
|
*/
|
|
56565
56769
|
binary: boolean().optional().meta({ label: "Binary indicator" })
|
|
56566
56770
|
});
|
|
56567
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
|
|
56771
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
|
|
56568
56772
|
method(object({
|
|
56569
56773
|
deviceId: number(),
|
|
56570
56774
|
/** Bound on the wait. Sensible range 3000–10000ms. */
|
|
@@ -58308,7 +58512,13 @@ results: array(object({
|
|
|
58308
58512
|
/** Ms epoch of the attempt. */
|
|
58309
58513
|
at: number()
|
|
58310
58514
|
})) });
|
|
58311
|
-
DeviceType.Switch, DeviceType.Light;
|
|
58515
|
+
DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
58516
|
+
object({
|
|
58517
|
+
active: boolean().meta({ label: "Active" }),
|
|
58518
|
+
/** Ms epoch when the slice was last updated. */
|
|
58519
|
+
lastChangedAt: number()
|
|
58520
|
+
});
|
|
58521
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
|
|
58312
58522
|
object({
|
|
58313
58523
|
/** Members whose current value entered the aggregate. */
|
|
58314
58524
|
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
@@ -58326,7 +58536,7 @@ object({
|
|
|
58326
58536
|
label: "Members"
|
|
58327
58537
|
}))
|
|
58328
58538
|
});
|
|
58329
|
-
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
|
|
58539
|
+
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
58330
58540
|
object({
|
|
58331
58541
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
58332
58542
|
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
@@ -59233,7 +59443,14 @@ DeviceType.Lock, method(object({
|
|
|
59233
59443
|
auth: "protected"
|
|
59234
59444
|
}), method(object({
|
|
59235
59445
|
deviceId: number().int().nonnegative(),
|
|
59236
|
-
code: string().min(1).optional()
|
|
59446
|
+
code: string().min(1).optional(),
|
|
59447
|
+
/**
|
|
59448
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
59449
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
59450
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
59451
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
59452
|
+
*/
|
|
59453
|
+
confirm: boolean().optional()
|
|
59237
59454
|
}), _void(), {
|
|
59238
59455
|
kind: "mutation",
|
|
59239
59456
|
auth: "protected"
|
|
@@ -61153,9 +61370,9 @@ object({
|
|
|
61153
61370
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
61154
61371
|
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
61155
61372
|
/** Voltage in volts. */
|
|
61156
|
-
volts: number().optional().meta(readingMeta("voltage")),
|
|
61373
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
61157
61374
|
/** Current in amperes. */
|
|
61158
|
-
amps: number().optional().meta(readingMeta("current")),
|
|
61375
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
61159
61376
|
/** Apparent power in volt-amperes. */
|
|
61160
61377
|
va: number().optional().meta(readingMeta("apparent-power")),
|
|
61161
61378
|
/** Power factor (0..1) if reported. */
|
|
@@ -61163,7 +61380,23 @@ object({
|
|
|
61163
61380
|
/** Ms epoch when the slice was last updated. */
|
|
61164
61381
|
lastFetchedAt: number()
|
|
61165
61382
|
});
|
|
61166
|
-
|
|
61383
|
+
[
|
|
61384
|
+
DeviceType.Sensor,
|
|
61385
|
+
DeviceType.Switch,
|
|
61386
|
+
DeviceType.Light,
|
|
61387
|
+
DeviceType.Fan,
|
|
61388
|
+
DeviceType.Climate,
|
|
61389
|
+
DeviceType.Thermostat,
|
|
61390
|
+
DeviceType.Humidifier,
|
|
61391
|
+
DeviceType.WaterHeater,
|
|
61392
|
+
DeviceType.MediaPlayer,
|
|
61393
|
+
DeviceType.Washer,
|
|
61394
|
+
DeviceType.Dryer,
|
|
61395
|
+
DeviceType.Dishwasher,
|
|
61396
|
+
DeviceType.Vacuum,
|
|
61397
|
+
DeviceType.LawnMower,
|
|
61398
|
+
DeviceType.PetFeeder
|
|
61399
|
+
].filter((type) => type !== DeviceType.Sensor);
|
|
61167
61400
|
/**
|
|
61168
61401
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
61169
61402
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -63649,7 +63882,7 @@ object({
|
|
|
63649
63882
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
63650
63883
|
lastChangedAt: number()
|
|
63651
63884
|
});
|
|
63652
|
-
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
|
|
63885
|
+
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
|
|
63653
63886
|
deviceId: number(),
|
|
63654
63887
|
on: boolean()
|
|
63655
63888
|
}), _void(), {
|
|
@@ -65341,8 +65574,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
65341
65574
|
kind: "mutation",
|
|
65342
65575
|
auth: "admin"
|
|
65343
65576
|
});
|
|
65577
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
65344
65578
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
65345
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, 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.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
65579
|
+
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;
|
|
65346
65580
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
65347
65581
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
65348
65582
|
new Set(CAP_NAMES);
|
|
@@ -68618,6 +68852,13 @@ Object.freeze({
|
|
|
68618
68852
|
access: "view",
|
|
68619
68853
|
role: "viewer"
|
|
68620
68854
|
},
|
|
68855
|
+
"groupActive.getStatus": {
|
|
68856
|
+
capName: "group-active",
|
|
68857
|
+
capScope: "device",
|
|
68858
|
+
addonId: null,
|
|
68859
|
+
access: "view",
|
|
68860
|
+
role: "viewer"
|
|
68861
|
+
},
|
|
68621
68862
|
"groupCommands.getStatus": {
|
|
68622
68863
|
capName: "group-commands",
|
|
68623
68864
|
capScope: "device",
|
|
@@ -78493,6 +78734,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78493
78734
|
icon: "flame"
|
|
78494
78735
|
}]
|
|
78495
78736
|
},
|
|
78737
|
+
"group-active": {
|
|
78738
|
+
scope: "device",
|
|
78739
|
+
noun: t("Group state", "Stato del gruppo")
|
|
78740
|
+
},
|
|
78496
78741
|
"group-commands": {
|
|
78497
78742
|
scope: "device",
|
|
78498
78743
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -80039,6 +80284,11 @@ Object.freeze({
|
|
|
80039
80284
|
form: "single",
|
|
80040
80285
|
optional: false
|
|
80041
80286
|
}],
|
|
80287
|
+
"groupActive.getStatus": [{
|
|
80288
|
+
name: "deviceId",
|
|
80289
|
+
form: "single",
|
|
80290
|
+
optional: false
|
|
80291
|
+
}],
|
|
80042
80292
|
"groupCommands.getStatus": [{
|
|
80043
80293
|
name: "deviceId",
|
|
80044
80294
|
form: "single",
|
|
@@ -83187,7 +83437,7 @@ var AgentUIAddon = class extends BaseAddon {
|
|
|
83187
83437
|
capability: adminUiCapability,
|
|
83188
83438
|
provider: {
|
|
83189
83439
|
getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
|
|
83190
|
-
getVersion: async () => ({ version: "1.2.
|
|
83440
|
+
getVersion: async () => ({ version: "1.2.279" })
|
|
83191
83441
|
}
|
|
83192
83442
|
}];
|
|
83193
83443
|
}
|
|
@@ -0,0 +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-BN-6thGi.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};
|
|
@@ -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,_ as ee,c as o,d as te,g as ne,h as s,l as c,p as re,t as l,x as u,y as d}from"./index-DD00yN8O.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=a(`hexagon`,[[`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`,key:`yt0hxn`}]]),oe=a(`mic-off`,[[`path`,{d:`M12 19v3`,key:`npa21l`}],[`path`,{d:`M15 9.34V5a3 3 0 0 0-5.68-1.33`,key:`1gzdoj`}],[`path`,{d:`M16.95 16.95A7 7 0 0 1 5 12v-2`,key:`cqa7eg`}],[`path`,{d:`M18.89 13.23A7 7 0 0 0 19 12v-2`,key:`16hl24`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M9 9v3a3 3 0 0 0 5.12 2.12`,key:`r2i35w`}]]),f=n(i(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function se(e){let t=e.supportsAudioMute===!0,n=t?e.audioEnabled:null,r=e.pendingOn??null;return{label:p,supported:t,on:n===null?null:r??n,available:t&&n!==null,costWhenOff:m}}function ce(e){return!e.available||e.on===null?null:{enabled:!e.on}}var h=n(e(),1),le=120,ue=`privacy-mask`,g=`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`,_=`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 v(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function de(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function fe(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(JSON.stringify(e[n])!==JSON.stringify(t[n]))return!1;return!0}function y({deviceId:e}){let n=t(),i=te(s().trpcClient,e),[a,p]=(0,f.useState)(null),[m,y]=(0,f.useState)(!1),[b,x]=(0,f.useState)(!1),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(null),[E,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(!1),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(null),[pe,P]=(0,f.useState)(null),[F,I]=(0,f.useState)(null),[L,R]=(0,f.useState)(null),z=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!i)return;let e=!1;return z.current=!1,p(null),y(!1),x(!1),C(null),T(null),k(!1),j(null),N(null),P(null),I(null),R(null),(async()=>{try{let t=await i.privacyMask?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(p(t),z.current)return;let n=await i.privacyMask?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(n.regions===null){x(!0),P(n.audioEnabled);return}z.current=!0;let r={enabled:n.enabled,regions:n.regions};T(r),C(r),P(n.audioEnabled)}catch(t){if(e)return;c(t)?y(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[i]);let B=(0,f.useMemo)(()=>S!==null&&w!==null&&(S.enabled!==w.enabled||!fe(S.regions,w.regions)),[S,w]),V=S?S.regions.length:0,H=a?a.maxRegions:0,U=(0,f.useRef)(0);(0,f.useEffect)(()=>{U.current=H},[H]);let W=H>0&&V>=H,me=(0,f.useCallback)(()=>{C(e=>e&&{...e,enabled:!e.enabled})},[]),G=(0,f.useCallback)(e=>{j(typeof e==`number`?e:null)},[]),K=(0,f.useMemo)(()=>S?S.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})})):[],[S,n]),q=(0,f.useCallback)((e,t)=>{let n=v(t);n&&C(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),J=(0,f.useCallback)(e=>{let t=v(e);t&&(N(null),C(e=>{if(!e)return e;let n=U.current;if(n>0&&e.regions.length>=n)return e;let r=de(e.regions),i={id:r,enabled:!0,shape:t};return j(r),{...e,regions:[...e.regions,i]}}))},[]),Y=(0,f.useCallback)(e=>{k(!0),j(null),N(e)},[]),he=(0,f.useCallback)(e=>{k(!0),N(null),j(e)},[]),ge=(0,f.useCallback)(e=>{j(t=>t===e?null:t),C(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),_e=(0,f.useCallback)(()=>{N(null),j(null),w&&C({enabled:w.enabled,regions:w.regions})},[w]),X=(0,f.useCallback)(async()=>{if(!(!i||!S)){D(!0);try{await i.privacyMask?.setMask({patch:{enabled:S.enabled,regions:[...S.regions]}});let e=await i.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};T(t),C(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{D(!1)}}},[i,S]),Z=se({supportsAudioMute:a===null?null:a.supportsAudioMute,audioEnabled:pe,pendingOn:F}),ve=(0,f.useCallback)(async()=>{let e=ce(Z);if(!(!i||e===null)){I(e.enabled),R(null);try{await i.privacyMask?.setAudioEnabled(e);let t=await i.privacyMask?.getStatus({});t&&P(t.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),R(e instanceof Error?e.message:String(e))}finally{I(null)}}},[i,Z]),ye=(0,f.useCallback)(()=>{k(e=>(e&&(N(null),j(null)),!e))},[]),Q=a?.supportedShapes??[];l((0,f.useMemo)(()=>O&&!m&&a&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:a.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,a,S,K,Q,A,G,q,J,M]));let be=a?.supportedShapes.includes(`rect`)??!1,xe=a?.supportedShapes.includes(`polygon`)??!1,$=a!==null&&(a.maxRegions<=0||a.supportedShapes.length===0),Se=!m&&!$&&!b&&a!==null&&S!==null;return i?(0,h.jsx)(re,{title:n(`uiLibrary.privacyMaskSettings.title`,`Privacy Mask`),icon:(0,h.jsx)(u,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[m||$?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unsupported`,`This camera doesn't support an on-board privacy mask.`)}):b?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unreadable`,`This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable.`)}):Se?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.privacyMaskSettings.help`,fallback:`Toggle <0>Edit mask</0> to draw blanked-out zones on the live frame, then <1>Save</1> to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it.`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`edit`),(0,h.jsx)(`strong`,{className:`text-foreground`},`save`)]})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:ye,disabled:E,"aria-pressed":O,className:O?_:g,children:O?n(`uiLibrary.privacyMaskSettings.doneEditing`,`Done editing`):n(`uiLibrary.privacyMaskSettings.editMask`,`Edit mask`)}),(0,h.jsx)(`button`,{type:`button`,onClick:me,disabled:E,"aria-pressed":S.enabled,className:S.enabled?_:g,children:S.enabled?n(`uiLibrary.privacyMaskSettings.maskOn`,`Mask on`):n(`uiLibrary.privacyMaskSettings.maskOff`,`Mask off`)}),be&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`rect`),disabled:E||W,"aria-pressed":M===`rect`,className:M===`rect`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addRect`,`Add a rectangle zone`),children:n(`uiLibrary.privacyMaskSettings.rectButton`,`+ Rect`)}),xe&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`polygon`),disabled:E||W,"aria-pressed":M===`polygon`,className:M===`polygon`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addPolygon`,`Add a polygon zone`),children:n(`uiLibrary.privacyMaskSettings.polygonButton`,`+ Polygon`)}),(0,h.jsx)(`span`,{className:`${o} ml-1 tabular-nums`,children:n(`uiLibrary.privacyMaskSettings.zoneCount`,`{{active}} / {{max}} zones`,{active:V,max:H})}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:_e,disabled:E||!B,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.privacyMaskSettings.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void X(),disabled:E||!B,className:_,children:E?n(`uiLibrary.privacyMaskSettings.saving`,`Saving…`):n(`uiLibrary.privacyMaskSettings.save`,`Save`)})]}),V>0?(0,h.jsx)(`div`,{className:`flex flex-col gap-1`,children:S.regions.map(e=>{let t=A===e.id,r=e.shape.kind===`polygon`?ae:ee;return(0,h.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${t?`border-primary/50 bg-primary/10`:`border-border bg-surface`}`,children:[(0,h.jsxs)(`button`,{type:`button`,onClick:()=>he(e.id),disabled:E,className:`flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors`,children:[(0,h.jsx)(r,{className:`h-3.5 w-3.5 shrink-0`}),(0,h.jsx)(`span`,{children:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})}),(0,h.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>ge(e.id),disabled:E,"aria-label":n(`uiLibrary.privacyMaskSettings.deleteZoneN`,`Delete zone {{id}}`,{id:e.id}),title:n(`uiLibrary.privacyMaskSettings.deleteZone`,`Delete zone`),className:`inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors`,children:(0,h.jsx)(ne,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.loading`,`Loading the camera's privacy mask…`)}),Z.supported&&(0,h.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,h.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||F!==null,onClick:()=>void ve(),title:Z.on===null?n(`uiLibrary.privacyMaskSettings.micTitleUnknown`,`Microphone — state unknown; the camera did not answer.`):Z.on?n(`uiLibrary.privacyMaskSettings.micTitleOn`,`The camera is capturing sound. Turning it off: {{cost}}`,{cost:Z.costWhenOff}):n(`uiLibrary.privacyMaskSettings.micTitleOff`,`The camera is silent. {{cost}}`,{cost:Z.costWhenOff}),className:Z.on===!1||Z.on===null?g:_,children:(0,h.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,h.jsx)(oe,{className:`h-3.5 w-3.5`}):(0,h.jsx)(d,{className:`h-3.5 w-3.5`}),Z.on===null?n(`uiLibrary.privacyMaskSettings.micUnknown`,`Microphone unknown`):Z.on?n(`uiLibrary.privacyMaskSettings.micOn`,`Microphone on`):n(`uiLibrary.privacyMaskSettings.micOff`,`Microphone off`)]})}),(0,h.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:L===null?Z.on===null?n(`uiLibrary.privacyMaskSettings.micNotReported`,`The camera did not report its microphone, so this state is unknown.`):Z.costWhenOff:n(`uiLibrary.privacyMaskSettings.writeFailed`,`Write failed: {{reason}}`,{reason:L})})]})]})}):null}export{y as PrivacyMaskSettings};
|
|
1
|
+
import{a as e,i as t,l as n,n as r,o as i}from"./scheduler-0XZJE03d.js";import{S as ee,_ as a,b as te,c as o,d as ne,g as s,l as c,p as re,t as l,v as u,w as d}from"./index-BN-6thGi.js";import{MaskShapeCanvas as ie}from"./MaskShapeCanvas-BTvl6Rin.js";var ae=d(`hexagon`,[[`path`,{d:`M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z`,key:`yt0hxn`}]]),oe=d(`mic-off`,[[`path`,{d:`M12 19v3`,key:`npa21l`}],[`path`,{d:`M15 9.34V5a3 3 0 0 0-5.68-1.33`,key:`1gzdoj`}],[`path`,{d:`M16.95 16.95A7 7 0 0 1 5 12v-2`,key:`cqa7eg`}],[`path`,{d:`M18.89 13.23A7 7 0 0 0 19 12v-2`,key:`16hl24`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M9 9v3a3 3 0 0 0 5.12 2.12`,key:`r2i35w`}]]),f=n(i(),1),p=`Microphone`,m=`The camera encodes no audio at all — live view, recordings, exports and the audio analyzer are all silent.`;function se(e){let t=e.supportsAudioMute===!0,n=t?e.audioEnabled:null,r=e.pendingOn??null;return{label:p,supported:t,on:n===null?null:r??n,available:t&&n!==null,costWhenOff:m}}function ce(e){return!e.available||e.on===null?null:{enabled:!e.on}}var h=n(e(),1),le=120,ue=`privacy-mask`,g=`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`,_=`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 v(e){return e.kind===`rect`||e.kind===`polygon`?e:null}function de(e){let t=new Set(e.map(e=>e.id)),n=0;for(;t.has(n);)n+=1;return n}function fe(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1)if(JSON.stringify(e[n])!==JSON.stringify(t[n]))return!1;return!0}function y({deviceId:e}){let n=t(),i=ne(s().trpcClient,e),[d,p]=(0,f.useState)(null),[m,y]=(0,f.useState)(!1),[b,x]=(0,f.useState)(!1),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(null),[E,D]=(0,f.useState)(!1),[O,k]=(0,f.useState)(!1),[A,j]=(0,f.useState)(null),[M,N]=(0,f.useState)(null),[pe,P]=(0,f.useState)(null),[F,I]=(0,f.useState)(null),[L,R]=(0,f.useState)(null),z=(0,f.useRef)(!1);(0,f.useEffect)(()=>{if(!i)return;let e=!1;return z.current=!1,p(null),y(!1),x(!1),C(null),T(null),k(!1),j(null),N(null),P(null),I(null),R(null),(async()=>{try{let t=await i.privacyMask?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(p(t),z.current)return;let n=await i.privacyMask?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);if(n.regions===null){x(!0),P(n.audioEnabled);return}z.current=!0;let r={enabled:n.enabled,regions:n.regions};T(r),C(r),P(n.audioEnabled)}catch(t){if(e)return;c(t)?y(!0):console.error(`Privacy Mask load failed`,t)}})(),()=>{e=!0}},[i]);let B=(0,f.useMemo)(()=>S!==null&&w!==null&&(S.enabled!==w.enabled||!fe(S.regions,w.regions)),[S,w]),V=S?S.regions.length:0,H=d?d.maxRegions:0,U=(0,f.useRef)(0);(0,f.useEffect)(()=>{U.current=H},[H]);let W=H>0&&V>=H,me=(0,f.useCallback)(()=>{C(e=>e&&{...e,enabled:!e.enabled})},[]),G=(0,f.useCallback)(e=>{j(typeof e==`number`?e:null)},[]),K=(0,f.useMemo)(()=>S?S.regions.map(e=>({id:e.id,shape:e.shape,enabled:e.enabled,label:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})})):[],[S,n]),q=(0,f.useCallback)((e,t)=>{let n=v(t);n&&C(t=>t&&{...t,regions:t.regions.map(t=>t.id===e?{...t,shape:n}:t)})},[]),J=(0,f.useCallback)(e=>{let t=v(e);t&&(N(null),C(e=>{if(!e)return e;let n=U.current;if(n>0&&e.regions.length>=n)return e;let r=de(e.regions),i={id:r,enabled:!0,shape:t};return j(r),{...e,regions:[...e.regions,i]}}))},[]),Y=(0,f.useCallback)(e=>{k(!0),j(null),N(e)},[]),he=(0,f.useCallback)(e=>{k(!0),N(null),j(e)},[]),ge=(0,f.useCallback)(e=>{j(t=>t===e?null:t),C(t=>t&&{...t,regions:t.regions.filter(t=>t.id!==e)})},[]),_e=(0,f.useCallback)(()=>{N(null),j(null),w&&C({enabled:w.enabled,regions:w.regions})},[w]),X=(0,f.useCallback)(async()=>{if(!(!i||!S)){D(!0);try{await i.privacyMask?.setMask({patch:{enabled:S.enabled,regions:[...S.regions]}});let e=await i.privacyMask?.getStatus({});if(e&&e.regions!==null){let t={enabled:e.enabled,regions:e.regions};T(t),C(t)}}catch(e){console.error(`Privacy Mask save failed`,e)}finally{D(!1)}}},[i,S]),Z=se({supportsAudioMute:d===null?null:d.supportsAudioMute,audioEnabled:pe,pendingOn:F}),ve=(0,f.useCallback)(async()=>{let e=ce(Z);if(!(!i||e===null)){I(e.enabled),R(null);try{await i.privacyMask?.setAudioEnabled(e);let t=await i.privacyMask?.getStatus({});t&&P(t.audioEnabled)}catch(e){console.error(`Privacy microphone write failed — reverting`,e),R(e instanceof Error?e.message:String(e))}finally{I(null)}}},[i,Z]),ye=(0,f.useCallback)(()=>{k(e=>(e&&(N(null),j(null)),!e))},[]),Q=d?.supportedShapes??[];l((0,f.useMemo)(()=>O&&!m&&d&&S?{id:ue,order:le,node:(0,h.jsx)(ie,{transparent:!0,items:K,supportedShapes:Q,polygonVertices:d.polygonVertices,selectedId:A,onSelect:G,onShapeChange:q,onDrawComplete:J,drawingKind:M})}:null,[O,m,d,S,K,Q,A,G,q,J,M]));let be=d?.supportedShapes.includes(`rect`)??!1,xe=d?.supportedShapes.includes(`polygon`)??!1,$=d!==null&&(d.maxRegions<=0||d.supportedShapes.length===0),Se=!m&&!$&&!b&&d!==null&&S!==null;return i?(0,h.jsx)(re,{title:n(`uiLibrary.privacyMaskSettings.title`,`Privacy Mask`),icon:(0,h.jsx)(ee,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,h.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[m||$?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unsupported`,`This camera doesn't support an on-board privacy mask.`)}):b?(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.unreadable`,`This camera hasn't answered, so what it currently masks is unknown — the editor stays closed rather than showing an empty frame you could save over. A battery camera answers once it wakes; otherwise check that it is reachable.`)}):Se?(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:(0,h.jsx)(r,{uiKey:`uiLibrary.privacyMaskSettings.help`,fallback:`Toggle <0>Edit mask</0> to draw blanked-out zones on the live frame, then <1>Save</1> to push them to the camera. Drag a rectangle to move, its corner to resize; drag polygon vertices, click an edge midpoint to add one, or right-click a vertex to remove it.`,components:[(0,h.jsx)(`strong`,{className:`text-foreground`},`edit`),(0,h.jsx)(`strong`,{className:`text-foreground`},`save`)]})}),(0,h.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,h.jsx)(`button`,{type:`button`,onClick:ye,disabled:E,"aria-pressed":O,className:O?_:g,children:O?n(`uiLibrary.privacyMaskSettings.doneEditing`,`Done editing`):n(`uiLibrary.privacyMaskSettings.editMask`,`Edit mask`)}),(0,h.jsx)(`button`,{type:`button`,onClick:me,disabled:E,"aria-pressed":S.enabled,className:S.enabled?_:g,children:S.enabled?n(`uiLibrary.privacyMaskSettings.maskOn`,`Mask on`):n(`uiLibrary.privacyMaskSettings.maskOff`,`Mask off`)}),be&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`rect`),disabled:E||W,"aria-pressed":M===`rect`,className:M===`rect`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addRect`,`Add a rectangle zone`),children:n(`uiLibrary.privacyMaskSettings.rectButton`,`+ Rect`)}),xe&&(0,h.jsx)(`button`,{type:`button`,onClick:()=>Y(`polygon`),disabled:E||W,"aria-pressed":M===`polygon`,className:M===`polygon`?_:g,title:W?n(`uiLibrary.privacyMaskSettings.maxZones`,`Maximum zones reached`):n(`uiLibrary.privacyMaskSettings.addPolygon`,`Add a polygon zone`),children:n(`uiLibrary.privacyMaskSettings.polygonButton`,`+ Polygon`)}),(0,h.jsx)(`span`,{className:`${o} ml-1 tabular-nums`,children:n(`uiLibrary.privacyMaskSettings.zoneCount`,`{{active}} / {{max}} zones`,{active:V,max:H})}),(0,h.jsx)(`span`,{className:`flex-1`}),(0,h.jsx)(`button`,{type:`button`,onClick:_e,disabled:E||!B,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.privacyMaskSettings.revert`,`Revert`)}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>void X(),disabled:E||!B,className:_,children:E?n(`uiLibrary.privacyMaskSettings.saving`,`Saving…`):n(`uiLibrary.privacyMaskSettings.save`,`Save`)})]}),V>0?(0,h.jsx)(`div`,{className:`flex flex-col gap-1`,children:S.regions.map(e=>{let t=A===e.id,r=e.shape.kind===`polygon`?ae:u;return(0,h.jsxs)(`div`,{className:`flex items-center gap-2 rounded-md border px-2 py-1 transition-colors ${t?`border-primary/50 bg-primary/10`:`border-border bg-surface`}`,children:[(0,h.jsxs)(`button`,{type:`button`,onClick:()=>he(e.id),disabled:E,className:`flex flex-1 items-center gap-2 text-left text-[11px] font-medium text-foreground-subtle hover:text-foreground disabled:opacity-40 transition-colors`,children:[(0,h.jsx)(r,{className:`h-3.5 w-3.5 shrink-0`}),(0,h.jsx)(`span`,{children:n(`uiLibrary.privacyMaskSettings.zoneN`,`Zone {{id}}`,{id:e.id})}),(0,h.jsx)(`span`,{className:`text-foreground-faint capitalize`,children:e.shape.kind})]}),(0,h.jsx)(`button`,{type:`button`,onClick:()=>ge(e.id),disabled:E,"aria-label":n(`uiLibrary.privacyMaskSettings.deleteZoneN`,`Delete zone {{id}}`,{id:e.id}),title:n(`uiLibrary.privacyMaskSettings.deleteZone`,`Delete zone`),className:`inline-flex h-6 w-6 items-center justify-center rounded border border-border bg-surface text-foreground-subtle hover:border-red-400/40 hover:bg-red-500/10 hover:text-red-400 disabled:opacity-40 transition-colors`,children:(0,h.jsx)(a,{className:`h-3.5 w-3.5`})})]},e.id)})}):null]}):(0,h.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.privacyMaskSettings.loading`,`Loading the camera's privacy mask…`)}),Z.supported&&(0,h.jsxs)(`div`,{className:`flex items-start gap-3 rounded-md border border-border bg-surface px-2 py-2`,children:[(0,h.jsx)(`button`,{type:`button`,role:`switch`,"aria-label":Z.label,"aria-checked":Z.on===!0,disabled:!Z.available||F!==null,onClick:()=>void ve(),title:Z.on===null?n(`uiLibrary.privacyMaskSettings.micTitleUnknown`,`Microphone — state unknown; the camera did not answer.`):Z.on?n(`uiLibrary.privacyMaskSettings.micTitleOn`,`The camera is capturing sound. Turning it off: {{cost}}`,{cost:Z.costWhenOff}):n(`uiLibrary.privacyMaskSettings.micTitleOff`,`The camera is silent. {{cost}}`,{cost:Z.costWhenOff}),className:Z.on===!1||Z.on===null?g:_,children:(0,h.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,children:[Z.on===!1?(0,h.jsx)(oe,{className:`h-3.5 w-3.5`}):(0,h.jsx)(te,{className:`h-3.5 w-3.5`}),Z.on===null?n(`uiLibrary.privacyMaskSettings.micUnknown`,`Microphone unknown`):Z.on?n(`uiLibrary.privacyMaskSettings.micOn`,`Microphone on`):n(`uiLibrary.privacyMaskSettings.micOff`,`Microphone off`)]})}),(0,h.jsx)(`p`,{className:`text-[10px] text-foreground-subtle flex-1 leading-relaxed`,children:L===null?Z.on===null?n(`uiLibrary.privacyMaskSettings.micNotReported`,`The camera did not report its microphone, so this state is unknown.`):Z.costWhenOff:n(`uiLibrary.privacyMaskSettings.writeFailed`,`Write failed: {{reason}}`,{reason:L})})]})]})}):null}export{y as PrivacyMaskSettings};
|