@camstack/addon-agent-ui 1.2.277 → 1.2.278
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 +244 -25
- package/dist/assets/{MotionZonesSettings-uUbAkmjZ.js → MotionZonesSettings-Dgi9UrKL.js} +1 -1
- package/dist/assets/{PrivacyMaskSettings-B9TXHomV.js → PrivacyMaskSettings-BX9fG-ou.js} +1 -1
- package/dist/assets/{SceneMonitorEditor-CEf8URN2.js → SceneMonitorEditor-BFSsQU7D.js} +1 -1
- package/dist/assets/index-Cf9eOmk0.js +112 -0
- package/dist/index.html +1 -1
- package/package.json +1 -1
- 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",
|
|
@@ -18178,13 +18240,17 @@ Object.freeze({
|
|
|
18178
18240
|
latchedAt: {
|
|
18179
18241
|
kind: "fixed",
|
|
18180
18242
|
result: ["number", "null"]
|
|
18243
|
+
},
|
|
18244
|
+
schmitt: {
|
|
18245
|
+
kind: "fixed",
|
|
18246
|
+
result: ["boolean", "null"]
|
|
18181
18247
|
}
|
|
18182
18248
|
});
|
|
18183
18249
|
/**
|
|
18184
18250
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18185
18251
|
* Data, not a form default written into the block.
|
|
18186
18252
|
*/
|
|
18187
|
-
var NOW$
|
|
18253
|
+
var NOW$2 = {
|
|
18188
18254
|
fieldPath: "lastFetchedAt",
|
|
18189
18255
|
expr: "now"
|
|
18190
18256
|
};
|
|
@@ -18198,6 +18264,8 @@ function numeric(input) {
|
|
|
18198
18264
|
family: "numeric",
|
|
18199
18265
|
deviceType: DeviceType.Sensor,
|
|
18200
18266
|
memberDeviceType: DeviceType.Sensor,
|
|
18267
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18268
|
+
activeWhen: void 0,
|
|
18201
18269
|
commandCaps: [],
|
|
18202
18270
|
baseUnit: baseUnitOf(input.quantity),
|
|
18203
18271
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18213,6 +18281,8 @@ function binary(input) {
|
|
|
18213
18281
|
family: "binary",
|
|
18214
18282
|
deviceType: DeviceType.Sensor,
|
|
18215
18283
|
memberDeviceType: DeviceType.Sensor,
|
|
18284
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18285
|
+
activeWhen: void 0,
|
|
18216
18286
|
commandCaps: [],
|
|
18217
18287
|
quantity: "none",
|
|
18218
18288
|
baseUnit: void 0,
|
|
@@ -18230,7 +18300,7 @@ var GROUP_TEMPLATES = [
|
|
|
18230
18300
|
quantity: "temperature",
|
|
18231
18301
|
defaultAggregate: "mean",
|
|
18232
18302
|
empty: "null",
|
|
18233
|
-
companions: [NOW$
|
|
18303
|
+
companions: [NOW$2]
|
|
18234
18304
|
}),
|
|
18235
18305
|
numeric({
|
|
18236
18306
|
id: "humidity",
|
|
@@ -18241,7 +18311,7 @@ var GROUP_TEMPLATES = [
|
|
|
18241
18311
|
quantity: "humidity",
|
|
18242
18312
|
defaultAggregate: "mean",
|
|
18243
18313
|
empty: "null",
|
|
18244
|
-
companions: [NOW$
|
|
18314
|
+
companions: [NOW$2]
|
|
18245
18315
|
}),
|
|
18246
18316
|
numeric({
|
|
18247
18317
|
id: "pressure",
|
|
@@ -18252,7 +18322,7 @@ var GROUP_TEMPLATES = [
|
|
|
18252
18322
|
quantity: "pressure",
|
|
18253
18323
|
defaultAggregate: "mean",
|
|
18254
18324
|
empty: "null",
|
|
18255
|
-
companions: [NOW$
|
|
18325
|
+
companions: [NOW$2]
|
|
18256
18326
|
}),
|
|
18257
18327
|
numeric({
|
|
18258
18328
|
id: "illuminance",
|
|
@@ -18263,7 +18333,7 @@ var GROUP_TEMPLATES = [
|
|
|
18263
18333
|
quantity: "illuminance",
|
|
18264
18334
|
defaultAggregate: "mean",
|
|
18265
18335
|
empty: "null",
|
|
18266
|
-
companions: [NOW$
|
|
18336
|
+
companions: [NOW$2]
|
|
18267
18337
|
}),
|
|
18268
18338
|
numeric({
|
|
18269
18339
|
id: "power",
|
|
@@ -18274,7 +18344,7 @@ var GROUP_TEMPLATES = [
|
|
|
18274
18344
|
quantity: "power",
|
|
18275
18345
|
defaultAggregate: "sum",
|
|
18276
18346
|
empty: "omit",
|
|
18277
|
-
companions: [NOW$
|
|
18347
|
+
companions: [NOW$2]
|
|
18278
18348
|
}),
|
|
18279
18349
|
numeric({
|
|
18280
18350
|
id: "energy",
|
|
@@ -18285,7 +18355,7 @@ var GROUP_TEMPLATES = [
|
|
|
18285
18355
|
quantity: "energy",
|
|
18286
18356
|
defaultAggregate: "sum",
|
|
18287
18357
|
empty: "omit",
|
|
18288
|
-
companions: [NOW$
|
|
18358
|
+
companions: [NOW$2]
|
|
18289
18359
|
}),
|
|
18290
18360
|
numeric({
|
|
18291
18361
|
id: "battery",
|
|
@@ -18320,7 +18390,7 @@ var GROUP_TEMPLATES = [
|
|
|
18320
18390
|
quantity: "device",
|
|
18321
18391
|
defaultAggregate: "mean",
|
|
18322
18392
|
empty: "null",
|
|
18323
|
-
companions: [NOW$
|
|
18393
|
+
companions: [NOW$2]
|
|
18324
18394
|
}),
|
|
18325
18395
|
binary({
|
|
18326
18396
|
id: "motion",
|
|
@@ -18414,6 +18484,11 @@ var GROUP_TEMPLATES = [
|
|
|
18414
18484
|
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
18485
|
deviceType: DeviceType.Switch,
|
|
18416
18486
|
outputCap: "switch",
|
|
18487
|
+
outputField: "on",
|
|
18488
|
+
member: {
|
|
18489
|
+
cap: "switch",
|
|
18490
|
+
fieldPath: "on"
|
|
18491
|
+
},
|
|
18417
18492
|
commandCaps: ["switch"]
|
|
18418
18493
|
}),
|
|
18419
18494
|
actuator({
|
|
@@ -18421,29 +18496,130 @@ var GROUP_TEMPLATES = [
|
|
|
18421
18496
|
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
18497
|
deviceType: DeviceType.Light,
|
|
18423
18498
|
outputCap: "switch",
|
|
18499
|
+
outputField: "on",
|
|
18500
|
+
member: {
|
|
18501
|
+
cap: "switch",
|
|
18502
|
+
fieldPath: "on"
|
|
18503
|
+
},
|
|
18424
18504
|
commandCaps: [
|
|
18425
18505
|
"switch",
|
|
18426
18506
|
"brightness",
|
|
18427
18507
|
"color"
|
|
18428
18508
|
]
|
|
18509
|
+
}),
|
|
18510
|
+
actuator({
|
|
18511
|
+
id: "fan",
|
|
18512
|
+
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.",
|
|
18513
|
+
deviceType: DeviceType.Fan,
|
|
18514
|
+
outputCap: "switch",
|
|
18515
|
+
outputField: "on",
|
|
18516
|
+
member: {
|
|
18517
|
+
cap: "switch",
|
|
18518
|
+
fieldPath: "on"
|
|
18519
|
+
},
|
|
18520
|
+
commandCaps: ["switch", "fan-control"]
|
|
18521
|
+
}),
|
|
18522
|
+
actuator({
|
|
18523
|
+
id: "cover",
|
|
18524
|
+
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.",
|
|
18525
|
+
deviceType: DeviceType.Cover,
|
|
18526
|
+
outputCap: "group-active",
|
|
18527
|
+
outputField: "active",
|
|
18528
|
+
member: {
|
|
18529
|
+
cap: "cover",
|
|
18530
|
+
fieldPath: "state"
|
|
18531
|
+
},
|
|
18532
|
+
activeWhen: [
|
|
18533
|
+
"open",
|
|
18534
|
+
"opening",
|
|
18535
|
+
"stopped"
|
|
18536
|
+
],
|
|
18537
|
+
commandCaps: ["cover"]
|
|
18538
|
+
}),
|
|
18539
|
+
actuator({
|
|
18540
|
+
id: "valve",
|
|
18541
|
+
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.",
|
|
18542
|
+
deviceType: DeviceType.Valve,
|
|
18543
|
+
outputCap: "group-active",
|
|
18544
|
+
outputField: "active",
|
|
18545
|
+
member: {
|
|
18546
|
+
cap: "valve",
|
|
18547
|
+
fieldPath: "state"
|
|
18548
|
+
},
|
|
18549
|
+
activeWhen: [
|
|
18550
|
+
"open",
|
|
18551
|
+
"opening",
|
|
18552
|
+
"stopped"
|
|
18553
|
+
],
|
|
18554
|
+
commandCaps: ["valve"]
|
|
18555
|
+
}),
|
|
18556
|
+
actuator({
|
|
18557
|
+
id: "lock",
|
|
18558
|
+
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.",
|
|
18559
|
+
deviceType: DeviceType.Lock,
|
|
18560
|
+
outputCap: "group-active",
|
|
18561
|
+
outputField: "active",
|
|
18562
|
+
member: {
|
|
18563
|
+
cap: "lock-control",
|
|
18564
|
+
fieldPath: "state"
|
|
18565
|
+
},
|
|
18566
|
+
activeWhen: ["locked"],
|
|
18567
|
+
commandCaps: ["lock-control"]
|
|
18568
|
+
}),
|
|
18569
|
+
actuator({
|
|
18570
|
+
id: "media-player",
|
|
18571
|
+
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.",
|
|
18572
|
+
deviceType: DeviceType.MediaPlayer,
|
|
18573
|
+
outputCap: "group-active",
|
|
18574
|
+
outputField: "active",
|
|
18575
|
+
member: {
|
|
18576
|
+
cap: "media-player",
|
|
18577
|
+
fieldPath: "state"
|
|
18578
|
+
},
|
|
18579
|
+
activeWhen: ["playing", "buffering"],
|
|
18580
|
+
commandCaps: ["media-player"]
|
|
18581
|
+
}),
|
|
18582
|
+
actuator({
|
|
18583
|
+
id: "climate",
|
|
18584
|
+
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.",
|
|
18585
|
+
deviceType: DeviceType.Thermostat,
|
|
18586
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
18587
|
+
outputCap: "group-active",
|
|
18588
|
+
outputField: "active",
|
|
18589
|
+
member: {
|
|
18590
|
+
cap: "climate-control",
|
|
18591
|
+
fieldPath: "mode"
|
|
18592
|
+
},
|
|
18593
|
+
activeWhen: [
|
|
18594
|
+
"heat",
|
|
18595
|
+
"cool",
|
|
18596
|
+
"auto",
|
|
18597
|
+
"heat_cool",
|
|
18598
|
+
"fan_only",
|
|
18599
|
+
"dry"
|
|
18600
|
+
],
|
|
18601
|
+
commandCaps: ["climate-control"]
|
|
18429
18602
|
})
|
|
18430
18603
|
];
|
|
18431
18604
|
function actuator(input) {
|
|
18432
18605
|
return {
|
|
18433
|
-
|
|
18606
|
+
id: input.id,
|
|
18607
|
+
summary: input.summary,
|
|
18608
|
+
deviceType: input.deviceType,
|
|
18609
|
+
outputCap: input.outputCap,
|
|
18610
|
+
outputField: input.outputField,
|
|
18611
|
+
commandCaps: input.commandCaps,
|
|
18434
18612
|
family: "actuator",
|
|
18435
18613
|
role: void 0,
|
|
18436
18614
|
memberDeviceType: input.deviceType,
|
|
18437
|
-
|
|
18615
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
18616
|
+
activeWhen: input.activeWhen,
|
|
18438
18617
|
quantity: "none",
|
|
18439
18618
|
baseUnit: void 0,
|
|
18440
18619
|
aggregates: BINARY_AGGREGATES,
|
|
18441
18620
|
defaultAggregate: "any",
|
|
18442
18621
|
empty: "offline",
|
|
18443
|
-
accept: [
|
|
18444
|
-
cap: "switch",
|
|
18445
|
-
fieldPath: "on"
|
|
18446
|
-
}],
|
|
18622
|
+
accept: [input.member],
|
|
18447
18623
|
companions: [CHANGED]
|
|
18448
18624
|
};
|
|
18449
18625
|
}
|
|
@@ -46107,7 +46283,20 @@ var NcRuleInputSchema = object({
|
|
|
46107
46283
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46108
46284
|
* path does not run Zod defaults.
|
|
46109
46285
|
*/
|
|
46110
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
46286
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
46287
|
+
/**
|
|
46288
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
46289
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
46290
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
46291
|
+
* same mark the Library writes, and records whose notification it was and
|
|
46292
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
46293
|
+
*
|
|
46294
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
46295
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
46296
|
+
* authored before this field meant. An editor turns it off by writing
|
|
46297
|
+
* `false`, so it needs no `clear` key.
|
|
46298
|
+
*/
|
|
46299
|
+
askVerdict: boolean().optional()
|
|
46111
46300
|
});
|
|
46112
46301
|
/**
|
|
46113
46302
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -58308,7 +58497,13 @@ results: array(object({
|
|
|
58308
58497
|
/** Ms epoch of the attempt. */
|
|
58309
58498
|
at: number()
|
|
58310
58499
|
})) });
|
|
58311
|
-
DeviceType.Switch, DeviceType.Light;
|
|
58500
|
+
DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
58501
|
+
object({
|
|
58502
|
+
active: boolean().meta({ label: "Active" }),
|
|
58503
|
+
/** Ms epoch when the slice was last updated. */
|
|
58504
|
+
lastChangedAt: number()
|
|
58505
|
+
});
|
|
58506
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
|
|
58312
58507
|
object({
|
|
58313
58508
|
/** Members whose current value entered the aggregate. */
|
|
58314
58509
|
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
@@ -58326,7 +58521,7 @@ object({
|
|
|
58326
58521
|
label: "Members"
|
|
58327
58522
|
}))
|
|
58328
58523
|
});
|
|
58329
|
-
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
|
|
58524
|
+
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
58330
58525
|
object({
|
|
58331
58526
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
58332
58527
|
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
@@ -59233,7 +59428,14 @@ DeviceType.Lock, method(object({
|
|
|
59233
59428
|
auth: "protected"
|
|
59234
59429
|
}), method(object({
|
|
59235
59430
|
deviceId: number().int().nonnegative(),
|
|
59236
|
-
code: string().min(1).optional()
|
|
59431
|
+
code: string().min(1).optional(),
|
|
59432
|
+
/**
|
|
59433
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
59434
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
59435
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
59436
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
59437
|
+
*/
|
|
59438
|
+
confirm: boolean().optional()
|
|
59237
59439
|
}), _void(), {
|
|
59238
59440
|
kind: "mutation",
|
|
59239
59441
|
auth: "protected"
|
|
@@ -63649,7 +63851,7 @@ object({
|
|
|
63649
63851
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
63650
63852
|
lastChangedAt: number()
|
|
63651
63853
|
});
|
|
63652
|
-
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
|
|
63854
|
+
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
|
|
63653
63855
|
deviceId: number(),
|
|
63654
63856
|
on: boolean()
|
|
63655
63857
|
}), _void(), {
|
|
@@ -65341,6 +65543,7 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
65341
65543
|
kind: "mutation",
|
|
65342
65544
|
auth: "admin"
|
|
65343
65545
|
});
|
|
65546
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
65344
65547
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
65345
65548
|
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;
|
|
65346
65549
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
@@ -68618,6 +68821,13 @@ Object.freeze({
|
|
|
68618
68821
|
access: "view",
|
|
68619
68822
|
role: "viewer"
|
|
68620
68823
|
},
|
|
68824
|
+
"groupActive.getStatus": {
|
|
68825
|
+
capName: "group-active",
|
|
68826
|
+
capScope: "device",
|
|
68827
|
+
addonId: null,
|
|
68828
|
+
access: "view",
|
|
68829
|
+
role: "viewer"
|
|
68830
|
+
},
|
|
68621
68831
|
"groupCommands.getStatus": {
|
|
68622
68832
|
capName: "group-commands",
|
|
68623
68833
|
capScope: "device",
|
|
@@ -78493,6 +78703,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78493
78703
|
icon: "flame"
|
|
78494
78704
|
}]
|
|
78495
78705
|
},
|
|
78706
|
+
"group-active": {
|
|
78707
|
+
scope: "device",
|
|
78708
|
+
noun: t("Group state", "Stato del gruppo")
|
|
78709
|
+
},
|
|
78496
78710
|
"group-commands": {
|
|
78497
78711
|
scope: "device",
|
|
78498
78712
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -80039,6 +80253,11 @@ Object.freeze({
|
|
|
80039
80253
|
form: "single",
|
|
80040
80254
|
optional: false
|
|
80041
80255
|
}],
|
|
80256
|
+
"groupActive.getStatus": [{
|
|
80257
|
+
name: "deviceId",
|
|
80258
|
+
form: "single",
|
|
80259
|
+
optional: false
|
|
80260
|
+
}],
|
|
80042
80261
|
"groupCommands.getStatus": [{
|
|
80043
80262
|
name: "deviceId",
|
|
80044
80263
|
form: "single",
|
|
@@ -83187,7 +83406,7 @@ var AgentUIAddon = class extends BaseAddon {
|
|
|
83187
83406
|
capability: adminUiCapability,
|
|
83188
83407
|
provider: {
|
|
83189
83408
|
getStaticDir: async () => ({ staticDir: path.resolve(__dirname) }),
|
|
83190
|
-
getVersion: async () => ({ version: "1.2.
|
|
83409
|
+
getVersion: async () => ({ version: "1.2.278" })
|
|
83191
83410
|
}
|
|
83192
83411
|
}];
|
|
83193
83412
|
}
|
|
@@ -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,c as o,d as s,h as c,l,p as u,t as ee}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,c as o,d as s,h as c,l,p as u,t as ee}from"./index-Cf9eOmk0.js";import{MaskShapeCanvas as d}from"./MaskShapeCanvas-BTvl6Rin.js";var f=a(`grid-2x2`,[[`path`,{d:`M12 3v18`,key:`108xh3`}],[`path`,{d:`M3 12h18`,key:`1i2n21`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),p=n(i(),1),m=n(e(),1),h=110,g=`motion-zones`,_=0,te=[1,2,3],v=1,y=`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`,b=`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 x(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 S(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 C(e,t){return Math.ceil(e/t)}function w(e,t,n){return Math.min(n-1,Math.floor((e+.5)/t*n))}function T(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:i*a},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)e[r*t+n]===!0&&(o[s*i+w(n,t,i)]=!0)}return o}function E(e,t,n,r){let i=C(t,r),a=C(n,r),o=Array.from({length:t*n},()=>!1);for(let r=0;r<n;r+=1){let s=w(r,n,a);for(let n=0;n<t;n+=1)o[r*t+n]=e[s*i+w(n,t,i)]===!0}return o}function D({deviceId:e}){let n=t(),i=s(c().trpcClient,e),[a,w]=(0,p.useState)(null),[D,O]=(0,p.useState)(!1),[k,A]=(0,p.useState)(null),[j,M]=(0,p.useState)(null),[N,P]=(0,p.useState)(v),[F,I]=(0,p.useState)(!1),[L,R]=(0,p.useState)(!1),z=(0,p.useRef)(!1);(0,p.useEffect)(()=>{if(!i)return;let e=!1;return z.current=!1,w(null),O(!1),A(null),M(null),P(v),R(!1),(async()=>{try{let t=await i.motionZones?.getOptions({});if(e)return;if(!t)throw Error(`device proxy not ready`);if(w(t),z.current)return;let n=await i.motionZones?.getStatus({});if(e)return;if(!n)throw Error(`device proxy not ready`);z.current=!0;let r=n.regions.find(e=>e.shape.kind===`grid`),a=x(r?r.shape.cells:[],t.grid.width,t.grid.height);M(a),A(T(a,t.grid.width,t.grid.height,v))}catch(t){if(e)return;l(t)?O(!0):console.error(`Motion Zones load failed`,t)}})(),()=>{e=!0}},[i]);let B=a?a.grid.width:0,V=a?a.grid.height:0,H=C(B,N),U=C(V,N),W=(0,p.useMemo)(()=>k&&a?E(k,B,V,N):null,[k,a,B,V,N]),G=(0,p.useMemo)(()=>W!==null&&j!==null&&!S(W,j),[W,j]),K=(0,p.useMemo)(()=>W?W.reduce((e,t)=>t?e+1:e,0):0,[W]),q=B*V,J=H*U,Y=(0,p.useCallback)(e=>{J>0&&A(Array.from({length:J},()=>e))},[J]),X=(0,p.useCallback)(()=>{A(e=>e&&e.map(e=>!e))},[]),Z=(0,p.useCallback)(()=>{j&&a&&A(T(j,B,V,N))},[j,a,B,V,N]),ne=(0,p.useCallback)(e=>{e===N||!a||A(t=>{if(!t)return P(e),t;let n=T(E(t,B,V,N),B,V,e);return P(e),n})},[N,a,B,V]),Q=(0,p.useMemo)(()=>a&&k?[{id:_,shape:{kind:`grid`,gridWidth:H,gridHeight:U,cells:[...k]}}]:[],[a,k,H,U]),$=(0,p.useCallback)((e,t)=>{t.kind===`grid`&&A(t.cells)},[]),re=(0,p.useCallback)(async()=>{if(!(!i||!k||!a)){I(!0);try{let e=E(k,a.grid.width,a.grid.height,N),t={kind:`grid`,gridWidth:a.grid.width,gridHeight:a.grid.height,cells:e};await i.motionZones?.setZone({patch:{regions:[{id:_,enabled:!0,shape:t}]}});let n=await i.motionZones?.getStatus({});if(n){let e=n.regions.find(e=>e.shape.kind===`grid`),t=x(e?e.shape.cells:[],a.grid.width,a.grid.height);M(t),A(T(t,a.grid.width,a.grid.height,N))}}catch(e){console.error(`Motion Zones save failed`,e)}finally{I(!1)}}},[i,k,a,N]);ee((0,p.useMemo)(()=>L&&!D&&a&&k?{id:g,order:h,node:(0,m.jsx)(d,{transparent:!0,items:Q,supportedShapes:[`grid`],grid:{width:H,height:U},selectedId:_,onSelect:()=>{},onShapeChange:$,onDrawComplete:()=>{},drawingKind:null})}:null,[L,D,a,k,Q,$,H,U]));let ie=!D&&a!==null&&k!==null;return i?(0,m.jsx)(u,{title:n(`uiLibrary.motionZones.title`,`Motion Zones`),icon:(0,m.jsx)(f,{className:`h-3.5 w-3.5 text-foreground-subtle`}),children:(0,m.jsx)(`div`,{className:`flex flex-col gap-3`,children:D?(0,m.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.motionZones.unsupported`,`This camera doesn't expose an on-board motion zones grid.`)}):ie?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`${o} leading-relaxed`,children:(0,m.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,m.jsx)(`strong`,{className:`text-foreground`},`e`),(0,m.jsx)(`strong`,{className:`text-foreground`},`s`),(0,m.jsx)(`strong`,{className:`text-foreground`},`c`)],values:{width:a.grid.width,height:a.grid.height}})}),(0,m.jsxs)(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[(0,m.jsx)(`button`,{type:`button`,onClick:()=>R(e=>!e),disabled:F,"aria-pressed":L,className:L?b:y,children:L?n(`uiLibrary.motionZones.doneEditing`,`Done editing`):n(`uiLibrary.motionZones.editGrid`,`Edit grid`)}),(0,m.jsx)(`button`,{type:`button`,onClick:()=>Y(!0),disabled:F,className:y,children:n(`uiLibrary.motionZones.allOn`,`All on`)}),(0,m.jsx)(`button`,{type:`button`,onClick:()=>Y(!1),disabled:F,className:y,children:n(`uiLibrary.motionZones.allOff`,`All off`)}),(0,m.jsx)(`button`,{type:`button`,onClick:X,disabled:F,className:y,children:n(`uiLibrary.motionZones.invert`,`Invert`)}),(0,m.jsxs)(`div`,{className:`flex items-center gap-1 ml-1`,role:`group`,"aria-label":n(`uiLibrary.motionZones.cellSize`,`Cell size`),children:[(0,m.jsx)(`span`,{className:`${o} mr-0.5`,children:n(`uiLibrary.motionZones.cellSize`,`Cell size`)}),te.map(e=>(0,m.jsxs)(`button`,{type:`button`,onClick:()=>ne(e),disabled:F,"aria-pressed":N===e,title:e===1?n(`uiLibrary.motionZones.cameraGrid`,`Camera grid {{width}}×{{height}} (finest)`,{width:B,height:V}):n(`uiLibrary.motionZones.paintingGrid`,`{{width}}×{{height}} painting grid · cells ×{{factor}} bigger`,{width:C(B,e),height:C(V,e),factor:e}),className:N===e?b:y,children:[`×`,e]},e))]}),(0,m.jsxs)(`span`,{className:`${o} ml-1 tabular-nums`,children:[n(`uiLibrary.motionZones.cells`,`{{active}} / {{total}} cells · {{width}}×{{height}}`,{active:K,total:q,width:a.grid.width,height:a.grid.height}),N===1?``:n(`uiLibrary.motionZones.paintSuffix`,` · paint {{width}}×{{height}}`,{width:H,height:U})]}),(0,m.jsx)(`span`,{className:`flex-1`}),(0,m.jsx)(`button`,{type:`button`,onClick:Z,disabled:F||!G,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,m.jsx)(`button`,{type:`button`,onClick:()=>void re(),disabled:F||!G,className:b,children:F?n(`uiLibrary.motionZones.saving`,`Saving…`):n(`uiLibrary.motionZones.save`,`Save`)})]})]}):(0,m.jsx)(`p`,{className:`${o} leading-relaxed`,children:n(`uiLibrary.motionZones.loading`,`Loading the camera's grid…`)})})}):null}export{D 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{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-Cf9eOmk0.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};
|