@camstack/addon-notifiers 1.2.622 → 1.2.623
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/README.md +24 -8
- package/dist/addon.js +543 -48
- package/dist/addon.mjs +543 -48
- package/i18n/en.json +1 -1
- package/i18n/it.json +1 -1
- package/package.json +3 -3
package/dist/addon.mjs
CHANGED
|
@@ -5578,7 +5578,7 @@ var ZodIssueCode = {
|
|
|
5578
5578
|
var ZodFirstPartyTypeKind;
|
|
5579
5579
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5580
5580
|
//#endregion
|
|
5581
|
-
//#region ../types/dist/sleep
|
|
5581
|
+
//#region ../types/dist/sleep--jXS9hWo.mjs
|
|
5582
5582
|
/**
|
|
5583
5583
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5584
5584
|
* window to float samples (D455).
|
|
@@ -8935,6 +8935,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8935
8935
|
"filesystem-browse",
|
|
8936
8936
|
"flood",
|
|
8937
8937
|
"gas",
|
|
8938
|
+
"group-active",
|
|
8938
8939
|
"group-commands",
|
|
8939
8940
|
"group-status",
|
|
8940
8941
|
"heating-valve",
|
|
@@ -8957,6 +8958,7 @@ var CAP_NAMES = Object.freeze([
|
|
|
8957
8958
|
"login-method",
|
|
8958
8959
|
"media-player",
|
|
8959
8960
|
"mesh-network",
|
|
8961
|
+
"meter",
|
|
8960
8962
|
"metrics-provider",
|
|
8961
8963
|
"model-convert",
|
|
8962
8964
|
"model-distributor",
|
|
@@ -9755,6 +9757,10 @@ Object.freeze({
|
|
|
9755
9757
|
"path": "detected",
|
|
9756
9758
|
"valueType": "boolean"
|
|
9757
9759
|
}],
|
|
9760
|
+
"group-active": [{
|
|
9761
|
+
"path": "active",
|
|
9762
|
+
"valueType": "boolean"
|
|
9763
|
+
}],
|
|
9758
9764
|
"group-status": [
|
|
9759
9765
|
{
|
|
9760
9766
|
"path": "included",
|
|
@@ -10014,6 +10020,10 @@ Object.freeze({
|
|
|
10014
10020
|
]
|
|
10015
10021
|
}
|
|
10016
10022
|
],
|
|
10023
|
+
"meter": [{
|
|
10024
|
+
"path": "value",
|
|
10025
|
+
"valueType": "number"
|
|
10026
|
+
}],
|
|
10017
10027
|
"motion": [
|
|
10018
10028
|
{
|
|
10019
10029
|
"path": "detected",
|
|
@@ -10771,7 +10781,7 @@ function userError(code, params, englishFallback) {
|
|
|
10771
10781
|
return new CamstackError(code, params, englishFallback);
|
|
10772
10782
|
}
|
|
10773
10783
|
//#endregion
|
|
10774
|
-
//#region ../types/dist/err-msg-
|
|
10784
|
+
//#region ../types/dist/err-msg-DIorGYeC.mjs
|
|
10775
10785
|
var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
10776
10786
|
DeviceType["Camera"] = "camera";
|
|
10777
10787
|
DeviceType["Hub"] = "hub";
|
|
@@ -10865,8 +10875,14 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
10865
10875
|
DeviceType["PetFeeder"] = "pet-feeder";
|
|
10866
10876
|
/** Built-in dishwasher — program lifecycle, door / remote-start gates,
|
|
10867
10877
|
* salt and rinse-aid levels. Installed with the `dishwasher` cap.
|
|
10868
|
-
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10878
|
+
* Sources: native Bosch Home Connect, reusable by similar integrations.
|
|
10879
|
+
* A plug-built dishwasher carries running-state and power instead. */
|
|
10869
10880
|
DeviceType["Dishwasher"] = "dishwasher";
|
|
10881
|
+
/** Washing machine. A plug-built one is running while its power stays
|
|
10882
|
+
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
10883
|
+
DeviceType["Washer"] = "washer";
|
|
10884
|
+
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
10885
|
+
DeviceType["Dryer"] = "dryer";
|
|
10870
10886
|
return DeviceType;
|
|
10871
10887
|
}({});
|
|
10872
10888
|
var DeviceFeature = /* @__PURE__ */ function(DeviceFeature) {
|
|
@@ -11114,7 +11130,7 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
11114
11130
|
return DeviceRole;
|
|
11115
11131
|
}({});
|
|
11116
11132
|
//#endregion
|
|
11117
|
-
//#region ../types/dist/composition-
|
|
11133
|
+
//#region ../types/dist/composition-Bb8iwHEJ.mjs
|
|
11118
11134
|
/**
|
|
11119
11135
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11120
11136
|
*
|
|
@@ -11429,6 +11445,13 @@ var table = {
|
|
|
11429
11445
|
apply: () => {
|
|
11430
11446
|
throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
|
|
11431
11447
|
}
|
|
11448
|
+
},
|
|
11449
|
+
schmitt: {
|
|
11450
|
+
minArgs: 3,
|
|
11451
|
+
maxArgs: 5,
|
|
11452
|
+
apply: () => {
|
|
11453
|
+
throw new ExpressionEvalError("schmitt: stateful evaluation context is required");
|
|
11454
|
+
}
|
|
11432
11455
|
}
|
|
11433
11456
|
};
|
|
11434
11457
|
Object.freeze(Object.assign(Object.create(null), table));
|
|
@@ -11870,6 +11893,99 @@ function validateExpressionSource(src) {
|
|
|
11870
11893
|
return null;
|
|
11871
11894
|
}
|
|
11872
11895
|
/**
|
|
11896
|
+
* One power reading becomes a washer, dryer or dishwasher (D944 helper).
|
|
11897
|
+
*
|
|
11898
|
+
* Standby of the control board is under 5 W; a motor or a heater is far
|
|
11899
|
+
* above that, so 5 W is the line. A door lock or a short pump spike is not
|
|
11900
|
+
* a cycle, so running starts only after 30 s above the line.
|
|
11901
|
+
*
|
|
11902
|
+
* A wash, dry or dishwasher pause (soak, heat-wait, heater off between
|
|
11903
|
+
* bursts) lasts several minutes. Ending the cycle on the first dip would
|
|
11904
|
+
* report "finished" in the middle of the program, so the stop dwell is
|
|
11905
|
+
* 10 minutes on every type. The number is stored per type so one machine
|
|
11906
|
+
* can move later without moving the others.
|
|
11907
|
+
*/
|
|
11908
|
+
var APPLIANCE_TEMPLATE_ID = "appliance";
|
|
11909
|
+
var APPLIANCE_KINDS = [
|
|
11910
|
+
"washer",
|
|
11911
|
+
"dryer",
|
|
11912
|
+
"dishwasher"
|
|
11913
|
+
];
|
|
11914
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
11915
|
+
/**
|
|
11916
|
+
* Statistics and integrals over a history read (D944).
|
|
11917
|
+
*
|
|
11918
|
+
* Pure: the same points always give the same answer, so a restart recomputes
|
|
11919
|
+
* rather than trusting a buffer. A gauge mean is time-weighted (value × ms),
|
|
11920
|
+
* never the mean of the samples. A hole with no held value is not integrated
|
|
11921
|
+
* as zero. A truncated read is not published.
|
|
11922
|
+
*/
|
|
11923
|
+
var STATISTIC_STATS = [
|
|
11924
|
+
"min",
|
|
11925
|
+
"max",
|
|
11926
|
+
"mean",
|
|
11927
|
+
"sum",
|
|
11928
|
+
"count",
|
|
11929
|
+
"change"
|
|
11930
|
+
];
|
|
11931
|
+
/**
|
|
11932
|
+
* The range a statistics or integral helper asks history for (D944).
|
|
11933
|
+
*
|
|
11934
|
+
* Calendar edges are the hub's timezone, never UTC midnight: a day is 23, 24
|
|
11935
|
+
* or 25 hours across a DST change. A tier is eligible only when its retention
|
|
11936
|
+
* is strictly longer than the longest that range can be. Forever qualifies.
|
|
11937
|
+
* Equal to a tier's retention is a refusal, not a short answer.
|
|
11938
|
+
*/
|
|
11939
|
+
var MINUTE_MS = 6e4;
|
|
11940
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
11941
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
11942
|
+
var ROLLING_SPANS = [
|
|
11943
|
+
"1h",
|
|
11944
|
+
"24h",
|
|
11945
|
+
"2d",
|
|
11946
|
+
"7d",
|
|
11947
|
+
"30d",
|
|
11948
|
+
"custom"
|
|
11949
|
+
];
|
|
11950
|
+
var CALENDAR_PERIODS = [
|
|
11951
|
+
"today",
|
|
11952
|
+
"yesterday",
|
|
11953
|
+
"this-week",
|
|
11954
|
+
"this-month"
|
|
11955
|
+
];
|
|
11956
|
+
var RESET_PERIODS = [
|
|
11957
|
+
"never",
|
|
11958
|
+
"daily",
|
|
11959
|
+
"weekly",
|
|
11960
|
+
"monthly"
|
|
11961
|
+
];
|
|
11962
|
+
5 * MINUTE_MS;
|
|
11963
|
+
24 * HOUR_MS, 2 * DAY_MS, 7 * DAY_MS, 30 * DAY_MS;
|
|
11964
|
+
7 * DAY_MS, 30 * DAY_MS, 365 * DAY_MS;
|
|
11965
|
+
var rollingWindowObject = object({
|
|
11966
|
+
kind: literal("rolling"),
|
|
11967
|
+
span: _enum(ROLLING_SPANS),
|
|
11968
|
+
customMinutes: number().int().min(5).optional()
|
|
11969
|
+
}).check((payload) => {
|
|
11970
|
+
if (payload.value.span === "custom" && payload.value.customMinutes === void 0) payload.issues.push({
|
|
11971
|
+
code: "custom",
|
|
11972
|
+
message: "a custom range needs a duration in minutes",
|
|
11973
|
+
path: ["customMinutes"],
|
|
11974
|
+
input: payload.value
|
|
11975
|
+
});
|
|
11976
|
+
if (payload.value.span !== "custom" && payload.value.customMinutes !== void 0) payload.issues.push({
|
|
11977
|
+
code: "custom",
|
|
11978
|
+
message: "only a custom range carries a duration",
|
|
11979
|
+
path: ["customMinutes"],
|
|
11980
|
+
input: payload.value
|
|
11981
|
+
});
|
|
11982
|
+
});
|
|
11983
|
+
var RollingWindowSchema = rollingWindowObject;
|
|
11984
|
+
var StatisticWindowSchema = discriminatedUnion("kind", [rollingWindowObject, object({
|
|
11985
|
+
kind: literal("calendar"),
|
|
11986
|
+
period: _enum(CALENDAR_PERIODS)
|
|
11987
|
+
})]);
|
|
11988
|
+
/**
|
|
11873
11989
|
* A composition source identity: `(addonId, stableId)`, never a numeric device
|
|
11874
11990
|
* id. Split out so helper schemas can name it without a cycle through
|
|
11875
11991
|
* `composition.ts` (D659).
|
|
@@ -11895,7 +12011,13 @@ var GROUP_TEMPLATE_IDS = [
|
|
|
11895
12011
|
"occupancy",
|
|
11896
12012
|
"binary",
|
|
11897
12013
|
"switch",
|
|
11898
|
-
"light"
|
|
12014
|
+
"light",
|
|
12015
|
+
"fan",
|
|
12016
|
+
"cover",
|
|
12017
|
+
"valve",
|
|
12018
|
+
"lock",
|
|
12019
|
+
"media-player",
|
|
12020
|
+
"climate"
|
|
11899
12021
|
];
|
|
11900
12022
|
var NUMERIC_AGGREGATES = [
|
|
11901
12023
|
"mean",
|
|
@@ -11925,14 +12047,73 @@ var HelperMemberSchema = object({
|
|
|
11925
12047
|
cap: string().min(1),
|
|
11926
12048
|
fieldPath: string().min(1)
|
|
11927
12049
|
});
|
|
11928
|
-
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
12050
|
+
var HelperSpecSchema = discriminatedUnion("kind", [
|
|
12051
|
+
object({
|
|
12052
|
+
kind: literal("group"),
|
|
12053
|
+
templateId: GroupTemplateIdSchema,
|
|
12054
|
+
aggregate: AggregateReducerSchema,
|
|
12055
|
+
members: array(HelperMemberSchema).min(1).max(64),
|
|
12056
|
+
/** Actuator groups only. Readings leave this unset. */
|
|
12057
|
+
active: ActiveCriterionSchema.optional()
|
|
12058
|
+
}),
|
|
12059
|
+
object({
|
|
12060
|
+
kind: literal("appliance"),
|
|
12061
|
+
templateId: literal(APPLIANCE_TEMPLATE_ID),
|
|
12062
|
+
appliance: _enum(APPLIANCE_KINDS),
|
|
12063
|
+
thresholdWatts: number().positive(),
|
|
12064
|
+
startDwellMs: number().int().nonnegative(),
|
|
12065
|
+
stopDwellMs: number().int().nonnegative(),
|
|
12066
|
+
member: HelperMemberSchema
|
|
12067
|
+
}),
|
|
12068
|
+
object({
|
|
12069
|
+
kind: literal("statistic"),
|
|
12070
|
+
stat: _enum(STATISTIC_STATS),
|
|
12071
|
+
window: StatisticWindowSchema,
|
|
12072
|
+
source: HelperMemberSchema
|
|
12073
|
+
}),
|
|
12074
|
+
object({
|
|
12075
|
+
kind: literal("integral"),
|
|
12076
|
+
reset: _enum(RESET_PERIODS),
|
|
12077
|
+
output: _enum(["energy", "meter"]),
|
|
12078
|
+
source: HelperMemberSchema
|
|
12079
|
+
}),
|
|
12080
|
+
object({
|
|
12081
|
+
kind: literal("threshold"),
|
|
12082
|
+
source: HelperMemberSchema,
|
|
12083
|
+
high: number(),
|
|
12084
|
+
low: number(),
|
|
12085
|
+
onForMs: number().int().nonnegative(),
|
|
12086
|
+
offForMs: number().int().nonnegative()
|
|
12087
|
+
}),
|
|
12088
|
+
object({
|
|
12089
|
+
kind: literal("arithmetic"),
|
|
12090
|
+
op: _enum([
|
|
12091
|
+
"sum",
|
|
12092
|
+
"difference",
|
|
12093
|
+
"ratio"
|
|
12094
|
+
]),
|
|
12095
|
+
inputs: array(HelperMemberSchema).min(2).max(64)
|
|
12096
|
+
}),
|
|
12097
|
+
object({
|
|
12098
|
+
kind: literal("expression"),
|
|
12099
|
+
expr: string().min(1),
|
|
12100
|
+
inputs: array(object({
|
|
12101
|
+
name: string().min(1),
|
|
12102
|
+
member: HelperMemberSchema
|
|
12103
|
+
})).min(1).max(64),
|
|
12104
|
+
output: _enum(["number", "boolean"])
|
|
12105
|
+
}),
|
|
12106
|
+
object({
|
|
12107
|
+
kind: literal("derivative"),
|
|
12108
|
+
window: RollingWindowSchema,
|
|
12109
|
+
per: _enum([
|
|
12110
|
+
"second",
|
|
12111
|
+
"minute",
|
|
12112
|
+
"hour"
|
|
12113
|
+
]),
|
|
12114
|
+
source: HelperMemberSchema
|
|
12115
|
+
})
|
|
12116
|
+
]);
|
|
11936
12117
|
/** A composed device's stableId is this prefix plus the block id. */
|
|
11937
12118
|
var COMPOSED_DEVICE_STABLE_ID_PREFIX = "composed-";
|
|
11938
12119
|
/** Longest snippet a `code` source may carry (slice 4). */
|
|
@@ -11993,6 +12174,12 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
11993
12174
|
"all"
|
|
11994
12175
|
]),
|
|
11995
12176
|
reads: array(CompositionFieldReadSchema).min(1).max(64),
|
|
12177
|
+
/**
|
|
12178
|
+
* Set when the reads are an enum state, not a yes/no: the listed states
|
|
12179
|
+
* count as active (a cover is `open`). A state outside the list is inactive;
|
|
12180
|
+
* a read that is not a state at all is left out. Only `any` / `all` use it.
|
|
12181
|
+
*/
|
|
12182
|
+
activeWhen: array(string().min(1)).min(1).optional(),
|
|
11996
12183
|
/** `value` is the reading; the others are the member counts. */
|
|
11997
12184
|
publish: _enum([
|
|
11998
12185
|
"value",
|
|
@@ -12006,6 +12193,26 @@ var CompositionFieldSourceSchema = discriminatedUnion("kind", [
|
|
|
12006
12193
|
"omit",
|
|
12007
12194
|
"offline"
|
|
12008
12195
|
])
|
|
12196
|
+
}),
|
|
12197
|
+
object({
|
|
12198
|
+
kind: literal("series"),
|
|
12199
|
+
read: CompositionFieldReadSchema,
|
|
12200
|
+
/** `value` is the statistic or the integral. The others are the gap counts. */
|
|
12201
|
+
publish: _enum([
|
|
12202
|
+
"value",
|
|
12203
|
+
"included",
|
|
12204
|
+
"excluded",
|
|
12205
|
+
"total"
|
|
12206
|
+
]),
|
|
12207
|
+
/**
|
|
12208
|
+
* What an unanswered `value` does. Counts ignore this. `offline` leaves the
|
|
12209
|
+
* device unseeded until history answers, so a restart cannot publish 0.
|
|
12210
|
+
*/
|
|
12211
|
+
empty: _enum([
|
|
12212
|
+
"null",
|
|
12213
|
+
"omit",
|
|
12214
|
+
"offline"
|
|
12215
|
+
])
|
|
12009
12216
|
})
|
|
12010
12217
|
]);
|
|
12011
12218
|
var CompositionCommandTargetSchema = discriminatedUnion("kind", [object({
|
|
@@ -17301,7 +17508,22 @@ var BrokerProviderInfoSchema = object({
|
|
|
17301
17508
|
*/
|
|
17302
17509
|
interactiveConnectPath: string().min(1).optional(),
|
|
17303
17510
|
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
17304
|
-
interactiveDisconnectPath: string().min(1).optional()
|
|
17511
|
+
interactiveDisconnectPath: string().min(1).optional(),
|
|
17512
|
+
/**
|
|
17513
|
+
* The kind signs in through a server-held interactive session instead of
|
|
17514
|
+
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
17515
|
+
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
17516
|
+
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
17517
|
+
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
17518
|
+
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
17519
|
+
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
17520
|
+
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
17521
|
+
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
17522
|
+
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
17523
|
+
* offers it as the default path; the fields the session fills are
|
|
17524
|
+
* marked `filledBySignIn` in the kind's settings schema.
|
|
17525
|
+
*/
|
|
17526
|
+
signIn: object({ basePath: string().min(1) }).optional()
|
|
17305
17527
|
}))
|
|
17306
17528
|
});
|
|
17307
17529
|
var ListInputSchema = object({
|
|
@@ -18300,7 +18522,8 @@ var CompositionProblemSchema = object({
|
|
|
18300
18522
|
"native-capability-replace",
|
|
18301
18523
|
"mixed-quantity",
|
|
18302
18524
|
"helper-mismatch",
|
|
18303
|
-
"member-constraint"
|
|
18525
|
+
"member-constraint",
|
|
18526
|
+
"retention"
|
|
18304
18527
|
]),
|
|
18305
18528
|
path: string(),
|
|
18306
18529
|
message: string()
|
|
@@ -18534,13 +18757,17 @@ Object.freeze({
|
|
|
18534
18757
|
latchedAt: {
|
|
18535
18758
|
kind: "fixed",
|
|
18536
18759
|
result: ["number", "null"]
|
|
18760
|
+
},
|
|
18761
|
+
schmitt: {
|
|
18762
|
+
kind: "fixed",
|
|
18763
|
+
result: ["boolean", "null"]
|
|
18537
18764
|
}
|
|
18538
18765
|
});
|
|
18539
18766
|
/**
|
|
18540
18767
|
* One catalog row per base reading a group helper can build (D944 §3).
|
|
18541
18768
|
* Data, not a form default written into the block.
|
|
18542
18769
|
*/
|
|
18543
|
-
var NOW = {
|
|
18770
|
+
var NOW$2 = {
|
|
18544
18771
|
fieldPath: "lastFetchedAt",
|
|
18545
18772
|
expr: "now"
|
|
18546
18773
|
};
|
|
@@ -18554,6 +18781,8 @@ function numeric(input) {
|
|
|
18554
18781
|
family: "numeric",
|
|
18555
18782
|
deviceType: DeviceType.Sensor,
|
|
18556
18783
|
memberDeviceType: DeviceType.Sensor,
|
|
18784
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18785
|
+
activeWhen: void 0,
|
|
18557
18786
|
commandCaps: [],
|
|
18558
18787
|
baseUnit: baseUnitOf(input.quantity),
|
|
18559
18788
|
aggregates: NUMERIC_AGGREGATES,
|
|
@@ -18569,6 +18798,8 @@ function binary(input) {
|
|
|
18569
18798
|
family: "binary",
|
|
18570
18799
|
deviceType: DeviceType.Sensor,
|
|
18571
18800
|
memberDeviceType: DeviceType.Sensor,
|
|
18801
|
+
memberDeviceTypes: [DeviceType.Sensor],
|
|
18802
|
+
activeWhen: void 0,
|
|
18572
18803
|
commandCaps: [],
|
|
18573
18804
|
quantity: "none",
|
|
18574
18805
|
baseUnit: void 0,
|
|
@@ -18586,7 +18817,7 @@ var GROUP_TEMPLATES = [
|
|
|
18586
18817
|
quantity: "temperature",
|
|
18587
18818
|
defaultAggregate: "mean",
|
|
18588
18819
|
empty: "null",
|
|
18589
|
-
companions: [NOW]
|
|
18820
|
+
companions: [NOW$2]
|
|
18590
18821
|
}),
|
|
18591
18822
|
numeric({
|
|
18592
18823
|
id: "humidity",
|
|
@@ -18597,7 +18828,7 @@ var GROUP_TEMPLATES = [
|
|
|
18597
18828
|
quantity: "humidity",
|
|
18598
18829
|
defaultAggregate: "mean",
|
|
18599
18830
|
empty: "null",
|
|
18600
|
-
companions: [NOW]
|
|
18831
|
+
companions: [NOW$2]
|
|
18601
18832
|
}),
|
|
18602
18833
|
numeric({
|
|
18603
18834
|
id: "pressure",
|
|
@@ -18608,7 +18839,7 @@ var GROUP_TEMPLATES = [
|
|
|
18608
18839
|
quantity: "pressure",
|
|
18609
18840
|
defaultAggregate: "mean",
|
|
18610
18841
|
empty: "null",
|
|
18611
|
-
companions: [NOW]
|
|
18842
|
+
companions: [NOW$2]
|
|
18612
18843
|
}),
|
|
18613
18844
|
numeric({
|
|
18614
18845
|
id: "illuminance",
|
|
@@ -18619,7 +18850,7 @@ var GROUP_TEMPLATES = [
|
|
|
18619
18850
|
quantity: "illuminance",
|
|
18620
18851
|
defaultAggregate: "mean",
|
|
18621
18852
|
empty: "null",
|
|
18622
|
-
companions: [NOW]
|
|
18853
|
+
companions: [NOW$2]
|
|
18623
18854
|
}),
|
|
18624
18855
|
numeric({
|
|
18625
18856
|
id: "power",
|
|
@@ -18630,7 +18861,7 @@ var GROUP_TEMPLATES = [
|
|
|
18630
18861
|
quantity: "power",
|
|
18631
18862
|
defaultAggregate: "sum",
|
|
18632
18863
|
empty: "omit",
|
|
18633
|
-
companions: [NOW]
|
|
18864
|
+
companions: [NOW$2]
|
|
18634
18865
|
}),
|
|
18635
18866
|
numeric({
|
|
18636
18867
|
id: "energy",
|
|
@@ -18641,7 +18872,7 @@ var GROUP_TEMPLATES = [
|
|
|
18641
18872
|
quantity: "energy",
|
|
18642
18873
|
defaultAggregate: "sum",
|
|
18643
18874
|
empty: "omit",
|
|
18644
|
-
companions: [NOW]
|
|
18875
|
+
companions: [NOW$2]
|
|
18645
18876
|
}),
|
|
18646
18877
|
numeric({
|
|
18647
18878
|
id: "battery",
|
|
@@ -18676,7 +18907,7 @@ var GROUP_TEMPLATES = [
|
|
|
18676
18907
|
quantity: "device",
|
|
18677
18908
|
defaultAggregate: "mean",
|
|
18678
18909
|
empty: "null",
|
|
18679
|
-
companions: [NOW]
|
|
18910
|
+
companions: [NOW$2]
|
|
18680
18911
|
}),
|
|
18681
18912
|
binary({
|
|
18682
18913
|
id: "motion",
|
|
@@ -18770,6 +19001,11 @@ var GROUP_TEMPLATES = [
|
|
|
18770
19001
|
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.",
|
|
18771
19002
|
deviceType: DeviceType.Switch,
|
|
18772
19003
|
outputCap: "switch",
|
|
19004
|
+
outputField: "on",
|
|
19005
|
+
member: {
|
|
19006
|
+
cap: "switch",
|
|
19007
|
+
fieldPath: "on"
|
|
19008
|
+
},
|
|
18773
19009
|
commandCaps: ["switch"]
|
|
18774
19010
|
}),
|
|
18775
19011
|
actuator({
|
|
@@ -18777,34 +19013,136 @@ var GROUP_TEMPLATES = [
|
|
|
18777
19013
|
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.",
|
|
18778
19014
|
deviceType: DeviceType.Light,
|
|
18779
19015
|
outputCap: "switch",
|
|
19016
|
+
outputField: "on",
|
|
19017
|
+
member: {
|
|
19018
|
+
cap: "switch",
|
|
19019
|
+
fieldPath: "on"
|
|
19020
|
+
},
|
|
18780
19021
|
commandCaps: [
|
|
18781
19022
|
"switch",
|
|
18782
19023
|
"brightness",
|
|
18783
19024
|
"color"
|
|
18784
19025
|
]
|
|
19026
|
+
}),
|
|
19027
|
+
actuator({
|
|
19028
|
+
id: "fan",
|
|
19029
|
+
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.",
|
|
19030
|
+
deviceType: DeviceType.Fan,
|
|
19031
|
+
outputCap: "switch",
|
|
19032
|
+
outputField: "on",
|
|
19033
|
+
member: {
|
|
19034
|
+
cap: "switch",
|
|
19035
|
+
fieldPath: "on"
|
|
19036
|
+
},
|
|
19037
|
+
commandCaps: ["switch", "fan-control"]
|
|
19038
|
+
}),
|
|
19039
|
+
actuator({
|
|
19040
|
+
id: "cover",
|
|
19041
|
+
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.",
|
|
19042
|
+
deviceType: DeviceType.Cover,
|
|
19043
|
+
outputCap: "group-active",
|
|
19044
|
+
outputField: "active",
|
|
19045
|
+
member: {
|
|
19046
|
+
cap: "cover",
|
|
19047
|
+
fieldPath: "state"
|
|
19048
|
+
},
|
|
19049
|
+
activeWhen: [
|
|
19050
|
+
"open",
|
|
19051
|
+
"opening",
|
|
19052
|
+
"stopped"
|
|
19053
|
+
],
|
|
19054
|
+
commandCaps: ["cover"]
|
|
19055
|
+
}),
|
|
19056
|
+
actuator({
|
|
19057
|
+
id: "valve",
|
|
19058
|
+
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.",
|
|
19059
|
+
deviceType: DeviceType.Valve,
|
|
19060
|
+
outputCap: "group-active",
|
|
19061
|
+
outputField: "active",
|
|
19062
|
+
member: {
|
|
19063
|
+
cap: "valve",
|
|
19064
|
+
fieldPath: "state"
|
|
19065
|
+
},
|
|
19066
|
+
activeWhen: [
|
|
19067
|
+
"open",
|
|
19068
|
+
"opening",
|
|
19069
|
+
"stopped"
|
|
19070
|
+
],
|
|
19071
|
+
commandCaps: ["valve"]
|
|
19072
|
+
}),
|
|
19073
|
+
actuator({
|
|
19074
|
+
id: "lock",
|
|
19075
|
+
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.",
|
|
19076
|
+
deviceType: DeviceType.Lock,
|
|
19077
|
+
outputCap: "group-active",
|
|
19078
|
+
outputField: "active",
|
|
19079
|
+
member: {
|
|
19080
|
+
cap: "lock-control",
|
|
19081
|
+
fieldPath: "state"
|
|
19082
|
+
},
|
|
19083
|
+
activeWhen: ["locked"],
|
|
19084
|
+
commandCaps: ["lock-control"]
|
|
19085
|
+
}),
|
|
19086
|
+
actuator({
|
|
19087
|
+
id: "media-player",
|
|
19088
|
+
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.",
|
|
19089
|
+
deviceType: DeviceType.MediaPlayer,
|
|
19090
|
+
outputCap: "group-active",
|
|
19091
|
+
outputField: "active",
|
|
19092
|
+
member: {
|
|
19093
|
+
cap: "media-player",
|
|
19094
|
+
fieldPath: "state"
|
|
19095
|
+
},
|
|
19096
|
+
activeWhen: ["playing", "buffering"],
|
|
19097
|
+
commandCaps: ["media-player"]
|
|
19098
|
+
}),
|
|
19099
|
+
actuator({
|
|
19100
|
+
id: "climate",
|
|
19101
|
+
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.",
|
|
19102
|
+
deviceType: DeviceType.Thermostat,
|
|
19103
|
+
memberDeviceTypes: [DeviceType.Thermostat, DeviceType.Climate],
|
|
19104
|
+
outputCap: "group-active",
|
|
19105
|
+
outputField: "active",
|
|
19106
|
+
member: {
|
|
19107
|
+
cap: "climate-control",
|
|
19108
|
+
fieldPath: "mode"
|
|
19109
|
+
},
|
|
19110
|
+
activeWhen: [
|
|
19111
|
+
"heat",
|
|
19112
|
+
"cool",
|
|
19113
|
+
"auto",
|
|
19114
|
+
"heat_cool",
|
|
19115
|
+
"fan_only",
|
|
19116
|
+
"dry"
|
|
19117
|
+
],
|
|
19118
|
+
commandCaps: ["climate-control"]
|
|
18785
19119
|
})
|
|
18786
19120
|
];
|
|
18787
19121
|
function actuator(input) {
|
|
18788
19122
|
return {
|
|
18789
|
-
|
|
19123
|
+
id: input.id,
|
|
19124
|
+
summary: input.summary,
|
|
19125
|
+
deviceType: input.deviceType,
|
|
19126
|
+
outputCap: input.outputCap,
|
|
19127
|
+
outputField: input.outputField,
|
|
19128
|
+
commandCaps: input.commandCaps,
|
|
18790
19129
|
family: "actuator",
|
|
18791
19130
|
role: void 0,
|
|
18792
19131
|
memberDeviceType: input.deviceType,
|
|
18793
|
-
|
|
19132
|
+
memberDeviceTypes: input.memberDeviceTypes ?? [input.deviceType],
|
|
19133
|
+
activeWhen: input.activeWhen,
|
|
18794
19134
|
quantity: "none",
|
|
18795
19135
|
baseUnit: void 0,
|
|
18796
19136
|
aggregates: BINARY_AGGREGATES,
|
|
18797
19137
|
defaultAggregate: "any",
|
|
18798
19138
|
empty: "offline",
|
|
18799
|
-
accept: [
|
|
18800
|
-
cap: "switch",
|
|
18801
|
-
fieldPath: "on"
|
|
18802
|
-
}],
|
|
19139
|
+
accept: [input.member],
|
|
18803
19140
|
companions: [CHANGED]
|
|
18804
19141
|
};
|
|
18805
19142
|
}
|
|
18806
19143
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
18807
19144
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
19145
|
+
DeviceType.Camera;
|
|
18808
19146
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
18809
19147
|
/**
|
|
18810
19148
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -43819,7 +44157,7 @@ var NetworkAccessStatusSchema = object({
|
|
|
43819
44157
|
});
|
|
43820
44158
|
/**
|
|
43821
44159
|
* Optional, richer endpoint shape returned by providers that expose
|
|
43822
|
-
* MORE than one ingress concurrently (Tailscale
|
|
44160
|
+
* MORE than one ingress concurrently (Tailscale Funnel with mixed
|
|
43823
44161
|
* serve+funnel rules, future ngrok multi-tunnel, …). Each entry carries
|
|
43824
44162
|
* the originating provider config (mode + sourcePort) so the
|
|
43825
44163
|
* orchestrator UI can label rows distinctly. Providers that expose only
|
|
@@ -46484,7 +46822,20 @@ var NcRuleInputSchema = object({
|
|
|
46484
46822
|
* save by the hub's grouping guard). `.optional()`, never `.default()`: the addon cap
|
|
46485
46823
|
* path does not run Zod defaults.
|
|
46486
46824
|
*/
|
|
46487
|
-
groupingSec: number().int().min(5).max(60).optional()
|
|
46825
|
+
groupingSec: number().int().min(5).max(60).optional(),
|
|
46826
|
+
/**
|
|
46827
|
+
* ASK ME TO CONFIRM DETECTIONS (#243): a DETECTION notification of this rule
|
|
46828
|
+
* carries "Correct" / "Wrong" buttons (and, where the channel has room, a
|
|
46829
|
+
* "Wrong, it is a <class>" choice). A tap marks the track for retraining, the
|
|
46830
|
+
* same mark the Library writes, and records whose notification it was and
|
|
46831
|
+
* when. A channel with no buttons gets the same two choices as links.
|
|
46832
|
+
*
|
|
46833
|
+
* `.optional()`, never `.default()` (a Zod default does not run on the addon
|
|
46834
|
+
* cap path): absent and `false` both mean off, which is what every rule
|
|
46835
|
+
* authored before this field meant. An editor turns it off by writing
|
|
46836
|
+
* `false`, so it needs no `clear` key.
|
|
46837
|
+
*/
|
|
46838
|
+
askVerdict: boolean().optional()
|
|
46488
46839
|
});
|
|
46489
46840
|
/**
|
|
46490
46841
|
* Partial patch for `updateRule` — any subset of the input fields, plus the
|
|
@@ -56941,7 +57292,7 @@ var BatteryStatusSchema = object({
|
|
|
56941
57292
|
*/
|
|
56942
57293
|
binary: boolean().optional().meta({ label: "Binary indicator" })
|
|
56943
57294
|
});
|
|
56944
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat;
|
|
57295
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.EventEmitter;
|
|
56945
57296
|
method(object({
|
|
56946
57297
|
deviceId: number(),
|
|
56947
57298
|
/** Bound on the wait. Sensible range 3000–10000ms. */
|
|
@@ -58685,7 +59036,13 @@ results: array(object({
|
|
|
58685
59036
|
/** Ms epoch of the attempt. */
|
|
58686
59037
|
at: number()
|
|
58687
59038
|
})) });
|
|
58688
|
-
DeviceType.Switch, DeviceType.Light;
|
|
59039
|
+
DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
59040
|
+
object({
|
|
59041
|
+
active: boolean().meta({ label: "Active" }),
|
|
59042
|
+
/** Ms epoch when the slice was last updated. */
|
|
59043
|
+
lastChangedAt: number()
|
|
59044
|
+
});
|
|
59045
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Climate, DeviceType.Thermostat;
|
|
58689
59046
|
object({
|
|
58690
59047
|
/** Members whose current value entered the aggregate. */
|
|
58691
59048
|
included: number().int().nonnegative().meta(readingMeta("count", {
|
|
@@ -58703,7 +59060,7 @@ object({
|
|
|
58703
59060
|
label: "Members"
|
|
58704
59061
|
}))
|
|
58705
59062
|
});
|
|
58706
|
-
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light;
|
|
59063
|
+
DeviceType.Sensor, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Cover, DeviceType.Valve, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.Thermostat, DeviceType.Climate;
|
|
58707
59064
|
object({
|
|
58708
59065
|
/** Radiator valve opening 0 (closed) .. 100 (open). Null = not yet known. */
|
|
58709
59066
|
position: number().min(0).max(100).nullable().meta(readingMeta("percentage")),
|
|
@@ -59610,7 +59967,14 @@ DeviceType.Lock, method(object({
|
|
|
59610
59967
|
auth: "protected"
|
|
59611
59968
|
}), method(object({
|
|
59612
59969
|
deviceId: number().int().nonnegative(),
|
|
59613
|
-
code: string().min(1).optional()
|
|
59970
|
+
code: string().min(1).optional(),
|
|
59971
|
+
/**
|
|
59972
|
+
* Honoured by a helper lock GROUP only (D944): unlocking several doors
|
|
59973
|
+
* with one call is refused unless the caller says it was confirmed. A
|
|
59974
|
+
* single lock ignores it. The refusal lives in the callee, so no
|
|
59975
|
+
* automation or API caller reaches the doors by skipping a dialog.
|
|
59976
|
+
*/
|
|
59977
|
+
confirm: boolean().optional()
|
|
59614
59978
|
}), _void(), {
|
|
59615
59979
|
kind: "mutation",
|
|
59616
59980
|
auth: "protected"
|
|
@@ -59618,6 +59982,11 @@ DeviceType.Lock, method(object({
|
|
|
59618
59982
|
kind: "mutation",
|
|
59619
59983
|
auth: "protected"
|
|
59620
59984
|
});
|
|
59985
|
+
object({
|
|
59986
|
+
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
59987
|
+
lastFetchedAt: number()
|
|
59988
|
+
});
|
|
59989
|
+
DeviceType.Sensor;
|
|
59621
59990
|
/**
|
|
59622
59991
|
* Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
|
|
59623
59992
|
* Apple TV, Spotify, Roku, generic OTT receivers, …) on
|
|
@@ -61525,9 +61894,9 @@ object({
|
|
|
61525
61894
|
/** Cumulative energy in kilowatt-hours since the meter was reset. */
|
|
61526
61895
|
kwhTotal: number().optional().meta(readingMeta("energy", { label: "Energy" })),
|
|
61527
61896
|
/** Voltage in volts. */
|
|
61528
|
-
volts: number().optional().meta(readingMeta("voltage")),
|
|
61897
|
+
volts: number().optional().meta(readingMeta("voltage", { label: "Voltage" })),
|
|
61529
61898
|
/** Current in amperes. */
|
|
61530
|
-
amps: number().optional().meta(readingMeta("current")),
|
|
61899
|
+
amps: number().optional().meta(readingMeta("current", { label: "Current" })),
|
|
61531
61900
|
/** Apparent power in volt-amperes. */
|
|
61532
61901
|
va: number().optional().meta(readingMeta("apparent-power")),
|
|
61533
61902
|
/** Power factor (0..1) if reported. */
|
|
@@ -61535,7 +61904,23 @@ object({
|
|
|
61535
61904
|
/** Ms epoch when the slice was last updated. */
|
|
61536
61905
|
lastFetchedAt: number()
|
|
61537
61906
|
});
|
|
61538
|
-
|
|
61907
|
+
[
|
|
61908
|
+
DeviceType.Sensor,
|
|
61909
|
+
DeviceType.Switch,
|
|
61910
|
+
DeviceType.Light,
|
|
61911
|
+
DeviceType.Fan,
|
|
61912
|
+
DeviceType.Climate,
|
|
61913
|
+
DeviceType.Thermostat,
|
|
61914
|
+
DeviceType.Humidifier,
|
|
61915
|
+
DeviceType.WaterHeater,
|
|
61916
|
+
DeviceType.MediaPlayer,
|
|
61917
|
+
DeviceType.Washer,
|
|
61918
|
+
DeviceType.Dryer,
|
|
61919
|
+
DeviceType.Dishwasher,
|
|
61920
|
+
DeviceType.Vacuum,
|
|
61921
|
+
DeviceType.LawnMower,
|
|
61922
|
+
DeviceType.PetFeeder
|
|
61923
|
+
].filter((type) => type !== DeviceType.Sensor);
|
|
61539
61924
|
/**
|
|
61540
61925
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
61541
61926
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -61982,7 +62367,7 @@ object({
|
|
|
61982
62367
|
phase: string().nullable()
|
|
61983
62368
|
});
|
|
61984
62369
|
Object.values(DeviceType);
|
|
61985
|
-
DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
62370
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
61986
62371
|
DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
|
|
61987
62372
|
kind: "mutation",
|
|
61988
62373
|
auth: "admin"
|
|
@@ -64021,7 +64406,7 @@ object({
|
|
|
64021
64406
|
/** Ms epoch of the last state change. Useful for UI "X minutes ago". */
|
|
64022
64407
|
lastChangedAt: number()
|
|
64023
64408
|
});
|
|
64024
|
-
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, method(object({
|
|
64409
|
+
DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, method(object({
|
|
64025
64410
|
deviceId: number(),
|
|
64026
64411
|
on: boolean()
|
|
64027
64412
|
}), _void(), {
|
|
@@ -65713,8 +66098,9 @@ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" })
|
|
|
65713
66098
|
kind: "mutation",
|
|
65714
66099
|
auth: "admin"
|
|
65715
66100
|
});
|
|
66101
|
+
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
65716
66102
|
SOURCE_CAPS.filter((cap) => cap !== "switch" && cap !== "motion");
|
|
65717
|
-
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.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
|
|
66103
|
+
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;
|
|
65718
66104
|
String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
|
|
65719
66105
|
DeviceRole.BinarySensor, DeviceRole.MotionSensor;
|
|
65720
66106
|
new Set(CAP_NAMES);
|
|
@@ -65767,7 +66153,7 @@ var AccessoryKind = {
|
|
|
65767
66153
|
PrivacyMask: DeviceRole.PrivacyMask
|
|
65768
66154
|
};
|
|
65769
66155
|
AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, AccessoryKind.PirSensor, AccessoryKind.Chime, AccessoryKind.Autotrack, AccessoryKind.Nightvision, AccessoryKind.PrivacyMask;
|
|
65770
|
-
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher;
|
|
66156
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer;
|
|
65771
66157
|
new Set(Object.values(DeviceType));
|
|
65772
66158
|
Object.freeze({
|
|
65773
66159
|
"accessories.getStatus": {
|
|
@@ -68990,6 +69376,13 @@ Object.freeze({
|
|
|
68990
69376
|
access: "view",
|
|
68991
69377
|
role: "viewer"
|
|
68992
69378
|
},
|
|
69379
|
+
"groupActive.getStatus": {
|
|
69380
|
+
capName: "group-active",
|
|
69381
|
+
capScope: "device",
|
|
69382
|
+
addonId: null,
|
|
69383
|
+
access: "view",
|
|
69384
|
+
role: "viewer"
|
|
69385
|
+
},
|
|
68993
69386
|
"groupCommands.getStatus": {
|
|
68994
69387
|
capName: "group-commands",
|
|
68995
69388
|
capScope: "device",
|
|
@@ -69956,6 +70349,13 @@ Object.freeze({
|
|
|
69956
70349
|
access: "create",
|
|
69957
70350
|
role: "admin"
|
|
69958
70351
|
},
|
|
70352
|
+
"meter.getStatus": {
|
|
70353
|
+
capName: "meter",
|
|
70354
|
+
capScope: "device",
|
|
70355
|
+
addonId: null,
|
|
70356
|
+
access: "view",
|
|
70357
|
+
role: "viewer"
|
|
70358
|
+
},
|
|
69959
70359
|
"metricsProvider.collectSnapshot": {
|
|
69960
70360
|
capName: "metrics-provider",
|
|
69961
70361
|
capScope: "system",
|
|
@@ -78054,7 +78454,39 @@ var APPLIANCE_CAP_VOCABULARY = {
|
|
|
78054
78454
|
phrase: t("When {device} finishes", "Quando {device} finisce"),
|
|
78055
78455
|
icon: "square"
|
|
78056
78456
|
}
|
|
78057
|
-
}
|
|
78457
|
+
},
|
|
78458
|
+
overrides: { byType: {
|
|
78459
|
+
[DeviceType.Washer]: {
|
|
78460
|
+
state: { running: {
|
|
78461
|
+
whenTrue: t("When the washing machine starts", "Quando la lavatrice inizia"),
|
|
78462
|
+
whenFalse: t("When the washing machine has finished", "Quando la lavatrice ha finito")
|
|
78463
|
+
} },
|
|
78464
|
+
events: {
|
|
78465
|
+
onStarted: { phrase: t("When the washing machine starts", "Quando la lavatrice inizia") },
|
|
78466
|
+
onFinished: { phrase: t("When the washing machine has finished", "Quando la lavatrice ha finito") }
|
|
78467
|
+
}
|
|
78468
|
+
},
|
|
78469
|
+
[DeviceType.Dryer]: {
|
|
78470
|
+
state: { running: {
|
|
78471
|
+
whenTrue: t("When the dryer starts", "Quando l'asciugatrice inizia"),
|
|
78472
|
+
whenFalse: t("When the dryer has finished", "Quando l'asciugatrice ha finito")
|
|
78473
|
+
} },
|
|
78474
|
+
events: {
|
|
78475
|
+
onStarted: { phrase: t("When the dryer starts", "Quando l'asciugatrice inizia") },
|
|
78476
|
+
onFinished: { phrase: t("When the dryer has finished", "Quando l'asciugatrice ha finito") }
|
|
78477
|
+
}
|
|
78478
|
+
},
|
|
78479
|
+
[DeviceType.Dishwasher]: {
|
|
78480
|
+
state: { running: {
|
|
78481
|
+
whenTrue: t("When the dishwasher starts", "Quando la lavastoviglie inizia"),
|
|
78482
|
+
whenFalse: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito")
|
|
78483
|
+
} },
|
|
78484
|
+
events: {
|
|
78485
|
+
onStarted: { phrase: t("When the dishwasher starts", "Quando la lavastoviglie inizia") },
|
|
78486
|
+
onFinished: { phrase: t("When the dishwasher has finished", "Quando la lavastoviglie ha finito") }
|
|
78487
|
+
}
|
|
78488
|
+
}
|
|
78489
|
+
} }
|
|
78058
78490
|
},
|
|
78059
78491
|
update: {
|
|
78060
78492
|
scope: "device",
|
|
@@ -78826,6 +79258,10 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78826
79258
|
icon: "flame"
|
|
78827
79259
|
}]
|
|
78828
79260
|
},
|
|
79261
|
+
"group-active": {
|
|
79262
|
+
scope: "device",
|
|
79263
|
+
noun: t("Group state", "Stato del gruppo")
|
|
79264
|
+
},
|
|
78829
79265
|
"group-commands": {
|
|
78830
79266
|
scope: "device",
|
|
78831
79267
|
noun: t("Group command", "Comando di gruppo")
|
|
@@ -78882,6 +79318,17 @@ var SENSOR_CAP_VOCABULARY = {
|
|
|
78882
79318
|
}],
|
|
78883
79319
|
events: { onMotionChanged: { notOperatorFacing: "the `detected` state trigger phrases both edges, from the mirror that survives a dropped event (D49)" } }
|
|
78884
79320
|
},
|
|
79321
|
+
meter: {
|
|
79322
|
+
scope: "device",
|
|
79323
|
+
noun: t("Meter", "Contatore"),
|
|
79324
|
+
state: [{
|
|
79325
|
+
kind: "numeric",
|
|
79326
|
+
field: "value",
|
|
79327
|
+
quantity: t("the total", "il totale"),
|
|
79328
|
+
label: t("Total", "Totale"),
|
|
79329
|
+
icon: "gauge"
|
|
79330
|
+
}]
|
|
79331
|
+
},
|
|
78885
79332
|
"numeric-sensor": {
|
|
78886
79333
|
scope: "device",
|
|
78887
79334
|
noun: t("Sensor", "Sensore"),
|
|
@@ -79354,8 +79801,47 @@ function projectAction(cap, action) {
|
|
|
79354
79801
|
};
|
|
79355
79802
|
}
|
|
79356
79803
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
79357
|
-
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;
|
|
79804
|
+
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;
|
|
79358
79805
|
DeviceFeature.BatteryOperated;
|
|
79806
|
+
DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher;
|
|
79807
|
+
var HA_AS = "a power sensor (device_class power). Home Assistant has no laundry domain. running-state stays unexported until a null reading can be published as unavailable.";
|
|
79808
|
+
var HOMEKIT_REASON = "HomeKit has no laundry-appliance service. The export tab would pair an accessory with nothing on it: running-state is withheld until a null reading can be unavailable, and power is not a HomeKit service this export honours.";
|
|
79809
|
+
var ALEXA_REASON = "Alexa has no laundry category in this export, and neither power nor running has an Alexa interface.";
|
|
79810
|
+
var GOOGLE_REASON = "Google WASHER, DRYER and DISHWASHER require RunCycle. A power threshold is not that trait, and this export does not publish RunCycle.";
|
|
79811
|
+
var MATTER_REASON = "No Matter export is built. The closest device types are Laundry Washer (0x0073) and Laundry Dryer (0x0074); a power threshold is not the Laundry Washer Mode cluster.";
|
|
79812
|
+
function withheld(reason) {
|
|
79813
|
+
return {
|
|
79814
|
+
[DeviceType.Washer]: {
|
|
79815
|
+
exported: false,
|
|
79816
|
+
reason
|
|
79817
|
+
},
|
|
79818
|
+
[DeviceType.Dryer]: {
|
|
79819
|
+
exported: false,
|
|
79820
|
+
reason
|
|
79821
|
+
},
|
|
79822
|
+
[DeviceType.Dishwasher]: {
|
|
79823
|
+
exported: false,
|
|
79824
|
+
reason
|
|
79825
|
+
}
|
|
79826
|
+
};
|
|
79827
|
+
}
|
|
79828
|
+
function offered(received) {
|
|
79829
|
+
return {
|
|
79830
|
+
[DeviceType.Washer]: {
|
|
79831
|
+
exported: true,
|
|
79832
|
+
as: received
|
|
79833
|
+
},
|
|
79834
|
+
[DeviceType.Dryer]: {
|
|
79835
|
+
exported: true,
|
|
79836
|
+
as: received
|
|
79837
|
+
},
|
|
79838
|
+
[DeviceType.Dishwasher]: {
|
|
79839
|
+
exported: true,
|
|
79840
|
+
as: received
|
|
79841
|
+
}
|
|
79842
|
+
};
|
|
79843
|
+
}
|
|
79844
|
+
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
79359
79845
|
Object.freeze({
|
|
79360
79846
|
"accessories.getStatus": [{
|
|
79361
79847
|
name: "deviceId",
|
|
@@ -80322,6 +80808,11 @@ Object.freeze({
|
|
|
80322
80808
|
form: "single",
|
|
80323
80809
|
optional: false
|
|
80324
80810
|
}],
|
|
80811
|
+
"groupActive.getStatus": [{
|
|
80812
|
+
name: "deviceId",
|
|
80813
|
+
form: "single",
|
|
80814
|
+
optional: false
|
|
80815
|
+
}],
|
|
80325
80816
|
"groupCommands.getStatus": [{
|
|
80326
80817
|
name: "deviceId",
|
|
80327
80818
|
form: "single",
|
|
@@ -80532,6 +81023,11 @@ Object.freeze({
|
|
|
80532
81023
|
form: "single",
|
|
80533
81024
|
optional: false
|
|
80534
81025
|
}],
|
|
81026
|
+
"meter.getStatus": [{
|
|
81027
|
+
name: "deviceId",
|
|
81028
|
+
form: "single",
|
|
81029
|
+
optional: false
|
|
81030
|
+
}],
|
|
80535
81031
|
"motion.getStatus": [{
|
|
80536
81032
|
name: "deviceId",
|
|
80537
81033
|
form: "single",
|
|
@@ -82139,7 +82635,6 @@ Object.freeze({
|
|
|
82139
82635
|
"network-access": "ingress",
|
|
82140
82636
|
"smtp-provider": "email"
|
|
82141
82637
|
});
|
|
82142
|
-
DeviceType.Camera;
|
|
82143
82638
|
/**
|
|
82144
82639
|
* A saved Customize assignment (D936). The schema is the type: callers use
|
|
82145
82640
|
* `z.infer`, they do not restate the shape.
|
|
@@ -83888,7 +84383,7 @@ function buildEmailDescriptor(providers = []) {
|
|
|
83888
84383
|
id: "email",
|
|
83889
84384
|
title: "Email",
|
|
83890
84385
|
titleKey: "notifiers:emailDescriptor.s_email.title",
|
|
83891
|
-
description: "Send via a Connectivity → Email provider (Google
|
|
84386
|
+
description: "Send via a Connectivity → Email provider (Google or Email (SMTP)). Install and Verify a provider first.",
|
|
83892
84387
|
descriptionKey: "notifiers:emailDescriptor.s_email.description",
|
|
83893
84388
|
fields: [
|
|
83894
84389
|
{
|
|
@@ -83905,7 +84400,7 @@ function buildEmailDescriptor(providers = []) {
|
|
|
83905
84400
|
key: "smtpAddonId",
|
|
83906
84401
|
label: "Email provider",
|
|
83907
84402
|
labelKey: "notifiers:emailDescriptor.s_email.smtpAddonId.label",
|
|
83908
|
-
description: options.length === 0 ? "No Email provider installed. Open Connectivity → Email, install Google
|
|
84403
|
+
description: options.length === 0 ? "No Email provider installed. Open Connectivity → Email, install Google or Email (SMTP), then Verify." : "Which Connectivity → Email entry delivers this target. Verify it under Connectivity → Email if the label says not verified.",
|
|
83909
84404
|
required: true,
|
|
83910
84405
|
options
|
|
83911
84406
|
},
|
|
@@ -86205,7 +86700,7 @@ var HTTP_NOTIFIER_ADAPTERS = [
|
|
|
86205
86700
|
*/
|
|
86206
86701
|
function buildNotifierAdapters(smtp) {
|
|
86207
86702
|
const emailSmtp = smtp ?? { async sendEmail() {
|
|
86208
|
-
throw new Error("email: no smtp-provider client — install Google
|
|
86703
|
+
throw new Error("email: no smtp-provider client — install Google or Email (SMTP) under Connectivity → Email and Verify it");
|
|
86209
86704
|
} };
|
|
86210
86705
|
return [...HTTP_NOTIFIER_ADAPTERS, createEmailAdapter(emailSmtp)];
|
|
86211
86706
|
}
|