@camstack/addon-provider-bosch 0.1.51 → 0.1.53
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 +13 -0
- package/dist/{account-integration-offline-7CH3r5jI.js → account-integration-offline-C-KJYRdX.js} +1 -1
- package/dist/{account-integration-offline-B1Wgr_rT.mjs → account-integration-offline-KQcJ_VVK.mjs} +1 -1
- package/dist/{bosch-instances-CYBnIcHg.js → bosch-instances-dqTG9no9.js} +1126 -241
- package/dist/{bosch-instances-CdvcBYye.mjs → bosch-instances-y_A00js_.mjs} +1115 -242
- package/dist/home-connect/addon.js +5 -3
- package/dist/home-connect/addon.mjs +5 -3
- package/dist/homecom/addon.js +14 -7
- package/dist/homecom/addon.mjs +14 -7
- package/dist/shc/addon.js +163 -133
- package/dist/shc/addon.mjs +163 -133
- package/package.json +2 -2
|
@@ -802,7 +802,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
802
802
|
return EventCategory;
|
|
803
803
|
}({});
|
|
804
804
|
//#endregion
|
|
805
|
-
//#region ../types/dist/composed-stable-id-
|
|
805
|
+
//#region ../types/dist/composed-stable-id-CPp3votu.mjs
|
|
806
806
|
/**
|
|
807
807
|
import { errMsg } from '@camstack/types'
|
|
808
808
|
* Extract a human-readable message from an unknown error value.
|
|
@@ -821,12 +821,12 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
821
821
|
DeviceType["Siren"] = "siren";
|
|
822
822
|
DeviceType["Switch"] = "switch";
|
|
823
823
|
DeviceType["Sensor"] = "sensor";
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
* `climate-control`
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
824
|
+
/** Every climate device: an air conditioner, a room thermostat, a radiator
|
|
825
|
+
* valve, a floor-heating zone, a heat-pump circuit. They share one set of
|
|
826
|
+
* controls (`climate-control`), so they are one type; WHICH kind of climate
|
|
827
|
+
* device it is is its {@link DeviceRole} (`climate-*`), which the UI and the
|
|
828
|
+
* exports branch on. The retired `thermostat` type is read as this one
|
|
829
|
+
* (`canonicalStoredDeviceType`). */
|
|
830
830
|
DeviceType["Climate"] = "climate";
|
|
831
831
|
DeviceType["Button"] = "button";
|
|
832
832
|
/** Generic stateless event emitter — carries a device's EXACT declared
|
|
@@ -917,10 +917,6 @@ var DeviceType = /* @__PURE__ */ function(DeviceType) {
|
|
|
917
917
|
/** Washing machine. A plug-built one is running while its power stays
|
|
918
918
|
* above a threshold. No program cap — that surface is the dishwasher's. */
|
|
919
919
|
DeviceType["Washer"] = "washer";
|
|
920
|
-
/** A dumb plug-in stove or heater: a switch (and, with a meter, a power
|
|
921
|
-
* reading) that only runs while the plug is on. Built from a smart plug;
|
|
922
|
-
* the `timed-run` cap switches it off again after a chosen time. */
|
|
923
|
-
DeviceType["Heater"] = "heater";
|
|
924
920
|
/** Tumble dryer. Same plug-built running surface as {@link Washer}. */
|
|
925
921
|
DeviceType["Dryer"] = "dryer";
|
|
926
922
|
/** The physical bridge box of a vendor ecosystem (Velux KLF-200, and later
|
|
@@ -1140,6 +1136,27 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
|
|
|
1140
1136
|
/** A smart plug: a `switch` whose source says it is an outlet (HA
|
|
1141
1137
|
* `device_class: outlet`). The Alexa export shows it as a SMARTPLUG. */
|
|
1142
1138
|
DeviceRole["Outlet"] = "outlet";
|
|
1139
|
+
/** A dumb plug-in stove or heater behind a switch: it runs only while the
|
|
1140
|
+
* switch is on. Offered `timed-run`, which switches it off again after a
|
|
1141
|
+
* chosen time. */
|
|
1142
|
+
DeviceRole["SwitchHeater"] = "switch-heater";
|
|
1143
|
+
/** A plain fan behind a switch (a smart plug powering it): drawn as a fan,
|
|
1144
|
+
* with the switch's on/off only. Speed or oscillation appear only when the
|
|
1145
|
+
* device also has a fan cap (D1033). */
|
|
1146
|
+
DeviceRole["SwitchFan"] = "switch-fan";
|
|
1147
|
+
/** Air conditioner / split unit: cooling, fan speed, louvres. */
|
|
1148
|
+
DeviceRole["ClimateAc"] = "climate-ac";
|
|
1149
|
+
/** Room thermostat: a setpoint and a room temperature. The default. */
|
|
1150
|
+
DeviceRole["ClimateThermostat"] = "climate-thermostat";
|
|
1151
|
+
/** Floor-heating zone actuator. */
|
|
1152
|
+
DeviceRole["ClimateFloor"] = "climate-floor";
|
|
1153
|
+
/** Radiator valve (thermostatic radiator head). */
|
|
1154
|
+
DeviceRole["ClimateRadiator"] = "climate-radiator";
|
|
1155
|
+
/** A heater with its own thermostat (stove, convector). */
|
|
1156
|
+
DeviceRole["ClimateHeater"] = "climate-heater";
|
|
1157
|
+
/** One heating/cooling circuit of a heat pump: a water loop with comfort /
|
|
1158
|
+
* eco setpoints and a heat/cool mode, no room sensor. */
|
|
1159
|
+
DeviceRole["ClimateHeatPumpCircuit"] = "climate-heat-pump-circuit";
|
|
1143
1160
|
/** Generic motion / occupancy / moving event source. Distinct from
|
|
1144
1161
|
* the camera accessory PirSensor role: that one is a camera child;
|
|
1145
1162
|
* this is a standalone HA / 3rd-party motion sensor. */
|
|
@@ -6027,7 +6044,7 @@ var ZodIssueCode = {
|
|
|
6027
6044
|
var ZodFirstPartyTypeKind;
|
|
6028
6045
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6029
6046
|
//#endregion
|
|
6030
|
-
//#region ../types/dist/sleep-
|
|
6047
|
+
//#region ../types/dist/sleep-D0roI_CZ.mjs
|
|
6031
6048
|
/**
|
|
6032
6049
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6033
6050
|
* window to float samples (D455).
|
|
@@ -11804,6 +11821,50 @@ var ACTUATOR_CAP_VOCABULARY = {
|
|
|
11804
11821
|
value: false
|
|
11805
11822
|
},
|
|
11806
11823
|
args: { oscillating: false }
|
|
11824
|
+
},
|
|
11825
|
+
{
|
|
11826
|
+
id: "fan.horizontalOscillateOn",
|
|
11827
|
+
method: "setOscillationAxis",
|
|
11828
|
+
label: t("Horizontal oscillation on", "Oscillazione orizzontale attiva"),
|
|
11829
|
+
phrase: t("Start the horizontal oscillation of {device}", "Attiva l’oscillazione orizzontale di {device}"),
|
|
11830
|
+
icon: "move-horizontal",
|
|
11831
|
+
args: {
|
|
11832
|
+
axis: "horizontal",
|
|
11833
|
+
oscillating: true
|
|
11834
|
+
}
|
|
11835
|
+
},
|
|
11836
|
+
{
|
|
11837
|
+
id: "fan.horizontalOscillateOff",
|
|
11838
|
+
method: "setOscillationAxis",
|
|
11839
|
+
label: t("Horizontal oscillation off", "Oscillazione orizzontale spenta"),
|
|
11840
|
+
phrase: t("Stop the horizontal oscillation of {device}", "Ferma l’oscillazione orizzontale di {device}"),
|
|
11841
|
+
icon: "move-horizontal",
|
|
11842
|
+
args: {
|
|
11843
|
+
axis: "horizontal",
|
|
11844
|
+
oscillating: false
|
|
11845
|
+
}
|
|
11846
|
+
},
|
|
11847
|
+
{
|
|
11848
|
+
id: "fan.verticalOscillateOn",
|
|
11849
|
+
method: "setOscillationAxis",
|
|
11850
|
+
label: t("Vertical oscillation on", "Oscillazione verticale attiva"),
|
|
11851
|
+
phrase: t("Start the vertical oscillation of {device}", "Attiva l’oscillazione verticale di {device}"),
|
|
11852
|
+
icon: "move-vertical",
|
|
11853
|
+
args: {
|
|
11854
|
+
axis: "vertical",
|
|
11855
|
+
oscillating: true
|
|
11856
|
+
}
|
|
11857
|
+
},
|
|
11858
|
+
{
|
|
11859
|
+
id: "fan.verticalOscillateOff",
|
|
11860
|
+
method: "setOscillationAxis",
|
|
11861
|
+
label: t("Vertical oscillation off", "Oscillazione verticale spenta"),
|
|
11862
|
+
phrase: t("Stop the vertical oscillation of {device}", "Ferma l’oscillazione verticale di {device}"),
|
|
11863
|
+
icon: "move-vertical",
|
|
11864
|
+
args: {
|
|
11865
|
+
axis: "vertical",
|
|
11866
|
+
oscillating: false
|
|
11867
|
+
}
|
|
11807
11868
|
}
|
|
11808
11869
|
],
|
|
11809
11870
|
state: [{
|
|
@@ -16181,6 +16242,10 @@ Object.freeze({
|
|
|
16181
16242
|
"path": "lastEvent.eventType",
|
|
16182
16243
|
"valueType": "string"
|
|
16183
16244
|
},
|
|
16245
|
+
{
|
|
16246
|
+
"path": "lastEvent.sourceId",
|
|
16247
|
+
"valueType": "string"
|
|
16248
|
+
},
|
|
16184
16249
|
{
|
|
16185
16250
|
"path": "lastEvent.timestamp",
|
|
16186
16251
|
"valueType": "number"
|
|
@@ -17444,7 +17509,7 @@ Object.freeze({
|
|
|
17444
17509
|
}
|
|
17445
17510
|
});
|
|
17446
17511
|
//#endregion
|
|
17447
|
-
//#region ../types/dist/implemented-methods-
|
|
17512
|
+
//#region ../types/dist/implemented-methods-5YMDeE3f.mjs
|
|
17448
17513
|
/**
|
|
17449
17514
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
17450
17515
|
*
|
|
@@ -44153,6 +44218,13 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
44153
44218
|
"in"
|
|
44154
44219
|
]);
|
|
44155
44220
|
/**
|
|
44221
|
+
* The stable name of an event leaf. A path (`when.0.1`) moves when a row is
|
|
44222
|
+
* reordered; the id does not, so a `triggered-by` leaf keeps pointing at the
|
|
44223
|
+
* same row. Optional: a leaf nothing refers to needs none.
|
|
44224
|
+
*/
|
|
44225
|
+
var EVENT_LEAF_ID_RE = /^[A-Za-z0-9_-]{1,40}$/;
|
|
44226
|
+
var eventLeafId = { id: string().regex(EVENT_LEAF_ID_RE).optional() };
|
|
44227
|
+
/**
|
|
44156
44228
|
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
44157
44229
|
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
44158
44230
|
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
@@ -44160,6 +44232,7 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
44160
44232
|
*/
|
|
44161
44233
|
var DeviceBecomesLeafSchema = object({
|
|
44162
44234
|
kind: literal("device-becomes"),
|
|
44235
|
+
...eventLeafId,
|
|
44163
44236
|
deviceId: number$1().int().nonnegative(),
|
|
44164
44237
|
cap: string().min(1),
|
|
44165
44238
|
fieldPath: string().min(1),
|
|
@@ -44212,6 +44285,7 @@ var DeviceBecomesLeafSchema = object({
|
|
|
44212
44285
|
});
|
|
44213
44286
|
var DeviceEventLeafSchema = object({
|
|
44214
44287
|
kind: literal("device-event"),
|
|
44288
|
+
...eventLeafId,
|
|
44215
44289
|
/** An `EventCategory` value. */
|
|
44216
44290
|
category: string().min(1),
|
|
44217
44291
|
deviceId: number$1().int().nonnegative().optional()
|
|
@@ -44224,18 +44298,24 @@ var DeviceEventLeafSchema = object({
|
|
|
44224
44298
|
*/
|
|
44225
44299
|
var ScheduleLeafSchema = object({
|
|
44226
44300
|
kind: literal("schedule"),
|
|
44301
|
+
...eventLeafId,
|
|
44227
44302
|
at: TimeSpecSchema.optional(),
|
|
44228
44303
|
days: array(WeekdayKeySchema).min(1).max(7).optional(),
|
|
44229
44304
|
/** @deprecated legacy cron — new schedules use `at`. */
|
|
44230
44305
|
cron: string().min(1).max(120).optional()
|
|
44231
44306
|
}).refine((leaf) => leaf.at === void 0 !== (leaf.cron === void 0), { message: "a schedule names exactly one of `at` (a time spec) and `cron` (legacy)" }).refine((leaf) => leaf.days === void 0 || leaf.at !== void 0, { message: "`days` goes with `at`; a cron expression carries its own days" });
|
|
44232
|
-
var ManualLeafSchema = object({
|
|
44307
|
+
var ManualLeafSchema = object({
|
|
44308
|
+
kind: literal("manual"),
|
|
44309
|
+
...eventLeafId
|
|
44310
|
+
});
|
|
44233
44311
|
var IntervalLeafSchema = object({
|
|
44234
44312
|
kind: literal("interval"),
|
|
44313
|
+
...eventLeafId,
|
|
44235
44314
|
minutes: number$1().int().min(1).max(1440)
|
|
44236
44315
|
});
|
|
44237
44316
|
var DetectionLeafSchema = object({
|
|
44238
44317
|
kind: literal("detection"),
|
|
44318
|
+
...eventLeafId,
|
|
44239
44319
|
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
44240
44320
|
subjects: array(DetectionSubjectSchema).min(1),
|
|
44241
44321
|
deviceIds: array(number$1().int().nonnegative()).min(1),
|
|
@@ -44246,6 +44326,7 @@ var DetectionLeafSchema = object({
|
|
|
44246
44326
|
});
|
|
44247
44327
|
var StationaryLeafSchema = object({
|
|
44248
44328
|
kind: literal("stationary"),
|
|
44329
|
+
...eventLeafId,
|
|
44249
44330
|
deviceIds: array(number$1().int().nonnegative()).min(1),
|
|
44250
44331
|
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
44251
44332
|
zones: array(string().min(1)).min(1).optional(),
|
|
@@ -44291,6 +44372,41 @@ var StationaryPresentLeafSchema = object({
|
|
|
44291
44372
|
minCount: number$1().int().positive().optional()
|
|
44292
44373
|
});
|
|
44293
44374
|
/**
|
|
44375
|
+
* True while the site-local clock is inside a daily window and/or on one of
|
|
44376
|
+
* the named days. `days` names the local day a window STARTS on, so
|
|
44377
|
+
* 22:00-06:00 on weekdays is still inside at 01:00 Saturday. A one-date spec
|
|
44378
|
+
* (`kind: 'date'`) is refused: only a clock time or a sun event makes a window
|
|
44379
|
+
* that repeats every day.
|
|
44380
|
+
*/
|
|
44381
|
+
var TimeIsLeafSchema = object({
|
|
44382
|
+
kind: literal("time-is"),
|
|
44383
|
+
between: object({
|
|
44384
|
+
from: TimeSpecSchema,
|
|
44385
|
+
to: TimeSpecSchema
|
|
44386
|
+
}).optional(),
|
|
44387
|
+
days: array(WeekdayKeySchema).min(1).max(7).optional()
|
|
44388
|
+
}).superRefine((leaf, ctx) => {
|
|
44389
|
+
if (leaf.between === void 0 && leaf.days === void 0) ctx.addIssue({
|
|
44390
|
+
code: "custom",
|
|
44391
|
+
message: "a time-is leaf needs a between window, days, or both"
|
|
44392
|
+
});
|
|
44393
|
+
for (const end of ["from", "to"]) if (leaf.between?.[end].kind === "date") ctx.addIssue({
|
|
44394
|
+
code: "custom",
|
|
44395
|
+
message: "a one-date time cannot bound a daily window: use a clock time or a sun event",
|
|
44396
|
+
path: ["between", end]
|
|
44397
|
+
});
|
|
44398
|
+
});
|
|
44399
|
+
/**
|
|
44400
|
+
* True for the run that one of the named event leaves started. It reads the
|
|
44401
|
+
* run, not a device: in the tree it is judged against the leaf that fired, in
|
|
44402
|
+
* a step guard against the trigger the run carries. A run no event leaf
|
|
44403
|
+
* started (a state-only tree becoming true) is false.
|
|
44404
|
+
*/
|
|
44405
|
+
var TriggeredByLeafSchema = object({
|
|
44406
|
+
kind: literal("triggered-by"),
|
|
44407
|
+
leafIds: array(string().regex(EVENT_LEAF_ID_RE)).min(1).max(20)
|
|
44408
|
+
});
|
|
44409
|
+
/**
|
|
44294
44410
|
* The escape hatch: the bounded expression engine over named device bindings.
|
|
44295
44411
|
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
44296
44412
|
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
@@ -44336,7 +44452,9 @@ var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
44336
44452
|
StationaryLeafSchema,
|
|
44337
44453
|
DeviceIsLeafSchema,
|
|
44338
44454
|
StationaryPresentLeafSchema,
|
|
44339
|
-
ExpressionLeafSchema
|
|
44455
|
+
ExpressionLeafSchema,
|
|
44456
|
+
TimeIsLeafSchema,
|
|
44457
|
+
TriggeredByLeafSchema
|
|
44340
44458
|
]));
|
|
44341
44459
|
var EVENT_KINDS = new Set([
|
|
44342
44460
|
"device-becomes",
|
|
@@ -44390,6 +44508,28 @@ function hasEventLeaves(node) {
|
|
|
44390
44508
|
});
|
|
44391
44509
|
return found;
|
|
44392
44510
|
}
|
|
44511
|
+
/** The event leaves of the tree that carry an id, in tree order. */
|
|
44512
|
+
function identifiedEventLeaves(when) {
|
|
44513
|
+
const found = [];
|
|
44514
|
+
forEachLeaf(when, (leaf, path) => {
|
|
44515
|
+
if (isEventLeaf(leaf) && leaf.id !== void 0) found.push({
|
|
44516
|
+
id: leaf.id,
|
|
44517
|
+
path,
|
|
44518
|
+
leaf
|
|
44519
|
+
});
|
|
44520
|
+
});
|
|
44521
|
+
return found;
|
|
44522
|
+
}
|
|
44523
|
+
/** Does the tree hold a `triggered-by` leaf? */
|
|
44524
|
+
function hasTriggeredByLeaves(when) {
|
|
44525
|
+
let found = false;
|
|
44526
|
+
forEachLeaf(when, (leaf) => {
|
|
44527
|
+
if (leaf.kind === "triggered-by") found = true;
|
|
44528
|
+
});
|
|
44529
|
+
return found;
|
|
44530
|
+
}
|
|
44531
|
+
/** Why a `triggered-by` leaf is refused outside an automation: there is no trigger row to name. */
|
|
44532
|
+
var TRIGGERED_BY_NEEDS_AUTOMATION = "\"triggered by\" reads which trigger started an automation — an action or a scheduler has no trigger rows, so it could never be true here";
|
|
44393
44533
|
function hasEmptyContainer(node) {
|
|
44394
44534
|
if (isLeaf(node)) return false;
|
|
44395
44535
|
if (node.kind === "not") return hasEmptyContainer(node.child);
|
|
@@ -44448,6 +44588,11 @@ function validateWhenTree(when) {
|
|
|
44448
44588
|
if (depth > 4) return `the rules nest ${depth} levels deep — the limit is 4. "not (any of …)" is the same as "all of (not …)", which is one level shallower`;
|
|
44449
44589
|
const leaves = countWhenLeaves(when);
|
|
44450
44590
|
if (leaves > 32) return `the rules use ${leaves} rows — the limit is 32`;
|
|
44591
|
+
const seen = /* @__PURE__ */ new Set();
|
|
44592
|
+
for (const entry of identifiedEventLeaves(when)) {
|
|
44593
|
+
if (seen.has(entry.id)) return `two trigger rows share the id "${entry.id}" — an id names one row`;
|
|
44594
|
+
seen.add(entry.id);
|
|
44595
|
+
}
|
|
44451
44596
|
const need = eventNeed(when, "when", false);
|
|
44452
44597
|
if ("error" in need) return need.error;
|
|
44453
44598
|
const [first, second] = need.paths;
|
|
@@ -44659,9 +44804,12 @@ function convertLegacyWhen(half) {
|
|
|
44659
44804
|
* every caller — automations, Actions, and the notification centre, which runs
|
|
44660
44805
|
* only Actions (planning#473).
|
|
44661
44806
|
*/
|
|
44807
|
+
/** The longest one `wait` step lasts. It is cut by a restart of the run and does not survive a hub restart. */
|
|
44808
|
+
var WAIT_MAX_SECONDS = 3600;
|
|
44809
|
+
var WAIT_TOO_LONG = `a wait is at most one hour (${WAIT_MAX_SECONDS} s)`;
|
|
44662
44810
|
var AutomationWaitStepSchema = object({
|
|
44663
44811
|
kind: literal("wait"),
|
|
44664
|
-
seconds: number$1().min(0).max(
|
|
44812
|
+
seconds: number$1().min(0).max(WAIT_MAX_SECONDS, WAIT_TOO_LONG)
|
|
44665
44813
|
});
|
|
44666
44814
|
/** The legacy `deviceId` of a cap step: still READ, never written (D983). */
|
|
44667
44815
|
var LegacyStepDeviceSchema = union([
|
|
@@ -44731,28 +44879,227 @@ var elseNeedsIf = {
|
|
|
44731
44879
|
path: ["else"]
|
|
44732
44880
|
};
|
|
44733
44881
|
var GuardedWaitStepSchema = AutomationWaitStepSchema.extend(stepGuardShape).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44734
|
-
|
|
44882
|
+
/**
|
|
44883
|
+
* A step of a STANDALONE list (an action, a scheduler point, a preset): the
|
|
44884
|
+
* same two step kinds an automation runs, minus a guard that reads the
|
|
44885
|
+
* automation's trigger, which such a list does not have.
|
|
44886
|
+
*/
|
|
44887
|
+
var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, AutomationCapStepBaseSchema.extend(stepGuardShape).refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44888
|
+
message: "a cap step needs a target",
|
|
44889
|
+
path: ["target"]
|
|
44890
|
+
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44891
|
+
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44892
|
+
path: ["actionId"]
|
|
44893
|
+
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf)]).superRefine((step, ctx) => {
|
|
44894
|
+
if (step.if !== void 0 && hasTriggeredByLeaves(step.if)) ctx.addIssue({
|
|
44895
|
+
code: "custom",
|
|
44896
|
+
message: TRIGGERED_BY_NEEDS_AUTOMATION,
|
|
44897
|
+
path: ["if"]
|
|
44898
|
+
});
|
|
44899
|
+
});
|
|
44900
|
+
/**
|
|
44901
|
+
* Flow control of an AUTOMATION's list (#500). A standalone list (an action, a
|
|
44902
|
+
* scheduler point, a preset) stays `wait` and `cap`: it has no run of its own
|
|
44903
|
+
* to end and no variable bag to write.
|
|
44904
|
+
*/
|
|
44905
|
+
var continueOnErrorShape = {
|
|
44906
|
+
/** A failure of this step is traced `failed-continued` and the next step runs. */
|
|
44907
|
+
continueOnError: literal(true).optional() };
|
|
44908
|
+
/** An automation's cap step: the shared guarded cap step, plus `continueOnError`. */
|
|
44909
|
+
var AutomationGuardedCapStepSchema = AutomationCapStepBaseSchema.extend({
|
|
44910
|
+
...stepGuardShape,
|
|
44911
|
+
...continueOnErrorShape
|
|
44912
|
+
}).refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44735
44913
|
message: "a cap step needs a target",
|
|
44736
44914
|
path: ["target"]
|
|
44737
44915
|
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
44738
44916
|
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44739
44917
|
path: ["actionId"]
|
|
44740
44918
|
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44741
|
-
|
|
44919
|
+
/** End the run here, on purpose. `reason` may carry `{{vars.x}}` slots. */
|
|
44920
|
+
var AutomationStopStepSchema = object({
|
|
44921
|
+
kind: literal("stop"),
|
|
44922
|
+
reason: string().min(1, "a stop needs a reason: it is what the run trace shows").max(200),
|
|
44923
|
+
/** `ok`: the run ends as a success. `failed`: it ends as a failure, with the reason. */
|
|
44924
|
+
outcome: _enum(["ok", "failed"]),
|
|
44925
|
+
/** Stop only when this reads true; false or unknown does not stop. */
|
|
44926
|
+
if: AutomationWhenSchema.optional()
|
|
44927
|
+
});
|
|
44928
|
+
var SET_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,39}$/;
|
|
44929
|
+
/** Names the run itself writes and reads (`leaf` is what `triggered-by` judges): a step may not overwrite them. */
|
|
44930
|
+
var RESERVED_RUN_VARS = [
|
|
44931
|
+
"kind",
|
|
44932
|
+
"leaf",
|
|
44933
|
+
"at"
|
|
44934
|
+
];
|
|
44935
|
+
/**
|
|
44936
|
+
* Write variables into the run's bag, for the steps after it. SUBSTITUTION
|
|
44937
|
+
* only, never evaluation: a text value may carry `{{vars.x}}` slots.
|
|
44938
|
+
*/
|
|
44939
|
+
var AutomationSetStepSchema = object({
|
|
44940
|
+
kind: literal("set"),
|
|
44941
|
+
vars: record(string(), union([
|
|
44942
|
+
string().max(500),
|
|
44943
|
+
number$1(),
|
|
44944
|
+
boolean()
|
|
44945
|
+
])).refine((vars) => Object.keys(vars).every((name) => SET_VAR_NAME_RE.test(name)), { message: "a variable name is letters, digits and _, not starting with a digit (max 40)" }).refine((vars) => Object.keys(vars).length >= 1, { message: "set needs at least one variable" }).refine((vars) => Object.keys(vars).length <= 20, { message: `set holds at most 20 variables` }).refine((vars) => Object.keys(vars).every((name) => !RESERVED_RUN_VARS.includes(name)), { message: `the names ${RESERVED_RUN_VARS.join(", ")} belong to the run and cannot be set` }),
|
|
44946
|
+
/** Set only when this reads true; false or unknown leaves the bag as it is. */
|
|
44947
|
+
if: AutomationWhenSchema.optional()
|
|
44948
|
+
});
|
|
44949
|
+
var AutomationRunActionStepSchema = object({
|
|
44950
|
+
kind: literal("action"),
|
|
44951
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
44952
|
+
actionId: string().min(1),
|
|
44953
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
44954
|
+
variables: record(string(), union([
|
|
44955
|
+
string(),
|
|
44956
|
+
number$1(),
|
|
44957
|
+
boolean()
|
|
44958
|
+
])).optional(),
|
|
44959
|
+
...continueOnErrorShape
|
|
44960
|
+
});
|
|
44961
|
+
/**
|
|
44962
|
+
* A step INSIDE a branch of a `choose`: a flow step with no condition of its
|
|
44963
|
+
* own (the branch is the condition) and no block inside it. Strict, so an `if`
|
|
44964
|
+
* written on such a step is refused rather than dropped.
|
|
44965
|
+
*/
|
|
44966
|
+
/** The longest a `wait-until` may wait: six hours. */
|
|
44967
|
+
var WAIT_UNTIL_TIMEOUT_MAX_S = 21600;
|
|
44968
|
+
/**
|
|
44969
|
+
* Pause the run until a condition is true (#500). The timeout is mandatory: a
|
|
44970
|
+
* run never waits without an end. `onTimeout` says what a wait that ran out
|
|
44971
|
+
* does: `continue` goes on to the next step, `stop` ends the run as a failure.
|
|
44972
|
+
* A condition that cannot be read is not "true": the wait goes on (D49).
|
|
44973
|
+
*/
|
|
44974
|
+
var AutomationWaitUntilStepSchema = object({
|
|
44975
|
+
kind: literal("wait-until"),
|
|
44976
|
+
when: AutomationWhenSchema,
|
|
44977
|
+
timeoutS: number$1().int().min(1, "a wait needs a timeout of at least one second").max(WAIT_UNTIL_TIMEOUT_MAX_S, `a wait for a condition is at most six hours (${WAIT_UNTIL_TIMEOUT_MAX_S} s)`),
|
|
44978
|
+
onTimeout: _enum(["continue", "stop"])
|
|
44979
|
+
}).strict();
|
|
44980
|
+
/** The step kinds that hold steps of their own, or pause on a condition: they nest two levels at most. */
|
|
44981
|
+
var BLOCK_STEP_KINDS = [
|
|
44982
|
+
"choose",
|
|
44983
|
+
"repeat",
|
|
44984
|
+
"parallel",
|
|
44985
|
+
"wait-until"
|
|
44986
|
+
];
|
|
44987
|
+
var NEST_TOO_DEEP = `steps nest two levels at most: a step inside a nested choose or repeat can be a plain step only`;
|
|
44988
|
+
function kindOf(input) {
|
|
44989
|
+
return typeof input === "object" && input !== null ? Reflect.get(input, "kind") : void 0;
|
|
44990
|
+
}
|
|
44991
|
+
/** Where the nesting ends, a block step is refused in words rather than as an unknown kind. */
|
|
44992
|
+
function nestTooDeep(issue) {
|
|
44993
|
+
const kind = kindOf(issue.input);
|
|
44994
|
+
return typeof kind === "string" && BLOCK_STEP_KINDS.includes(kind) ? NEST_TOO_DEEP : void 0;
|
|
44995
|
+
}
|
|
44996
|
+
var plainStepSchemas = [
|
|
44997
|
+
AutomationWaitStepSchema.strict(),
|
|
44998
|
+
AutomationCapStepBaseSchema.extend(continueOnErrorShape).strict().refine((step) => step.target !== void 0 || step.deviceId !== void 0, {
|
|
44999
|
+
message: "a cap step needs a target",
|
|
45000
|
+
path: ["target"]
|
|
45001
|
+
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
45002
|
+
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
45003
|
+
path: ["actionId"]
|
|
45004
|
+
}),
|
|
45005
|
+
AutomationStopStepSchema.omit({ if: true }).strict(),
|
|
45006
|
+
AutomationSetStepSchema.omit({ if: true }).strict(),
|
|
45007
|
+
AutomationRunActionStepSchema.strict()
|
|
45008
|
+
];
|
|
45009
|
+
/**
|
|
45010
|
+
* A plain step: a flow step with no condition of its own and no step inside
|
|
45011
|
+
* it. The second (last) level of nesting holds these only.
|
|
45012
|
+
*/
|
|
45013
|
+
var AutomationBranchStepSchema = discriminatedUnion("kind", plainStepSchemas, { error: nestTooDeep });
|
|
45014
|
+
/** A step that can run beside others: it writes no variable and does not end the run. */
|
|
45015
|
+
var AutomationParallelMemberSchema = AutomationBranchStepSchema.refine((step) => step.kind === "cap" || step.kind === "action" || step.kind === "wait", "a parallel holds device commands, actions and waits only: a set or a stop beside other steps has no order");
|
|
45016
|
+
/**
|
|
45017
|
+
* Several steps at once (#500): all are started together and all are awaited.
|
|
45018
|
+
* A failure fails the run once the others finished, as in a sequence; a member
|
|
45019
|
+
* marked "continue if this fails" does not.
|
|
45020
|
+
*/
|
|
45021
|
+
var AutomationParallelStepSchema = object({
|
|
45022
|
+
kind: literal("parallel"),
|
|
45023
|
+
steps: array(AutomationParallelMemberSchema).min(2, "a parallel needs at least two steps").max(5, `a parallel takes at most 5 steps`)
|
|
45024
|
+
}).strict();
|
|
45025
|
+
var REPEAT_NEEDS_MAX = "a repeat on a condition needs a maximum number of iterations";
|
|
45026
|
+
/** A loop on a condition: the bound is mandatory, so a run never loops without an end. */
|
|
45027
|
+
var repeatConditionShape = {
|
|
45028
|
+
when: AutomationWhenSchema,
|
|
45029
|
+
maxIterations: number$1({ error: REPEAT_NEEDS_MAX }).int().min(1, REPEAT_NEEDS_MAX).max(50, `a repeat on a condition runs at most 50 times`),
|
|
45030
|
+
/** A pause between two iterations, never after the last. */
|
|
45031
|
+
waitS: number$1().int().min(1).max(WAIT_MAX_SECONDS, WAIT_TOO_LONG).optional()
|
|
45032
|
+
};
|
|
45033
|
+
/** What decides how many times a `repeat` runs. */
|
|
45034
|
+
var AutomationRepeatLoopSchema = discriminatedUnion("by", [
|
|
45035
|
+
object({
|
|
45036
|
+
by: literal("count"),
|
|
45037
|
+
count: number$1().int().min(1).max(20, `a repeat runs at most 20 times`)
|
|
45038
|
+
}).strict(),
|
|
45039
|
+
object({
|
|
45040
|
+
by: literal("while"),
|
|
45041
|
+
...repeatConditionShape
|
|
45042
|
+
}).strict(),
|
|
45043
|
+
object({
|
|
45044
|
+
by: literal("until"),
|
|
45045
|
+
...repeatConditionShape
|
|
45046
|
+
}).strict(),
|
|
45047
|
+
object({
|
|
45048
|
+
by: literal("forEach"),
|
|
45049
|
+
target: object({
|
|
45050
|
+
kind: literal("devices"),
|
|
45051
|
+
ids: array(number$1().int()).min(1, "a repeat over devices needs at least one device").max(50, `a repeat over devices takes at most 50 devices`)
|
|
45052
|
+
}).strict()
|
|
45053
|
+
}).strict()
|
|
45054
|
+
]);
|
|
45055
|
+
/**
|
|
45056
|
+
* The same steps several times (#500). Every form is bounded at save, so a
|
|
45057
|
+
* run cannot loop without an end. A failure or a `stop` in the steps ends the
|
|
45058
|
+
* loop and the run, as in a sequence.
|
|
45059
|
+
*/
|
|
45060
|
+
function repeatSchemaOf(step) {
|
|
45061
|
+
return object({
|
|
45062
|
+
kind: literal("repeat"),
|
|
45063
|
+
loop: AutomationRepeatLoopSchema,
|
|
45064
|
+
steps: array(step).min(1, "a repeat needs at least one step").max(10, `a repeat takes at most 10 steps`)
|
|
45065
|
+
}).strict();
|
|
45066
|
+
}
|
|
45067
|
+
/**
|
|
45068
|
+
* One step, several ways (#500): the branches are read in order and the FIRST
|
|
45069
|
+
* whose condition is true runs; `else` runs when every branch is false. A
|
|
45070
|
+
* branch that cannot be read ends the choice: nothing runs, not the branches
|
|
45071
|
+
* after it and not the else (D49). The #472 `if` / `else` of a wait or cap
|
|
45072
|
+
* step is the one-branch case and stays as it is stored.
|
|
45073
|
+
*/
|
|
45074
|
+
function chooseSchemaOf(step) {
|
|
45075
|
+
const steps = array(step).max(10);
|
|
45076
|
+
return object({
|
|
45077
|
+
kind: literal("choose"),
|
|
45078
|
+
branches: array(object({
|
|
45079
|
+
if: AutomationWhenSchema,
|
|
45080
|
+
steps: steps.min(1, "a branch needs at least one step")
|
|
45081
|
+
})).min(1, "a choose needs at least one branch").max(6, `a choose takes at most 6 branches`),
|
|
45082
|
+
else: steps.optional()
|
|
45083
|
+
});
|
|
45084
|
+
}
|
|
45085
|
+
var AutomationBodyStepSchema = discriminatedUnion("kind", [
|
|
45086
|
+
...plainStepSchemas,
|
|
45087
|
+
AutomationWaitUntilStepSchema,
|
|
45088
|
+
AutomationParallelStepSchema,
|
|
45089
|
+
chooseSchemaOf(AutomationBranchStepSchema),
|
|
45090
|
+
repeatSchemaOf(AutomationBranchStepSchema)
|
|
45091
|
+
]);
|
|
45092
|
+
var AutomationRepeatStepSchema = repeatSchemaOf(AutomationBodyStepSchema);
|
|
44742
45093
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
44743
45094
|
GuardedWaitStepSchema,
|
|
44744
|
-
|
|
44745
|
-
|
|
44746
|
-
|
|
44747
|
-
|
|
44748
|
-
|
|
44749
|
-
|
|
44750
|
-
|
|
44751
|
-
|
|
44752
|
-
number$1(),
|
|
44753
|
-
boolean()
|
|
44754
|
-
])).optional()
|
|
44755
|
-
}),
|
|
45095
|
+
AutomationGuardedCapStepSchema,
|
|
45096
|
+
AutomationStopStepSchema,
|
|
45097
|
+
AutomationSetStepSchema,
|
|
45098
|
+
chooseSchemaOf(AutomationBodyStepSchema),
|
|
45099
|
+
AutomationWaitUntilStepSchema,
|
|
45100
|
+
AutomationParallelStepSchema,
|
|
45101
|
+
AutomationRepeatStepSchema,
|
|
45102
|
+
AutomationRunActionStepSchema,
|
|
44756
45103
|
object({
|
|
44757
45104
|
kind: literal("code"),
|
|
44758
45105
|
/** Compiled into the automation's OWN block by esbuild — not a third
|
|
@@ -44815,11 +45162,6 @@ function convertLegacyRecipe(legacy) {
|
|
|
44815
45162
|
...rest
|
|
44816
45163
|
};
|
|
44817
45164
|
}
|
|
44818
|
-
/**
|
|
44819
|
-
* Where recipes are PARSED (the core-blocks cap, the MCP tool, the literal a
|
|
44820
|
-
* saved block carries): the new shape as it is, or the old shape converted to
|
|
44821
|
-
* it. The output type is always the new one.
|
|
44822
|
-
*/
|
|
44823
45165
|
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
44824
45166
|
var DETECTION_DESCRIPTIONS = {
|
|
44825
45167
|
deviceId: "the camera that saw it",
|
|
@@ -44835,6 +45177,11 @@ var STATIONARY_DESCRIPTIONS = {
|
|
|
44835
45177
|
phase: "appeared or departed",
|
|
44836
45178
|
entryId: "the stopped object"
|
|
44837
45179
|
};
|
|
45180
|
+
var BECOMES_DESCRIPTIONS = {
|
|
45181
|
+
deviceId: "the device that changed",
|
|
45182
|
+
from: "the value before the change, empty when it is not known",
|
|
45183
|
+
to: "the value it changed to"
|
|
45184
|
+
};
|
|
44838
45185
|
function describe$1(descriptions) {
|
|
44839
45186
|
return Object.entries(descriptions).map(([name, description]) => ({
|
|
44840
45187
|
name,
|
|
@@ -44843,6 +45190,10 @@ function describe$1(descriptions) {
|
|
|
44843
45190
|
}
|
|
44844
45191
|
describe$1(DETECTION_DESCRIPTIONS);
|
|
44845
45192
|
describe$1(STATIONARY_DESCRIPTIONS);
|
|
45193
|
+
describe$1(BECOMES_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45194
|
+
...v,
|
|
45195
|
+
hintKey: "becomesDeviceId"
|
|
45196
|
+
} : v);
|
|
44846
45197
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
44847
45198
|
/**
|
|
44848
45199
|
* Schedulers (D983) — an entity that performs actions over the day.
|
|
@@ -44867,6 +45218,13 @@ var SchedulerStateTreeSchema = AutomationWhenSchema.superRefine((when, ctx) => {
|
|
|
44867
45218
|
});
|
|
44868
45219
|
return;
|
|
44869
45220
|
}
|
|
45221
|
+
if (hasTriggeredByLeaves(when)) {
|
|
45222
|
+
ctx.addIssue({
|
|
45223
|
+
code: "custom",
|
|
45224
|
+
message: TRIGGERED_BY_NEEDS_AUTOMATION
|
|
45225
|
+
});
|
|
45226
|
+
return;
|
|
45227
|
+
}
|
|
44870
45228
|
const problem = validateWhenTree(when);
|
|
44871
45229
|
if (problem !== null) ctx.addIssue({
|
|
44872
45230
|
code: "custom",
|
|
@@ -44888,12 +45246,16 @@ var HoldOverrideSchema = discriminatedUnion("mode", [object({ mode: literal("res
|
|
|
44888
45246
|
})]);
|
|
44889
45247
|
/** A missed edge up to this late still runs. */
|
|
44890
45248
|
var DEFAULT_MAX_LATE_MS = 10 * 6e4;
|
|
45249
|
+
var SchedulerStepSchema = ActionStepSchema.refine((step) => step.kind !== "wait" || step.seconds <= 300, {
|
|
45250
|
+
message: `a scheduler point waits at most five minutes (300 s); put a longer wait in an action`,
|
|
45251
|
+
path: ["seconds"]
|
|
45252
|
+
});
|
|
44891
45253
|
var SchedulerPointSchema = object({
|
|
44892
45254
|
id: string().min(1).max(64),
|
|
44893
45255
|
at: SchedulerTimeSpecSchema,
|
|
44894
45256
|
mode: SchedulerPointModeSchema,
|
|
44895
45257
|
/** The actions of an `edge` or `hold`; a `release` has none. */
|
|
44896
|
-
steps: array(
|
|
45258
|
+
steps: array(SchedulerStepSchema).max(20).default([]),
|
|
44897
45259
|
/** Checked when the point runs (an edge) or is applied (a hold). */
|
|
44898
45260
|
conditions: SchedulerStateTreeSchema.optional(),
|
|
44899
45261
|
/** Hold only; absent means `respect`. */
|
|
@@ -45087,6 +45449,8 @@ var AutomationConditionTraceNodeSchema = object({
|
|
|
45087
45449
|
"device-is",
|
|
45088
45450
|
"expression",
|
|
45089
45451
|
"stationary-present",
|
|
45452
|
+
"time-is",
|
|
45453
|
+
"triggered-by",
|
|
45090
45454
|
"device-becomes",
|
|
45091
45455
|
"device-event",
|
|
45092
45456
|
"schedule",
|
|
@@ -45159,18 +45523,46 @@ var StepTraceBaseSchema = object({
|
|
|
45159
45523
|
]).optional(),
|
|
45160
45524
|
/** Why the condition read what it read, in the evaluator's own words. */
|
|
45161
45525
|
conditionReason: string().optional(),
|
|
45162
|
-
/** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. `skipped`: its `if` was false (or unknown) and nothing ran in its place. */
|
|
45526
|
+
/** `empty`: the target resolved to no device, so nothing was done — never silently `ok`. `skipped`: its `if` was false (or unknown) and nothing ran in its place. `failed-continued`: it failed and was marked to be passed over (#500). */
|
|
45163
45527
|
outcome: _enum([
|
|
45164
45528
|
"ok",
|
|
45165
45529
|
"error",
|
|
45166
45530
|
"not-run",
|
|
45167
45531
|
"empty",
|
|
45168
|
-
"skipped"
|
|
45532
|
+
"skipped",
|
|
45533
|
+
"failed-continued",
|
|
45534
|
+
"timed-out"
|
|
45169
45535
|
]),
|
|
45170
45536
|
message: string().optional()
|
|
45171
45537
|
});
|
|
45538
|
+
/** What a block step adds to its row: which list ran, what each branch read, how many rounds. */
|
|
45539
|
+
var blockTraceShape = {
|
|
45540
|
+
/** A `choose` step (#500): the list that ran, `branches.<n>` or `else`. Absent when none did. */
|
|
45541
|
+
branch: string().optional(),
|
|
45542
|
+
/** A `choose` step: what each branch read, in order, up to the one that decided. */
|
|
45543
|
+
branchChecks: array(object({
|
|
45544
|
+
condition: _enum([
|
|
45545
|
+
"true",
|
|
45546
|
+
"false",
|
|
45547
|
+
"unknown"
|
|
45548
|
+
]),
|
|
45549
|
+
conditionReason: string().optional()
|
|
45550
|
+
})).optional(),
|
|
45551
|
+
/** A `repeat` step (#500): how many times its steps ran. */
|
|
45552
|
+
iterations: number$1().int().optional()
|
|
45553
|
+
};
|
|
45554
|
+
/** A step one level down: it may be a block step itself, whose own steps are plain rows (the nesting ends there). */
|
|
45555
|
+
var NestedStepTraceSchema = StepTraceBaseSchema.extend({
|
|
45556
|
+
...blockTraceShape,
|
|
45557
|
+
steps: array(StepTraceBaseSchema).optional()
|
|
45558
|
+
});
|
|
45172
45559
|
/** A step trace; the `else` steps that ran are one level of the same shape (the nesting is bounded at one). */
|
|
45173
|
-
var AutomationStepTraceSchema = StepTraceBaseSchema.extend({
|
|
45560
|
+
var AutomationStepTraceSchema = StepTraceBaseSchema.extend({
|
|
45561
|
+
else: array(StepTraceBaseSchema).optional(),
|
|
45562
|
+
...blockTraceShape,
|
|
45563
|
+
/** A `choose` step: the steps of the list that ran. A `parallel`: its members. A `repeat`: the LAST iteration. */
|
|
45564
|
+
steps: array(NestedStepTraceSchema).optional()
|
|
45565
|
+
});
|
|
45174
45566
|
var AutomationRunTraceSchema = object({
|
|
45175
45567
|
runId: string().min(1),
|
|
45176
45568
|
startedAt: number$1(),
|
|
@@ -45420,7 +45812,7 @@ object({
|
|
|
45420
45812
|
* is irrelevant; the array is treated as a set by consumers. */
|
|
45421
45813
|
hiddenChildIds: array(number$1()).readonly()
|
|
45422
45814
|
});
|
|
45423
|
-
DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Sensor, DeviceType.
|
|
45815
|
+
DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Sensor, DeviceType.Climate, DeviceType.Cover, DeviceType.Lock, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Generic, method(object({
|
|
45424
45816
|
deviceId: number$1().int().nonnegative(),
|
|
45425
45817
|
childDeviceId: number$1().int().nonnegative(),
|
|
45426
45818
|
hidden: boolean()
|
|
@@ -45577,7 +45969,9 @@ var AddonHttpRouteSchema = object({
|
|
|
45577
45969
|
]).optional(),
|
|
45578
45970
|
description: string().optional(),
|
|
45579
45971
|
/** The hub limits this route per client address before the handler runs. */
|
|
45580
|
-
rateLimit: _enum(["auth"]).optional()
|
|
45972
|
+
rateLimit: _enum(["auth"]).optional(),
|
|
45973
|
+
/** Served once per integration instance; the handler reads `instanceId`. */
|
|
45974
|
+
perInstance: boolean().optional()
|
|
45581
45975
|
});
|
|
45582
45976
|
/**
|
|
45583
45977
|
* Cross-process route invocation envelope. The hub captures the
|
|
@@ -45594,6 +45988,8 @@ var InvokeRequestSchema = object({
|
|
|
45594
45988
|
query: record(string(), string()),
|
|
45595
45989
|
body: unknown(),
|
|
45596
45990
|
headers: record(string(), string()),
|
|
45991
|
+
/** The instance the hub resolved for a `perInstance` route. */
|
|
45992
|
+
instanceId: string().optional(),
|
|
45597
45993
|
user: object({
|
|
45598
45994
|
id: string(),
|
|
45599
45995
|
username: string(),
|
|
@@ -47718,7 +48114,7 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
47718
48114
|
DeviceType.Switch,
|
|
47719
48115
|
DeviceType.PetFeeder,
|
|
47720
48116
|
DeviceType.Container,
|
|
47721
|
-
DeviceType.
|
|
48117
|
+
DeviceType.Climate,
|
|
47722
48118
|
DeviceType.EventEmitter
|
|
47723
48119
|
];
|
|
47724
48120
|
/** Generic Customize target for the percentage reading (D936). */
|
|
@@ -49481,7 +49877,14 @@ var climateControlCapability = {
|
|
|
49481
49877
|
scope: "device",
|
|
49482
49878
|
deviceNative: true,
|
|
49483
49879
|
mode: "singleton",
|
|
49484
|
-
deviceTypes: [DeviceType.
|
|
49880
|
+
deviceTypes: [DeviceType.Climate],
|
|
49881
|
+
power: {
|
|
49882
|
+
kind: "mode",
|
|
49883
|
+
field: "mode",
|
|
49884
|
+
offValue: "off",
|
|
49885
|
+
set: "setMode",
|
|
49886
|
+
lastField: "lastActiveMode"
|
|
49887
|
+
},
|
|
49485
49888
|
methods: {
|
|
49486
49889
|
setMode: method(object({
|
|
49487
49890
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -49866,6 +50269,7 @@ var CompositionProblemSchema = object({
|
|
|
49866
50269
|
"nested-field",
|
|
49867
50270
|
"missing-field",
|
|
49868
50271
|
"kind-mismatch",
|
|
50272
|
+
"source-kind-mismatch",
|
|
49869
50273
|
"may-be-null",
|
|
49870
50274
|
"source-missing",
|
|
49871
50275
|
"source-unreadable",
|
|
@@ -49887,7 +50291,8 @@ var CompositionProblemSchema = object({
|
|
|
49887
50291
|
"command-method-unknown",
|
|
49888
50292
|
"command-source-unknown",
|
|
49889
50293
|
"command-input-unknown",
|
|
49890
|
-
"command-input-missing"
|
|
50294
|
+
"command-input-missing",
|
|
50295
|
+
"power-source-is-actuator"
|
|
49891
50296
|
]),
|
|
49892
50297
|
path: string(),
|
|
49893
50298
|
message: string()
|
|
@@ -50925,6 +51330,13 @@ var mediaPlayerCapability = {
|
|
|
50925
51330
|
deviceNative: true,
|
|
50926
51331
|
mode: "singleton",
|
|
50927
51332
|
deviceTypes: [DeviceType.MediaPlayer],
|
|
51333
|
+
power: {
|
|
51334
|
+
kind: "methods",
|
|
51335
|
+
field: "state",
|
|
51336
|
+
offValues: ["off", "standby"],
|
|
51337
|
+
on: "turnOn",
|
|
51338
|
+
off: "turnOff"
|
|
51339
|
+
},
|
|
50928
51340
|
methods: {
|
|
50929
51341
|
turnOn: optionalMethod(deviceOnly, _void(), {
|
|
50930
51342
|
kind: "mutation",
|
|
@@ -51055,7 +51467,7 @@ var mediaPlayerCapability = {
|
|
|
51055
51467
|
isMuted: { kind: "boolean" }
|
|
51056
51468
|
} }
|
|
51057
51469
|
};
|
|
51058
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.
|
|
51470
|
+
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Climate, DeviceType.EventEmitter, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Fan, DeviceType.Lock, DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Fan, DeviceType.Climate, 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.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.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.Humidifier, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.PetFeeder, DeviceType.Gateway, DeviceType.Camera, DeviceType.Sensor, DeviceType.MediaPlayer, DeviceType.Vacuum, DeviceType.Generic, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.PetFountain, DeviceType.Fan, DeviceType.Humidifier, DeviceType.Climate, DeviceType.WaterHeater, DeviceType.MediaPlayer, DeviceType.Washer, DeviceType.Dryer, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.PetFeeder, DeviceType.MediaPlayer;
|
|
51059
51471
|
var NUMBER = {
|
|
51060
51472
|
kind: "fixed",
|
|
51061
51473
|
result: ["number"]
|
|
@@ -51138,7 +51550,7 @@ function numeric(input) {
|
|
|
51138
51550
|
}, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
|
|
51139
51551
|
};
|
|
51140
51552
|
}
|
|
51141
|
-
var CLIMATE_DEVICES = [DeviceType.
|
|
51553
|
+
var CLIMATE_DEVICES = [DeviceType.Climate];
|
|
51142
51554
|
function binary(input) {
|
|
51143
51555
|
return {
|
|
51144
51556
|
...input,
|
|
@@ -51460,8 +51872,8 @@ var GROUP_TEMPLATES = [
|
|
|
51460
51872
|
actuator({
|
|
51461
51873
|
id: "climate",
|
|
51462
51874
|
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.",
|
|
51463
|
-
deviceType: DeviceType.
|
|
51464
|
-
memberDeviceTypes: [DeviceType.
|
|
51875
|
+
deviceType: DeviceType.Climate,
|
|
51876
|
+
memberDeviceTypes: [DeviceType.Climate],
|
|
51465
51877
|
outputCap: "group-active",
|
|
51466
51878
|
outputField: "active",
|
|
51467
51879
|
member: {
|
|
@@ -51504,6 +51916,118 @@ function actuator(input) {
|
|
|
51504
51916
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
51505
51917
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
51506
51918
|
DeviceType.Camera;
|
|
51919
|
+
var PowerSourceRefusalReasonSchema = _enum([
|
|
51920
|
+
"no-power-source",
|
|
51921
|
+
"switch-missing",
|
|
51922
|
+
"switch-offline",
|
|
51923
|
+
"switch-unknown",
|
|
51924
|
+
"restart-in-progress",
|
|
51925
|
+
"switch-call-failed",
|
|
51926
|
+
"switch-is-actuator"
|
|
51927
|
+
]);
|
|
51928
|
+
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
51929
|
+
accepted: literal(false),
|
|
51930
|
+
reason: PowerSourceRefusalReasonSchema,
|
|
51931
|
+
/** One line for the operator, naming the switch. */
|
|
51932
|
+
message: string()
|
|
51933
|
+
})]);
|
|
51934
|
+
var PowerSourceInfoSchema = object({
|
|
51935
|
+
/** False when the device has no power source linked; the rest is then empty. */
|
|
51936
|
+
linked: boolean(),
|
|
51937
|
+
switchDeviceId: number$1().nullable(),
|
|
51938
|
+
switchName: string().nullable(),
|
|
51939
|
+
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
51940
|
+
switchOn: boolean().nullable(),
|
|
51941
|
+
switchOnline: boolean().nullable(),
|
|
51942
|
+
restartInProgress: boolean(),
|
|
51943
|
+
offSeconds: number$1().int()
|
|
51944
|
+
});
|
|
51945
|
+
/** The composed slice: the switch state the link mirrors, plus the stored off time. */
|
|
51946
|
+
var PowerSourceStateSchema = object({
|
|
51947
|
+
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
51948
|
+
switchOn: boolean().nullable(),
|
|
51949
|
+
offSeconds: number$1().int(),
|
|
51950
|
+
lastFetchedAt: number$1()
|
|
51951
|
+
});
|
|
51952
|
+
var POWER_SOURCE_DEVICE_TYPES = [
|
|
51953
|
+
DeviceType.Gateway,
|
|
51954
|
+
DeviceType.Camera,
|
|
51955
|
+
DeviceType.Sensor,
|
|
51956
|
+
DeviceType.MediaPlayer,
|
|
51957
|
+
DeviceType.Vacuum,
|
|
51958
|
+
DeviceType.Generic,
|
|
51959
|
+
DeviceType.Washer,
|
|
51960
|
+
DeviceType.Dryer,
|
|
51961
|
+
DeviceType.Dishwasher,
|
|
51962
|
+
DeviceType.PetFountain,
|
|
51963
|
+
DeviceType.Fan,
|
|
51964
|
+
DeviceType.Humidifier,
|
|
51965
|
+
DeviceType.Climate,
|
|
51966
|
+
DeviceType.WaterHeater
|
|
51967
|
+
];
|
|
51968
|
+
var powerSourceCustomize = {
|
|
51969
|
+
version: 1,
|
|
51970
|
+
targets: [{
|
|
51971
|
+
id: "switch",
|
|
51972
|
+
cardinality: "scalar",
|
|
51973
|
+
vocabulary: {
|
|
51974
|
+
cap: "power-source",
|
|
51975
|
+
stateField: "switchOn"
|
|
51976
|
+
},
|
|
51977
|
+
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
51978
|
+
apply: "add",
|
|
51979
|
+
path: "switchOn",
|
|
51980
|
+
sources: [{
|
|
51981
|
+
kind: "switch",
|
|
51982
|
+
transforms: [{ kind: "identity" }]
|
|
51983
|
+
}],
|
|
51984
|
+
companions: [{
|
|
51985
|
+
kind: "constant",
|
|
51986
|
+
path: "offSeconds",
|
|
51987
|
+
value: 10,
|
|
51988
|
+
when: "add",
|
|
51989
|
+
formerValues: [0]
|
|
51990
|
+
}, {
|
|
51991
|
+
kind: "clock",
|
|
51992
|
+
path: "lastFetchedAt",
|
|
51993
|
+
when: "always"
|
|
51994
|
+
}],
|
|
51995
|
+
sourceReport: { status: "assignment-status" }
|
|
51996
|
+
}]
|
|
51997
|
+
};
|
|
51998
|
+
var deviceInput$2 = object({ deviceId: number$1().int().nonnegative() });
|
|
51999
|
+
var powerSourceCapability = {
|
|
52000
|
+
name: "power-source",
|
|
52001
|
+
scope: "device",
|
|
52002
|
+
mode: "singleton",
|
|
52003
|
+
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
52004
|
+
methods: {
|
|
52005
|
+
getStatus: systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "protected" }),
|
|
52006
|
+
powerOn: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
52007
|
+
kind: "mutation",
|
|
52008
|
+
auth: "protected"
|
|
52009
|
+
}),
|
|
52010
|
+
powerOff: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
52011
|
+
kind: "mutation",
|
|
52012
|
+
auth: "protected"
|
|
52013
|
+
}),
|
|
52014
|
+
forceRestart: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
52015
|
+
kind: "mutation",
|
|
52016
|
+
auth: "protected"
|
|
52017
|
+
})
|
|
52018
|
+
},
|
|
52019
|
+
runtimeState: PowerSourceStateSchema,
|
|
52020
|
+
customize: powerSourceCustomize,
|
|
52021
|
+
/**
|
|
52022
|
+
* Runtime-state durability: **session** — mirrored from the linked switch on
|
|
52023
|
+
* its next report; a restored value would outlive the reading it names.
|
|
52024
|
+
*
|
|
52025
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
52026
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
52027
|
+
*/
|
|
52028
|
+
durability: "session",
|
|
52029
|
+
history: "none"
|
|
52030
|
+
};
|
|
51507
52031
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
51508
52032
|
/**
|
|
51509
52033
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -54038,8 +54562,14 @@ method(object({
|
|
|
54038
54562
|
"string",
|
|
54039
54563
|
"number",
|
|
54040
54564
|
"boolean",
|
|
54041
|
-
"enum"
|
|
54565
|
+
"enum",
|
|
54566
|
+
"union"
|
|
54042
54567
|
]),
|
|
54568
|
+
unionMembers: array(_enum([
|
|
54569
|
+
"string",
|
|
54570
|
+
"number",
|
|
54571
|
+
"boolean"
|
|
54572
|
+
])).optional(),
|
|
54043
54573
|
enumValues: array(string()).optional(),
|
|
54044
54574
|
item: boolean().optional(),
|
|
54045
54575
|
quantity: ReadingQuantitySchema.optional()
|
|
@@ -56131,25 +56661,81 @@ var enumSensorCapability = {
|
|
|
56131
56661
|
* whether persisting is worth a SQLite commit. */
|
|
56132
56662
|
volatileStateFields: ["lastFetchedAt", "timestamp"]
|
|
56133
56663
|
};
|
|
56664
|
+
var EventVocabularyKindSchema = _enum([
|
|
56665
|
+
"press",
|
|
56666
|
+
"long-press",
|
|
56667
|
+
"long-press-start",
|
|
56668
|
+
"release",
|
|
56669
|
+
"repeat",
|
|
56670
|
+
"double-press",
|
|
56671
|
+
"other"
|
|
56672
|
+
]);
|
|
56673
|
+
/** The part of the device an event comes from: a button, a channel. */
|
|
56674
|
+
var EventVocabularySourceSchema = object({
|
|
56675
|
+
id: string().min(1),
|
|
56676
|
+
label: string().min(1)
|
|
56677
|
+
});
|
|
56678
|
+
/** One scalar field of an event's `data`, by dotted path. */
|
|
56679
|
+
var EventVocabularyFieldSchema = object({
|
|
56680
|
+
path: string().min(1),
|
|
56681
|
+
kind: _enum([
|
|
56682
|
+
"string",
|
|
56683
|
+
"number",
|
|
56684
|
+
"boolean",
|
|
56685
|
+
"enum"
|
|
56686
|
+
]),
|
|
56687
|
+
enumValues: array(string()).optional(),
|
|
56688
|
+
label: string().min(1).optional()
|
|
56689
|
+
});
|
|
56690
|
+
var EventVocabularyEntrySchema = object({
|
|
56691
|
+
/** The native token, verbatim. */
|
|
56692
|
+
id: string().min(1),
|
|
56693
|
+
source: EventVocabularySourceSchema.optional(),
|
|
56694
|
+
label: string().min(1),
|
|
56695
|
+
description: string().optional(),
|
|
56696
|
+
kind: EventVocabularyKindSchema.optional(),
|
|
56697
|
+
payload: array(EventVocabularyFieldSchema).optional()
|
|
56698
|
+
});
|
|
56699
|
+
/**
|
|
56700
|
+
* The vocabulary of a source that declares tokens and nothing else: one
|
|
56701
|
+
* entry per token, labelled with the token itself.
|
|
56702
|
+
*/
|
|
56703
|
+
function plainEventVocabulary(eventTypes) {
|
|
56704
|
+
return [...new Set(eventTypes)].filter((eventType) => eventType !== "").map((eventType) => ({
|
|
56705
|
+
id: eventType,
|
|
56706
|
+
label: eventType
|
|
56707
|
+
}));
|
|
56708
|
+
}
|
|
56134
56709
|
/**
|
|
56135
56710
|
* Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
|
|
56136
56711
|
* device. Carries the device's EXACT declared event vocabulary verbatim
|
|
56137
56712
|
* (NO normalization). Two shapes:
|
|
56138
|
-
* - structured source (HA `event.*` entity) → `
|
|
56139
|
-
* declared
|
|
56140
|
-
* - generic/legacy source (HA bus events, e.g. zha_event) → `
|
|
56713
|
+
* - structured source (HA `event.*` entity) → `vocabulary` is the fixed
|
|
56714
|
+
* declared set (e.g. press_short, press_long, …).
|
|
56715
|
+
* - generic/legacy source (HA bus events, e.g. zha_event) → `vocabulary`
|
|
56141
56716
|
* is [] (open) and `lastEvent.data` carries the complex payload.
|
|
56717
|
+
* `vocabulary` (D1044) names each event for a person and types it for a
|
|
56718
|
+
* rule; `eventTypes` is its list of native tokens, kept for older readers.
|
|
56719
|
+
* A fire names the part of the device it came from in `sourceId` when the
|
|
56720
|
+
* vocabulary declares sources (a remote's buttons).
|
|
56142
56721
|
* `seq` is monotonic so two identical events still produce distinct slice writes.
|
|
56143
56722
|
*/
|
|
56144
56723
|
var EventFireSchema = object({
|
|
56145
56724
|
deviceId: number$1(),
|
|
56146
56725
|
eventType: string(),
|
|
56726
|
+
sourceId: string().optional(),
|
|
56147
56727
|
data: record(string(), unknown()).nullable(),
|
|
56148
56728
|
timestamp: number$1().meta(readingMeta("none")),
|
|
56149
56729
|
seq: number$1().meta(readingMeta("count"))
|
|
56150
56730
|
});
|
|
56151
56731
|
var EventEmitterStatusSchema = object({
|
|
56152
56732
|
eventTypes: array(string()),
|
|
56733
|
+
/**
|
|
56734
|
+
* Required of a provider by the type. A status written before the field
|
|
56735
|
+
* existed still parses, as an empty vocabulary: read it through
|
|
56736
|
+
* `readEventVocabulary`, which then answers with the plain tokens.
|
|
56737
|
+
*/
|
|
56738
|
+
vocabulary: array(EventVocabularyEntrySchema).default([]),
|
|
56153
56739
|
lastEvent: EventFireSchema.nullable(),
|
|
56154
56740
|
eventCountSinceStart: number$1().meta(readingMeta("count"))
|
|
56155
56741
|
});
|
|
@@ -56667,8 +57253,85 @@ method(_void(), array(FailureContributionSchema).readonly());
|
|
|
56667
57253
|
* - `FanPreset` — preset selector
|
|
56668
57254
|
* - `FanDirection` — direction toggle (ceiling fans)
|
|
56669
57255
|
* - `FanOscillating` — oscillation toggle
|
|
57256
|
+
*
|
|
57257
|
+
* Per-axis oscillation (`oscillationAxes`, `setOscillationAxis`, D1034): an air
|
|
57258
|
+
* circulator sweeps horizontally and vertically, each axis with its own
|
|
57259
|
+
* switch, fixed angle, sweep bounds and limits. A provider may report them
|
|
57260
|
+
* natively; otherwise the OPERATOR links sibling devices to each field in
|
|
57261
|
+
* Customize (a `replace` over the fields listed in `overridable` — nothing is
|
|
57262
|
+
* linked or inferred by itself), and a write to a linked field is forwarded to
|
|
57263
|
+
* its source. `oscillating` stays the single summary flag: when axes exist it
|
|
57264
|
+
* means "any axis oscillates" (`anyAxisOscillating`).
|
|
56670
57265
|
*/
|
|
56671
57266
|
var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
57267
|
+
var FAN_OSCILLATION_AXES = ["horizontal", "vertical"];
|
|
57268
|
+
var FanOscillationAxisNameSchema = _enum(FAN_OSCILLATION_AXES);
|
|
57269
|
+
var FanOscillationAxisFieldSchema = _enum([
|
|
57270
|
+
"oscillating",
|
|
57271
|
+
"angle",
|
|
57272
|
+
"sweepFrom",
|
|
57273
|
+
"sweepTo"
|
|
57274
|
+
]);
|
|
57275
|
+
/** The limits of one axis, read from the source that serves its angle. */
|
|
57276
|
+
var FAN_OSCILLATION_AXIS_LIMITS = [
|
|
57277
|
+
"min",
|
|
57278
|
+
"max",
|
|
57279
|
+
"step"
|
|
57280
|
+
];
|
|
57281
|
+
/**
|
|
57282
|
+
* One oscillation axis. Every reading the device has not reported is `null`,
|
|
57283
|
+
* never `0` (D393): `0°` is a real position. Each leaf defaults to `null`, so
|
|
57284
|
+
* a slice that names only the leaves it knows (a Customize link of one field)
|
|
57285
|
+
* is still a whole axis.
|
|
57286
|
+
*/
|
|
57287
|
+
var FanOscillationAxisSchema = object({
|
|
57288
|
+
/** Whether the axis sweeps. */
|
|
57289
|
+
oscillating: boolean().nullable().default(null),
|
|
57290
|
+
/** The fixed position (degrees) the axis holds when it does not sweep. */
|
|
57291
|
+
angle: number$1().nullable().default(null),
|
|
57292
|
+
/** The sweep bounds (degrees): left → right, bottom → top. */
|
|
57293
|
+
sweepFrom: number$1().nullable().default(null),
|
|
57294
|
+
sweepTo: number$1().nullable().default(null),
|
|
57295
|
+
/** The angles the axis accepts. Null when the device states none. */
|
|
57296
|
+
min: number$1().nullable().default(null),
|
|
57297
|
+
max: number$1().nullable().default(null),
|
|
57298
|
+
step: number$1().nullable().default(null)
|
|
57299
|
+
});
|
|
57300
|
+
/** Both axes; an axis the device does not have is `null`. */
|
|
57301
|
+
var FanOscillationAxesSchema = object({
|
|
57302
|
+
horizontal: FanOscillationAxisSchema.nullable().default(null),
|
|
57303
|
+
vertical: FanOscillationAxisSchema.nullable().default(null)
|
|
57304
|
+
});
|
|
57305
|
+
var FanOscillationAxisRefusalSchema = _enum([
|
|
57306
|
+
"nothing-to-set",
|
|
57307
|
+
"axis-not-linked",
|
|
57308
|
+
"out-of-range",
|
|
57309
|
+
"source-gone",
|
|
57310
|
+
"source-unknown",
|
|
57311
|
+
"source-call-failed"
|
|
57312
|
+
]);
|
|
57313
|
+
var FanOscillationAxisResultSchema = discriminatedUnion("status", [object({ status: literal("applied") }), object({
|
|
57314
|
+
status: literal("refused"),
|
|
57315
|
+
reason: FanOscillationAxisRefusalSchema,
|
|
57316
|
+
/** The field at fault, when one is. */
|
|
57317
|
+
field: FanOscillationAxisFieldSchema.optional(),
|
|
57318
|
+
/** One line for the operator, naming the axis, the field and the source. */
|
|
57319
|
+
detail: string(),
|
|
57320
|
+
/** Fields that DID land before the refusal (writes run in order, stop at the first failure). */
|
|
57321
|
+
applied: array(FanOscillationAxisFieldSchema)
|
|
57322
|
+
})]);
|
|
57323
|
+
var SetOscillationAxisInputSchema = object({
|
|
57324
|
+
deviceId: number$1().int().nonnegative(),
|
|
57325
|
+
axis: FanOscillationAxisNameSchema,
|
|
57326
|
+
oscillating: boolean().optional(),
|
|
57327
|
+
angle: number$1().optional(),
|
|
57328
|
+
sweepFrom: number$1().optional(),
|
|
57329
|
+
sweepTo: number$1().optional()
|
|
57330
|
+
});
|
|
57331
|
+
/** The state path of one axis field: `oscillationAxes.<axis>.<field>`. */
|
|
57332
|
+
function fanAxisPath(axis, field) {
|
|
57333
|
+
return `oscillationAxes.${axis}.${field}`;
|
|
57334
|
+
}
|
|
56672
57335
|
var FanControlStatusSchema = object({
|
|
56673
57336
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
56674
57337
|
* speed surface (single-speed fan). */
|
|
@@ -56687,9 +57350,102 @@ var FanControlStatusSchema = object({
|
|
|
56687
57350
|
/** Oscillation toggle. Null when the device has no oscillation
|
|
56688
57351
|
* surface. */
|
|
56689
57352
|
oscillating: boolean().nullable(),
|
|
57353
|
+
/**
|
|
57354
|
+
* Per-axis oscillation (air circulators). Absent when the device has no
|
|
57355
|
+
* per-axis surface and nothing is linked in Customize.
|
|
57356
|
+
*/
|
|
57357
|
+
oscillationAxes: FanOscillationAxesSchema.optional(),
|
|
56690
57358
|
/** Ms epoch when the slice was last updated. */
|
|
56691
57359
|
lastChangedAt: number$1()
|
|
56692
57360
|
});
|
|
57361
|
+
var FAN_CUSTOMIZE_DEVICE_TYPES = [DeviceType.Fan];
|
|
57362
|
+
/** The Customize section the axis targets are listed under. */
|
|
57363
|
+
var AXES_SECTION = "oscillation-axes";
|
|
57364
|
+
function axisOscillatingTarget(axis) {
|
|
57365
|
+
return {
|
|
57366
|
+
id: `${axis}-oscillating`,
|
|
57367
|
+
cardinality: "scalar",
|
|
57368
|
+
vocabulary: {
|
|
57369
|
+
cap: "fan-control",
|
|
57370
|
+
stateField: "oscillating"
|
|
57371
|
+
},
|
|
57372
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57373
|
+
apply: "replace",
|
|
57374
|
+
section: AXES_SECTION,
|
|
57375
|
+
path: fanAxisPath(axis, "oscillating"),
|
|
57376
|
+
sources: [{
|
|
57377
|
+
kind: "switch",
|
|
57378
|
+
transforms: [{ kind: "identity" }]
|
|
57379
|
+
}],
|
|
57380
|
+
sourceReport: { status: "assignment-status" }
|
|
57381
|
+
};
|
|
57382
|
+
}
|
|
57383
|
+
/**
|
|
57384
|
+
* A numeric axis field. The `angle` link also carries the axis limits, read
|
|
57385
|
+
* from the SAME linked control's own range (`min`/`max`/`step`), so the limits
|
|
57386
|
+
* are the source's, never guessed.
|
|
57387
|
+
*/
|
|
57388
|
+
function axisNumericTarget(axis, field) {
|
|
57389
|
+
const base = {
|
|
57390
|
+
id: `${axis}-${field}`,
|
|
57391
|
+
cardinality: "scalar",
|
|
57392
|
+
vocabulary: { cap: "fan-control" },
|
|
57393
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57394
|
+
apply: "replace",
|
|
57395
|
+
section: AXES_SECTION,
|
|
57396
|
+
path: fanAxisPath(axis, field),
|
|
57397
|
+
sources: [{
|
|
57398
|
+
kind: "numeric",
|
|
57399
|
+
transforms: [{ kind: "identity" }]
|
|
57400
|
+
}],
|
|
57401
|
+
sourceReport: { status: "assignment-status" }
|
|
57402
|
+
};
|
|
57403
|
+
if (field !== "angle") return base;
|
|
57404
|
+
return {
|
|
57405
|
+
...base,
|
|
57406
|
+
companions: FAN_OSCILLATION_AXIS_LIMITS.map((limit) => ({
|
|
57407
|
+
kind: "source-field",
|
|
57408
|
+
path: fanAxisPath(axis, limit),
|
|
57409
|
+
field: limit
|
|
57410
|
+
}))
|
|
57411
|
+
};
|
|
57412
|
+
}
|
|
57413
|
+
var AXIS_TARGETS = FAN_OSCILLATION_AXES.flatMap((axis) => [
|
|
57414
|
+
axisOscillatingTarget(axis),
|
|
57415
|
+
axisNumericTarget(axis, "angle"),
|
|
57416
|
+
axisNumericTarget(axis, "sweepFrom"),
|
|
57417
|
+
axisNumericTarget(axis, "sweepTo")
|
|
57418
|
+
]);
|
|
57419
|
+
/** A select (an enum control) the operator links to the fan's `preset`. */
|
|
57420
|
+
var PRESET_TARGET = {
|
|
57421
|
+
id: "preset",
|
|
57422
|
+
cardinality: "scalar",
|
|
57423
|
+
vocabulary: { cap: "fan-control" },
|
|
57424
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57425
|
+
apply: "replace",
|
|
57426
|
+
path: "preset",
|
|
57427
|
+
sources: [{
|
|
57428
|
+
kind: "field",
|
|
57429
|
+
valueKinds: ["string"],
|
|
57430
|
+
transforms: [{ kind: "identity" }]
|
|
57431
|
+
}],
|
|
57432
|
+
sourceReport: { status: "assignment-status" }
|
|
57433
|
+
};
|
|
57434
|
+
var FAN_CONTROL_TARGETS = [...AXIS_TARGETS, PRESET_TARGET];
|
|
57435
|
+
/**
|
|
57436
|
+
* Customize (D1034): the fan already serves `fan-control` natively, so every
|
|
57437
|
+
* target is a `replace` over a field listed in `overridable` — the axis leaves
|
|
57438
|
+
* and `preset`, nothing else. Nothing is pre-linked: the operator picks each
|
|
57439
|
+
* source (D356).
|
|
57440
|
+
*/
|
|
57441
|
+
var fanControlCustomize = {
|
|
57442
|
+
version: 1,
|
|
57443
|
+
overridable: FAN_CONTROL_TARGETS.map((target) => ({
|
|
57444
|
+
path: target.path,
|
|
57445
|
+
by: target.id
|
|
57446
|
+
})),
|
|
57447
|
+
targets: FAN_CONTROL_TARGETS
|
|
57448
|
+
};
|
|
56693
57449
|
var fanControlCapability = {
|
|
56694
57450
|
name: "fan-control",
|
|
56695
57451
|
scope: "device",
|
|
@@ -56724,6 +57480,18 @@ var fanControlCapability = {
|
|
|
56724
57480
|
}), _void(), {
|
|
56725
57481
|
kind: "mutation",
|
|
56726
57482
|
auth: "protected"
|
|
57483
|
+
}),
|
|
57484
|
+
/**
|
|
57485
|
+
* Sets one oscillation axis: its switch, fixed angle and/or sweep bounds.
|
|
57486
|
+
* Fields left out are untouched. A field linked in Customize is forwarded
|
|
57487
|
+
* to its linked source (D1034); otherwise the native provider serves it;
|
|
57488
|
+
* neither → `axis-not-linked`. Ranges are checked against the axis limits
|
|
57489
|
+
* before anything is written; writes run in order and stop at the first
|
|
57490
|
+
* failure, which the result names.
|
|
57491
|
+
*/
|
|
57492
|
+
setOscillationAxis: optionalMethod(SetOscillationAxisInputSchema, FanOscillationAxisResultSchema, {
|
|
57493
|
+
kind: "mutation",
|
|
57494
|
+
auth: "protected"
|
|
56727
57495
|
})
|
|
56728
57496
|
},
|
|
56729
57497
|
status: {
|
|
@@ -56735,6 +57503,7 @@ var fanControlCapability = {
|
|
|
56735
57503
|
* sliders read `speed` for live updates.
|
|
56736
57504
|
*/
|
|
56737
57505
|
runtimeState: FanControlStatusSchema,
|
|
57506
|
+
customize: fanControlCustomize,
|
|
56738
57507
|
/**
|
|
56739
57508
|
* Runtime-state durability: **session** — as `brightness`.
|
|
56740
57509
|
*
|
|
@@ -56754,16 +57523,33 @@ var fanControlCapability = {
|
|
|
56754
57523
|
oscillating: { kind: "boolean" }
|
|
56755
57524
|
} }
|
|
56756
57525
|
};
|
|
57526
|
+
/** One axis as a preset wants it; an absent field is left as it is. */
|
|
57527
|
+
var FanPresetAxisSchema = object({
|
|
57528
|
+
oscillating: boolean().optional(),
|
|
57529
|
+
angle: number$1().optional(),
|
|
57530
|
+
sweepFrom: number$1().optional(),
|
|
57531
|
+
sweepTo: number$1().optional()
|
|
57532
|
+
});
|
|
57533
|
+
/** The fan-control state a preset sets; an absent field is left as it is. */
|
|
57534
|
+
var FanPresetStateSchema = object({
|
|
57535
|
+
speed: number$1().min(0).max(100).optional(),
|
|
57536
|
+
/** The fan's own preset mode (or the linked select's option). */
|
|
57537
|
+
preset: string().min(1).optional(),
|
|
57538
|
+
direction: FanDirectionSchema.optional(),
|
|
57539
|
+
oscillationAxes: object({
|
|
57540
|
+
horizontal: FanPresetAxisSchema.optional(),
|
|
57541
|
+
vertical: FanPresetAxisSchema.optional()
|
|
57542
|
+
}).optional()
|
|
57543
|
+
});
|
|
56757
57544
|
var FanPresetSchema = object({
|
|
56758
57545
|
/** Stable key; never reused after a delete within one device's list. */
|
|
56759
57546
|
id: string().min(1).max(64),
|
|
56760
57547
|
/** What the operator and a voice assistant call it. */
|
|
56761
57548
|
name: string().trim().min(1).max(40),
|
|
56762
|
-
|
|
57549
|
+
state: FanPresetStateSchema
|
|
56763
57550
|
});
|
|
56764
57551
|
var FanPresetsRefusalSchema = _enum([
|
|
56765
|
-
"
|
|
56766
|
-
"bad-target",
|
|
57552
|
+
"empty-state",
|
|
56767
57553
|
"duplicate-name",
|
|
56768
57554
|
"duplicate-id",
|
|
56769
57555
|
"too-many",
|
|
@@ -56775,72 +57561,55 @@ var FanPresetsSetResultSchema = discriminatedUnion("status", [object({
|
|
|
56775
57561
|
}), object({
|
|
56776
57562
|
status: literal("refused"),
|
|
56777
57563
|
reason: FanPresetsRefusalSchema,
|
|
56778
|
-
/** Names the preset
|
|
57564
|
+
/** Names the preset at fault. */
|
|
56779
57565
|
detail: string()
|
|
56780
57566
|
})]);
|
|
56781
|
-
var
|
|
56782
|
-
|
|
57567
|
+
var FanPresetFieldResultSchema = object({
|
|
57568
|
+
/** The state path: `speed`, `preset`, `direction`, `oscillationAxes.horizontal.angle`. */
|
|
57569
|
+
field: string(),
|
|
56783
57570
|
outcome: _enum([
|
|
56784
57571
|
"applied",
|
|
56785
57572
|
"failed",
|
|
56786
57573
|
"not-run",
|
|
56787
57574
|
"refused"
|
|
56788
57575
|
]),
|
|
56789
|
-
deviceId: number$1().int().nonnegative().optional(),
|
|
56790
|
-
cap: string().optional(),
|
|
56791
|
-
method: string().optional(),
|
|
56792
57576
|
/** The cause, for every outcome but `applied`. */
|
|
56793
57577
|
message: string().optional()
|
|
56794
57578
|
});
|
|
56795
57579
|
var FanPresetApplyResultSchema = object({
|
|
56796
|
-
/** `applied`: every
|
|
57580
|
+
/** `applied`: every field landed. `partial`: some did, one did not. `refused`: nothing ran. */
|
|
56797
57581
|
status: _enum([
|
|
56798
57582
|
"applied",
|
|
56799
57583
|
"partial",
|
|
56800
57584
|
"refused"
|
|
56801
57585
|
]),
|
|
56802
|
-
|
|
57586
|
+
fields: array(FanPresetFieldResultSchema)
|
|
56803
57587
|
});
|
|
56804
57588
|
var FanCaptureFieldSchema = _enum([
|
|
56805
57589
|
"speed",
|
|
56806
|
-
"oscillating",
|
|
56807
57590
|
"direction",
|
|
56808
|
-
"preset"
|
|
57591
|
+
"preset",
|
|
57592
|
+
"oscillationAxes"
|
|
56809
57593
|
]);
|
|
56810
|
-
var FanCaptureOmissionSchema = object({
|
|
56811
|
-
/** A fan field (`speed`) or `step <n>` for a sibling step. */
|
|
56812
|
-
what: string(),
|
|
56813
|
-
/** Why no value was taken: unknown reading (never stored as 0, D393), or a step that cannot be read. */
|
|
56814
|
-
reason: string()
|
|
56815
|
-
});
|
|
56816
|
-
var FanCaptureItemSchema = object({
|
|
56817
|
-
group: _enum([
|
|
56818
|
-
"fan",
|
|
56819
|
-
"oscillation",
|
|
56820
|
-
"angles"
|
|
56821
|
-
]),
|
|
56822
|
-
/** What the step sets, in the operator's words (a device name, a fan field). */
|
|
56823
|
-
label: string()
|
|
56824
|
-
});
|
|
56825
57594
|
var FanPresetCaptureResultSchema = object({
|
|
56826
|
-
/** The
|
|
56827
|
-
|
|
56828
|
-
|
|
56829
|
-
|
|
56830
|
-
|
|
57595
|
+
/** The fan's CURRENT fan-control state, as a preset; NOT saved. */
|
|
57596
|
+
state: FanPresetStateSchema,
|
|
57597
|
+
omitted: array(object({
|
|
57598
|
+
/** A state path (`speed`, `oscillationAxes.vertical.angle`). */
|
|
57599
|
+
what: string(),
|
|
57600
|
+
/** Why no value was taken: an unknown reading is never stored as 0 (D393). */
|
|
57601
|
+
reason: string()
|
|
57602
|
+
}))
|
|
56831
57603
|
});
|
|
56832
|
-
var deviceInput$
|
|
57604
|
+
var deviceInput$1 = object({ deviceId: number$1().int().nonnegative() });
|
|
56833
57605
|
DeviceType.Fan;
|
|
56834
|
-
method(deviceInput$
|
|
57606
|
+
method(deviceInput$1, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$1.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
|
|
56835
57607
|
kind: "mutation",
|
|
56836
57608
|
auth: "admin"
|
|
56837
|
-
}), method(deviceInput$
|
|
57609
|
+
}), method(deviceInput$1.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
|
|
56838
57610
|
kind: "mutation",
|
|
56839
57611
|
auth: "protected"
|
|
56840
|
-
}), method(deviceInput$
|
|
56841
|
-
fields: array(FanCaptureFieldSchema).optional(),
|
|
56842
|
-
steps: array(ActionStepSchema).max(20).default([])
|
|
56843
|
-
}), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
57612
|
+
}), method(deviceInput$1.extend({ fields: array(FanCaptureFieldSchema).optional() }), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
56844
57613
|
/**
|
|
56845
57614
|
* Per-device feature/identity probe slice. Holds the runtime-resolved
|
|
56846
57615
|
* truth about what a device CAN do — which the kernel uses to:
|
|
@@ -57052,8 +57821,7 @@ var groupActiveCapability = {
|
|
|
57052
57821
|
DeviceType.Valve,
|
|
57053
57822
|
DeviceType.Lock,
|
|
57054
57823
|
DeviceType.MediaPlayer,
|
|
57055
|
-
DeviceType.Climate
|
|
57056
|
-
DeviceType.Thermostat
|
|
57824
|
+
DeviceType.Climate
|
|
57057
57825
|
],
|
|
57058
57826
|
methods: {},
|
|
57059
57827
|
status: {
|
|
@@ -57095,7 +57863,6 @@ var groupCommandsCapability = {
|
|
|
57095
57863
|
DeviceType.Valve,
|
|
57096
57864
|
DeviceType.Lock,
|
|
57097
57865
|
DeviceType.MediaPlayer,
|
|
57098
|
-
DeviceType.Thermostat,
|
|
57099
57866
|
DeviceType.Climate
|
|
57100
57867
|
],
|
|
57101
57868
|
methods: {},
|
|
@@ -57151,7 +57918,6 @@ var groupStatusCapability = {
|
|
|
57151
57918
|
DeviceType.Valve,
|
|
57152
57919
|
DeviceType.Lock,
|
|
57153
57920
|
DeviceType.MediaPlayer,
|
|
57154
|
-
DeviceType.Thermostat,
|
|
57155
57921
|
DeviceType.Climate
|
|
57156
57922
|
],
|
|
57157
57923
|
methods: {},
|
|
@@ -57507,7 +58273,7 @@ var heatingValveCapability = {
|
|
|
57507
58273
|
scope: "device",
|
|
57508
58274
|
deviceNative: true,
|
|
57509
58275
|
mode: "singleton",
|
|
57510
|
-
deviceTypes: [DeviceType.
|
|
58276
|
+
deviceTypes: [DeviceType.Climate],
|
|
57511
58277
|
methods: {},
|
|
57512
58278
|
status: {
|
|
57513
58279
|
schema: HeatingValveStatusSchema,
|
|
@@ -57537,13 +58303,18 @@ var heatingValveCapability = {
|
|
|
57537
58303
|
* retried in a loop - D49); a future one is re-armed.
|
|
57538
58304
|
*
|
|
57539
58305
|
* What a device may be run for, and for how long at most, is per device type
|
|
57540
|
-
* ({@link
|
|
58306
|
+
* and role ({@link timedRunLimitOf}): the maximum is a safety bound, not a preference,
|
|
57541
58307
|
* so a request above it is REFUSED with a typed reason, never clamped.
|
|
57542
58308
|
*/
|
|
57543
58309
|
var TimedRunUnitSchema = _enum(["s", "min"]);
|
|
57544
58310
|
_enum(["switch", "pet-fountain"]);
|
|
57545
|
-
|
|
57546
|
-
|
|
58311
|
+
/**
|
|
58312
|
+
* The device types `timed-run` may be offered on. A `switch` is narrowed by
|
|
58313
|
+
* role ({@link TIMED_RUN_DEVICE_ROLES}): only a plug-in heater runs on a timer.
|
|
58314
|
+
*/
|
|
58315
|
+
var TIMED_RUN_DEVICE_TYPES = [DeviceType.Switch, DeviceType.PetFountain];
|
|
58316
|
+
/** The roles that make a device of these types timed-run-able (D1033). */
|
|
58317
|
+
var TIMED_RUN_DEVICE_ROLES = { [DeviceType.Switch]: [DeviceRole.SwitchHeater] };
|
|
57547
58318
|
/** One duration, as the one-tap run asks for it. */
|
|
57548
58319
|
var TimedRunQuickRunSchema = object({
|
|
57549
58320
|
duration: number$1().positive().finite(),
|
|
@@ -57593,6 +58364,7 @@ var timedRunCapability = {
|
|
|
57593
58364
|
kind: "wrapper",
|
|
57594
58365
|
defaultActive: true,
|
|
57595
58366
|
deviceTypes: TIMED_RUN_DEVICE_TYPES,
|
|
58367
|
+
deviceRoles: TIMED_RUN_DEVICE_ROLES,
|
|
57596
58368
|
deviceConfig: { ui: {
|
|
57597
58369
|
kind: "derived-form",
|
|
57598
58370
|
builderId: "timed-run",
|
|
@@ -57753,8 +58525,8 @@ var HeroSceneSchema = object({
|
|
|
57753
58525
|
/** 0..1: volume, cover position, cycle progress. */
|
|
57754
58526
|
level: number$1().min(0).max(1).nullable()
|
|
57755
58527
|
});
|
|
57756
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.
|
|
57757
|
-
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
58528
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
58529
|
+
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
57758
58530
|
/** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
|
|
57759
58531
|
var HeroTextSchema = object({
|
|
57760
58532
|
en: string(),
|
|
@@ -58139,8 +58911,8 @@ var HeroRowModelSchema = object({
|
|
|
58139
58911
|
extras: array(HeroControlSchema).max(2).optional(),
|
|
58140
58912
|
trailing: HeroFactSchema.nullable()
|
|
58141
58913
|
});
|
|
58142
|
-
DeviceType.Climate
|
|
58143
|
-
DeviceType.Climate, DeviceType.
|
|
58914
|
+
DeviceType.Climate;
|
|
58915
|
+
DeviceType.Climate, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer;
|
|
58144
58916
|
/**
|
|
58145
58917
|
* hero-layout — what a device's hero says (D1023). Read-only: the FIXED
|
|
58146
58918
|
* layouts live in `@camstack/types` and nothing here stores or edits one.
|
|
@@ -58172,7 +58944,10 @@ method(heroInput, HeroModelSchema.nullable(), {
|
|
|
58172
58944
|
}), method(rowModelsInput, array(HeroRowModelSchema.nullable()), {
|
|
58173
58945
|
auth: "protected",
|
|
58174
58946
|
role: "viewer"
|
|
58175
|
-
}), method(object({
|
|
58947
|
+
}), method(object({
|
|
58948
|
+
deviceType: string(),
|
|
58949
|
+
role: string().nullable().optional()
|
|
58950
|
+
}), HeroLayoutRowsSchema, {
|
|
58176
58951
|
auth: "protected",
|
|
58177
58952
|
role: "viewer"
|
|
58178
58953
|
});
|
|
@@ -58219,6 +58994,11 @@ var humidifierCapability = {
|
|
|
58219
58994
|
deviceNative: true,
|
|
58220
58995
|
mode: "singleton",
|
|
58221
58996
|
deviceTypes: [DeviceType.Humidifier],
|
|
58997
|
+
power: {
|
|
58998
|
+
kind: "field",
|
|
58999
|
+
field: "on",
|
|
59000
|
+
set: "setOn"
|
|
59001
|
+
},
|
|
58222
59002
|
methods: {
|
|
58223
59003
|
setOn: method(object({
|
|
58224
59004
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -58292,7 +59072,7 @@ var humiditySensorCapability = {
|
|
|
58292
59072
|
scope: "device",
|
|
58293
59073
|
deviceNative: true,
|
|
58294
59074
|
mode: "singleton",
|
|
58295
|
-
deviceTypes: [DeviceType.Sensor, DeviceType.
|
|
59075
|
+
deviceTypes: [DeviceType.Sensor, DeviceType.Climate],
|
|
58296
59076
|
methods: {},
|
|
58297
59077
|
status: {
|
|
58298
59078
|
schema: HumiditySensorStatusSchema,
|
|
@@ -71474,7 +72254,18 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
71474
72254
|
* The value is baked into the authorization code at `/authorize` and travels
|
|
71475
72255
|
* on the tokens, so editing this field changes FUTURE links only.
|
|
71476
72256
|
*/
|
|
71477
|
-
refreshTokenTtlSec: union([number$1().int().positive(), literal("never")]).optional()
|
|
72257
|
+
refreshTokenTtlSec: union([number$1().int().positive(), literal("never")]).optional(),
|
|
72258
|
+
/**
|
|
72259
|
+
* The integration instances an account can be linked to (planning#415),
|
|
72260
|
+
* beside the default one. `/api/oauth2/authorize?instance=<id>` is accepted
|
|
72261
|
+
* only for an id listed here; with no `instance=` the link is made to the
|
|
72262
|
+
* default instance, which is never listed and always accepted — it is the
|
|
72263
|
+
* address third parties already hold. Omit for a single-instance integration.
|
|
72264
|
+
*/
|
|
72265
|
+
instances: array(object({
|
|
72266
|
+
id: string(),
|
|
72267
|
+
displayName: string()
|
|
72268
|
+
})).optional()
|
|
71478
72269
|
});
|
|
71479
72270
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
71480
72271
|
/**
|
|
@@ -71964,6 +72755,11 @@ var petFountainCapability = {
|
|
|
71964
72755
|
deviceNative: true,
|
|
71965
72756
|
mode: "singleton",
|
|
71966
72757
|
deviceTypes: [DeviceType.PetFountain],
|
|
72758
|
+
power: {
|
|
72759
|
+
kind: "field",
|
|
72760
|
+
field: "pumpRunning",
|
|
72761
|
+
set: "setPump"
|
|
72762
|
+
},
|
|
71967
72763
|
methods: {
|
|
71968
72764
|
/** Start or stop the pump. */
|
|
71969
72765
|
setPump: method(object({
|
|
@@ -73539,7 +74335,6 @@ var POWER_METER_DEVICE_TYPES = [
|
|
|
73539
74335
|
DeviceType.Light,
|
|
73540
74336
|
DeviceType.Fan,
|
|
73541
74337
|
DeviceType.Climate,
|
|
73542
|
-
DeviceType.Thermostat,
|
|
73543
74338
|
DeviceType.Humidifier,
|
|
73544
74339
|
DeviceType.WaterHeater,
|
|
73545
74340
|
DeviceType.MediaPlayer,
|
|
@@ -73548,8 +74343,7 @@ var POWER_METER_DEVICE_TYPES = [
|
|
|
73548
74343
|
DeviceType.Dishwasher,
|
|
73549
74344
|
DeviceType.Vacuum,
|
|
73550
74345
|
DeviceType.LawnMower,
|
|
73551
|
-
DeviceType.PetFeeder
|
|
73552
|
-
DeviceType.Heater
|
|
74346
|
+
DeviceType.PetFeeder
|
|
73553
74347
|
];
|
|
73554
74348
|
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
73555
74349
|
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
@@ -73662,117 +74456,6 @@ var powerMeterCapability = {
|
|
|
73662
74456
|
volatileStateFields: ["lastFetchedAt"],
|
|
73663
74457
|
customize: powerMeterCustomize
|
|
73664
74458
|
};
|
|
73665
|
-
var PowerSourceRefusalReasonSchema = _enum([
|
|
73666
|
-
"no-power-source",
|
|
73667
|
-
"switch-missing",
|
|
73668
|
-
"switch-offline",
|
|
73669
|
-
"switch-unknown",
|
|
73670
|
-
"restart-in-progress",
|
|
73671
|
-
"switch-call-failed"
|
|
73672
|
-
]);
|
|
73673
|
-
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
73674
|
-
accepted: literal(false),
|
|
73675
|
-
reason: PowerSourceRefusalReasonSchema,
|
|
73676
|
-
/** One line for the operator, naming the switch. */
|
|
73677
|
-
message: string()
|
|
73678
|
-
})]);
|
|
73679
|
-
var PowerSourceInfoSchema = object({
|
|
73680
|
-
/** False when the device has no power source linked; the rest is then empty. */
|
|
73681
|
-
linked: boolean(),
|
|
73682
|
-
switchDeviceId: number$1().nullable(),
|
|
73683
|
-
switchName: string().nullable(),
|
|
73684
|
-
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
73685
|
-
switchOn: boolean().nullable(),
|
|
73686
|
-
switchOnline: boolean().nullable(),
|
|
73687
|
-
restartInProgress: boolean(),
|
|
73688
|
-
offSeconds: number$1().int()
|
|
73689
|
-
});
|
|
73690
|
-
/** The composed slice: the switch state the link mirrors, plus the stored off time. */
|
|
73691
|
-
var PowerSourceStateSchema = object({
|
|
73692
|
-
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
73693
|
-
switchOn: boolean().nullable(),
|
|
73694
|
-
offSeconds: number$1().int(),
|
|
73695
|
-
lastFetchedAt: number$1()
|
|
73696
|
-
});
|
|
73697
|
-
var POWER_SOURCE_DEVICE_TYPES = [
|
|
73698
|
-
DeviceType.Gateway,
|
|
73699
|
-
DeviceType.Camera,
|
|
73700
|
-
DeviceType.Sensor,
|
|
73701
|
-
DeviceType.MediaPlayer,
|
|
73702
|
-
DeviceType.Vacuum,
|
|
73703
|
-
DeviceType.Generic,
|
|
73704
|
-
DeviceType.Washer,
|
|
73705
|
-
DeviceType.Dryer,
|
|
73706
|
-
DeviceType.Dishwasher,
|
|
73707
|
-
DeviceType.PetFountain,
|
|
73708
|
-
DeviceType.Fan,
|
|
73709
|
-
DeviceType.Humidifier,
|
|
73710
|
-
DeviceType.Climate,
|
|
73711
|
-
DeviceType.WaterHeater
|
|
73712
|
-
];
|
|
73713
|
-
var powerSourceCustomize = {
|
|
73714
|
-
version: 1,
|
|
73715
|
-
targets: [{
|
|
73716
|
-
id: "switch",
|
|
73717
|
-
cardinality: "scalar",
|
|
73718
|
-
vocabulary: {
|
|
73719
|
-
cap: "power-source",
|
|
73720
|
-
stateField: "switchOn"
|
|
73721
|
-
},
|
|
73722
|
-
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
73723
|
-
apply: "add",
|
|
73724
|
-
path: "switchOn",
|
|
73725
|
-
sources: [{
|
|
73726
|
-
kind: "switch",
|
|
73727
|
-
transforms: [{ kind: "identity" }]
|
|
73728
|
-
}],
|
|
73729
|
-
companions: [{
|
|
73730
|
-
kind: "constant",
|
|
73731
|
-
path: "offSeconds",
|
|
73732
|
-
value: 10,
|
|
73733
|
-
when: "add",
|
|
73734
|
-
formerValues: [0]
|
|
73735
|
-
}, {
|
|
73736
|
-
kind: "clock",
|
|
73737
|
-
path: "lastFetchedAt",
|
|
73738
|
-
when: "always"
|
|
73739
|
-
}],
|
|
73740
|
-
sourceReport: { status: "assignment-status" }
|
|
73741
|
-
}]
|
|
73742
|
-
};
|
|
73743
|
-
var deviceInput$1 = object({ deviceId: number$1().int().nonnegative() });
|
|
73744
|
-
var powerSourceCapability = {
|
|
73745
|
-
name: "power-source",
|
|
73746
|
-
scope: "device",
|
|
73747
|
-
mode: "singleton",
|
|
73748
|
-
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
73749
|
-
methods: {
|
|
73750
|
-
getStatus: systemMethod(deviceInput$1, PowerSourceInfoSchema, { auth: "protected" }),
|
|
73751
|
-
powerOn: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
73752
|
-
kind: "mutation",
|
|
73753
|
-
auth: "protected"
|
|
73754
|
-
}),
|
|
73755
|
-
powerOff: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
73756
|
-
kind: "mutation",
|
|
73757
|
-
auth: "protected"
|
|
73758
|
-
}),
|
|
73759
|
-
forceRestart: systemMethod(deviceInput$1, PowerSourceActionResultSchema, {
|
|
73760
|
-
kind: "mutation",
|
|
73761
|
-
auth: "protected"
|
|
73762
|
-
})
|
|
73763
|
-
},
|
|
73764
|
-
runtimeState: PowerSourceStateSchema,
|
|
73765
|
-
customize: powerSourceCustomize,
|
|
73766
|
-
/**
|
|
73767
|
-
* Runtime-state durability: **session** — mirrored from the linked switch on
|
|
73768
|
-
* its next report; a restored value would outlive the reading it names.
|
|
73769
|
-
*
|
|
73770
|
-
* See `RuntimeStateDurability`. Enforced by
|
|
73771
|
-
* `scripts/check-runtime-state-durability.ts`.
|
|
73772
|
-
*/
|
|
73773
|
-
durability: "session",
|
|
73774
|
-
history: "none"
|
|
73775
|
-
};
|
|
73776
74459
|
/**
|
|
73777
74460
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
73778
74461
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -77247,9 +77930,7 @@ var RUNNING_CUSTOMIZE_DEVICE_TYPES = [
|
|
|
77247
77930
|
DeviceType.Humidifier,
|
|
77248
77931
|
DeviceType.Fan,
|
|
77249
77932
|
DeviceType.Climate,
|
|
77250
|
-
DeviceType.
|
|
77251
|
-
DeviceType.PetFeeder,
|
|
77252
|
-
DeviceType.Heater
|
|
77933
|
+
DeviceType.PetFeeder
|
|
77253
77934
|
];
|
|
77254
77935
|
var runningStateCapability = {
|
|
77255
77936
|
name: "running-state",
|
|
@@ -78521,6 +79202,13 @@ var SsoBridgeClaimsSchema = object({
|
|
|
78521
79202
|
/** OAuth authorization-code binding — set only on `oauth-code` tokens. */
|
|
78522
79203
|
redirectUri: string().optional(),
|
|
78523
79204
|
integrationId: string().optional(),
|
|
79205
|
+
/**
|
|
79206
|
+
* The integration INSTANCE an OAuth link was made for (planning#415). Set on
|
|
79207
|
+
* `oauth-code`, `oauth-access` and `oauth-refresh` tokens of a link made
|
|
79208
|
+
* with `instance=`; absent on a link to the default instance. A cloud relay
|
|
79209
|
+
* reads it off the access token to route; the hub answers from the session.
|
|
79210
|
+
*/
|
|
79211
|
+
instanceId: string().optional(),
|
|
78524
79212
|
/** JWT ID — unique per issued code; consumed-set enforces single-use. */
|
|
78525
79213
|
jti: string().optional(),
|
|
78526
79214
|
/** PKCE S256 challenge — set only on `oauth-code` tokens issued to a public
|
|
@@ -79337,8 +80025,7 @@ var switchCapability = {
|
|
|
79337
80025
|
DeviceType.Switch,
|
|
79338
80026
|
DeviceType.Siren,
|
|
79339
80027
|
DeviceType.Light,
|
|
79340
|
-
DeviceType.Fan
|
|
79341
|
-
DeviceType.Heater
|
|
80028
|
+
DeviceType.Fan
|
|
79342
80029
|
],
|
|
79343
80030
|
methods: { setState: method(object({
|
|
79344
80031
|
deviceId: number$1(),
|
|
@@ -80032,7 +80719,7 @@ var temperatureSensorCapability = {
|
|
|
80032
80719
|
scope: "device",
|
|
80033
80720
|
deviceNative: true,
|
|
80034
80721
|
mode: "singleton",
|
|
80035
|
-
deviceTypes: [DeviceType.Sensor, DeviceType.
|
|
80722
|
+
deviceTypes: [DeviceType.Sensor, DeviceType.Climate],
|
|
80036
80723
|
methods: {},
|
|
80037
80724
|
status: {
|
|
80038
80725
|
schema: TemperatureSensorStatusSchema,
|
|
@@ -80900,6 +81587,8 @@ var OauthSessionSummarySchema = object({
|
|
|
80900
81587
|
userId: string(),
|
|
80901
81588
|
username: string(),
|
|
80902
81589
|
integrationId: string(),
|
|
81590
|
+
/** The instance the link was made for; `null` = the default instance. */
|
|
81591
|
+
instanceId: string().nullable().optional(),
|
|
80903
81592
|
scopes: array(TokenScopeSchema),
|
|
80904
81593
|
createdAt: number$1(),
|
|
80905
81594
|
lastUsedAt: number$1(),
|
|
@@ -80999,6 +81688,9 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(_void(), ob
|
|
|
80999
81688
|
access: "view"
|
|
81000
81689
|
}), method(object({
|
|
81001
81690
|
integrationId: string(),
|
|
81691
|
+
/** The integration instance the link is for. Omit for the default
|
|
81692
|
+
* instance — never pass the addon id to mean it. */
|
|
81693
|
+
instanceId: string().optional(),
|
|
81002
81694
|
userId: string(),
|
|
81003
81695
|
username: string(),
|
|
81004
81696
|
scopes: array(TokenScopeSchema),
|
|
@@ -81037,7 +81729,13 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(_void(), ob
|
|
|
81037
81729
|
}), method(object({ token: string() }), object({
|
|
81038
81730
|
userId: string(),
|
|
81039
81731
|
username: string(),
|
|
81040
|
-
scopes: array(TokenScopeSchema)
|
|
81732
|
+
scopes: array(TokenScopeSchema),
|
|
81733
|
+
/** The instance the link was made for, read from the SESSION.
|
|
81734
|
+
* Absent = the default instance. `oauthLinkServesInstance` is the
|
|
81735
|
+
* helper a per-instance route calls to compare it with the instance
|
|
81736
|
+
* of its path; no route declares `perInstance` yet (the Alexa
|
|
81737
|
+
* per-instance route, T3, is the first consumer). */
|
|
81738
|
+
instanceId: string().optional()
|
|
81041
81739
|
}).nullable(), { access: "view" }), method(object({ userId: string() }), array(ExternalIdentitySchema), { auth: "admin" }), method(ExternalIdentityKeySchema, object({
|
|
81042
81740
|
userId: string(),
|
|
81043
81741
|
issuer: string()
|
|
@@ -81679,6 +82377,13 @@ var waterHeaterCapability = {
|
|
|
81679
82377
|
deviceNative: true,
|
|
81680
82378
|
mode: "singleton",
|
|
81681
82379
|
deviceTypes: [DeviceType.WaterHeater],
|
|
82380
|
+
power: {
|
|
82381
|
+
kind: "mode",
|
|
82382
|
+
field: "operationMode",
|
|
82383
|
+
offValue: "off",
|
|
82384
|
+
set: "setOperationMode",
|
|
82385
|
+
lastField: "lastActiveMode"
|
|
82386
|
+
},
|
|
81682
82387
|
methods: {
|
|
81683
82388
|
setTargetTemp: method(object({
|
|
81684
82389
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -84370,15 +85075,16 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84370
85075
|
});
|
|
84371
85076
|
const meta = await this.ctx.api.deviceManager.loadMeta.query({ deviceId: id });
|
|
84372
85077
|
if (meta === null) throw new Error(`${this.providerName}: no persisted meta for "${stableId}" (id ${id}) — cannot reload`);
|
|
84373
|
-
const
|
|
84374
|
-
|
|
85078
|
+
const Class = this.deviceClassForRow({
|
|
85079
|
+
type: meta.type,
|
|
85080
|
+
stableId
|
|
85081
|
+
});
|
|
84375
85082
|
if (!Class) throw new Error(`${this.providerName}: no device class for type "${meta.type}" — "${stableId}" is an accessory child; reload its parent instead`);
|
|
84376
85083
|
await devices.create(stableId, Class, {}, meta.parentDeviceId ?? null);
|
|
84377
85084
|
const rows = await this.ctx.api.deviceManager.listPersistedByAddon.query({ addonId: this.addonId });
|
|
84378
85085
|
for (const row of rows) {
|
|
84379
85086
|
if (row.parentDeviceId !== id) continue;
|
|
84380
|
-
const
|
|
84381
|
-
const ChildClass = childType !== void 0 ? this.deviceClasses[childType] : void 0;
|
|
85087
|
+
const ChildClass = this.deviceClassForRow(row);
|
|
84382
85088
|
if (!ChildClass) continue;
|
|
84383
85089
|
try {
|
|
84384
85090
|
await devices.create(row.stableId, ChildClass, {}, id);
|
|
@@ -84405,6 +85111,16 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84405
85111
|
return { deviceId: id };
|
|
84406
85112
|
}
|
|
84407
85113
|
/**
|
|
85114
|
+
* The class a PERSISTED row is rebuilt with. Default: by type, from
|
|
85115
|
+
* `deviceClasses`. A provider whose distinct devices share one type (D1033:
|
|
85116
|
+
* a radiator valve and a room climate are both `climate`) overrides this
|
|
85117
|
+
* and tells them apart by the row's stable id or config.
|
|
85118
|
+
*/
|
|
85119
|
+
deviceClassForRow(row) {
|
|
85120
|
+
const type = Object.values(DeviceType).find((t) => t === row.type);
|
|
85121
|
+
return type !== void 0 ? this.deviceClasses[type] : void 0;
|
|
85122
|
+
}
|
|
85123
|
+
/**
|
|
84408
85124
|
* Restore devices from persisted state. Two-pass:
|
|
84409
85125
|
*
|
|
84410
85126
|
* 1. **Top-level pass** — invokes `kernel.devices.create()` for every
|
|
@@ -84465,7 +85181,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84465
85181
|
restored.add(saved.id);
|
|
84466
85182
|
return;
|
|
84467
85183
|
}
|
|
84468
|
-
const Class = this.
|
|
85184
|
+
const Class = this.deviceClassForRow(saved);
|
|
84469
85185
|
if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
|
|
84470
85186
|
if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
|
|
84471
85187
|
await this.healSavedConfig(saved, savedDevices);
|
|
@@ -84482,7 +85198,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84482
85198
|
};
|
|
84483
85199
|
const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
|
|
84484
85200
|
const restoreOne = async (saved) => {
|
|
84485
|
-
if (!this.
|
|
85201
|
+
if (!this.deviceClassForRow(saved)) {
|
|
84486
85202
|
this.ctx.logger.warn("No device class registered for restored type — skipping", {
|
|
84487
85203
|
tags: {
|
|
84488
85204
|
deviceId: saved.id,
|
|
@@ -84521,7 +85237,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84521
85237
|
const failedTopLevelIds = new Set(failures.map((failure) => failure.saved.id));
|
|
84522
85238
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
84523
85239
|
for (const saved of childRows) {
|
|
84524
|
-
if (!this.
|
|
85240
|
+
if (!this.deviceClassForRow(saved)) continue;
|
|
84525
85241
|
if (saved.parentDeviceId === null) continue;
|
|
84526
85242
|
if (restored.has(saved.parentDeviceId)) {
|
|
84527
85243
|
try {
|
|
@@ -84575,7 +85291,9 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84575
85291
|
}
|
|
84576
85292
|
};
|
|
84577
85293
|
DeviceType.HeatPump;
|
|
84578
|
-
DeviceType.
|
|
85294
|
+
DeviceType.Climate;
|
|
85295
|
+
DeviceRole.ClimateThermostat, DeviceRole.ClimateAc, DeviceRole.ClimateRadiator, DeviceRole.ClimateFloor, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit;
|
|
85296
|
+
DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.HeatPump, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, 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.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
84579
85297
|
new Set(Object.values(DeviceType));
|
|
84580
85298
|
/** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
|
|
84581
85299
|
function contribution(cap, schema, build) {
|
|
@@ -86581,7 +87299,7 @@ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pu
|
|
|
86581
87299
|
...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
|
|
86582
87300
|
...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
|
|
86583
87301
|
];
|
|
86584
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.
|
|
87302
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
86585
87303
|
/**
|
|
86586
87304
|
* Who looks like what (D1023). One look per {@link DeviceType}, refined by a
|
|
86587
87305
|
* {@link DeviceRole} where the role changes how the device reads (a leak
|
|
@@ -87225,9 +87943,9 @@ lookOf({
|
|
|
87225
87943
|
idle: INFO,
|
|
87226
87944
|
standby: MUTED
|
|
87227
87945
|
});
|
|
87228
|
-
DeviceType.Climate
|
|
87229
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.
|
|
87230
|
-
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
87946
|
+
DeviceType.Climate;
|
|
87947
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
87948
|
+
DeviceRole.Siren, DeviceRole.Floodlight, DeviceRole.Spotlight, DeviceRole.PirSensor, DeviceRole.Chime, DeviceRole.Autotrack, DeviceRole.Nightvision, DeviceRole.PrivacyMask, DeviceRole.Doorbell, DeviceRole.CoverBlind, DeviceRole.CoverShutter, DeviceRole.CoverAwning, DeviceRole.CoverCurtain, DeviceRole.CoverGarage, DeviceRole.CoverGate, DeviceRole.CoverDoor, DeviceRole.CoverWindow, DeviceRole.BinaryHelper, DeviceRole.Outlet, DeviceRole.SwitchHeater, DeviceRole.SwitchFan, DeviceRole.ClimateAc, DeviceRole.ClimateThermostat, DeviceRole.ClimateFloor, DeviceRole.ClimateRadiator, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit, DeviceRole.MotionSensor, DeviceRole.ContactSensor, DeviceRole.LeakSensor, DeviceRole.SmokeSensor, DeviceRole.COSensor, DeviceRole.GasSensor, DeviceRole.TamperSensor, DeviceRole.VibrationSensor, DeviceRole.ConnectivitySensor, DeviceRole.SoundSensor, DeviceRole.BinarySensor, DeviceRole.TemperatureSensor, DeviceRole.HumiditySensor, DeviceRole.AmbientLightSensor, DeviceRole.PressureSensor, DeviceRole.WindSensor, DeviceRole.RainSensor, DeviceRole.UvSensor, DeviceRole.SolarRadiationSensor, DeviceRole.SoilMoistureSensor, DeviceRole.PowerSensor, DeviceRole.EnergySensor, DeviceRole.VoltageSensor, DeviceRole.CurrentSensor, DeviceRole.AirQualitySensor, DeviceRole.BatterySensor, DeviceRole.NumericSensor, DeviceRole.EnumSensor, DeviceRole.DateTimeSensor, DeviceRole.GenericSensor, DeviceRole.NumericControl, DeviceRole.SelectControl, DeviceRole.TextControl, DeviceRole.DateTimeControl, DeviceRole.MobilePushNotifier, DeviceRole.MessagingNotifier, DeviceRole.EmailNotifier, DeviceRole.GenericNotifier, DeviceRole.IrTransmitter;
|
|
87231
87949
|
Object.freeze({
|
|
87232
87950
|
"accessories.getStatus": {
|
|
87233
87951
|
capName: "accessories",
|
|
@@ -90624,6 +91342,13 @@ Object.freeze({
|
|
|
90624
91342
|
access: "create",
|
|
90625
91343
|
role: "viewer"
|
|
90626
91344
|
},
|
|
91345
|
+
"fanControl.setOscillationAxis": {
|
|
91346
|
+
capName: "fan-control",
|
|
91347
|
+
capScope: "device",
|
|
91348
|
+
addonId: null,
|
|
91349
|
+
access: "create",
|
|
91350
|
+
role: "viewer"
|
|
91351
|
+
},
|
|
90627
91352
|
"fanControl.setPercentage": {
|
|
90628
91353
|
capName: "fan-control",
|
|
90629
91354
|
capScope: "device",
|
|
@@ -97645,7 +98370,7 @@ function projectAction(cap, action) {
|
|
|
97645
98370
|
};
|
|
97646
98371
|
}
|
|
97647
98372
|
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
97648
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.
|
|
98373
|
+
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, 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.HeatPump, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.PetFountain, DeviceType.Dishwasher, DeviceType.Washer, DeviceType.Dryer, DeviceType.Gateway;
|
|
97649
98374
|
DeviceFeature.BatteryOperated;
|
|
97650
98375
|
function bound(cap, methods = [], authority = "reported") {
|
|
97651
98376
|
return {
|
|
@@ -97704,6 +98429,149 @@ function offered(received) {
|
|
|
97704
98429
|
};
|
|
97705
98430
|
}
|
|
97706
98431
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
98432
|
+
var PowerSwitchRefusedError = class extends Error {
|
|
98433
|
+
reason;
|
|
98434
|
+
constructor(reason, message) {
|
|
98435
|
+
super(message);
|
|
98436
|
+
this.name = "PowerSwitchRefusedError";
|
|
98437
|
+
this.reason = reason;
|
|
98438
|
+
}
|
|
98439
|
+
};
|
|
98440
|
+
/** `true` / `false` from the owner slice, `null` when the hinted field is
|
|
98441
|
+
* absent or not of the hinted shape. */
|
|
98442
|
+
function powerOnFromSlice(hint, slice) {
|
|
98443
|
+
const value = slice?.[hint.field];
|
|
98444
|
+
switch (hint.kind) {
|
|
98445
|
+
case "field": return typeof value === "boolean" ? value : null;
|
|
98446
|
+
case "methods": return typeof value === "string" ? !hint.offValues.includes(value) : null;
|
|
98447
|
+
case "mode": return typeof value === "string" ? value !== hint.offValue : null;
|
|
98448
|
+
}
|
|
98449
|
+
}
|
|
98450
|
+
function ownerMethod(owner, name, capName) {
|
|
98451
|
+
const fn = Reflect.get(owner, name);
|
|
98452
|
+
if (typeof fn !== "function") throw new PowerSwitchRefusedError("owner-method-missing", `${capName} provider does not serve '${name}', which its power hint names`);
|
|
98453
|
+
return async (input) => fn.call(owner, input);
|
|
98454
|
+
}
|
|
98455
|
+
async function lastNonOffMode(host, capName, hint) {
|
|
98456
|
+
const isMode = (v) => typeof v === "string" && v.length > 0 && v !== hint.offValue;
|
|
98457
|
+
const fromHub = (await host.readHubSlice(capName).catch(() => null))?.[hint.lastField];
|
|
98458
|
+
if (isMode(fromHub)) return fromHub;
|
|
98459
|
+
const local = host.runtimeState.getCapState(capName)?.[hint.lastField];
|
|
98460
|
+
return isMode(local) ? local : null;
|
|
98461
|
+
}
|
|
98462
|
+
async function applyPower(host, capName, hint, owner, on) {
|
|
98463
|
+
const deviceId = host.deviceId;
|
|
98464
|
+
switch (hint.kind) {
|
|
98465
|
+
case "field":
|
|
98466
|
+
await ownerMethod(owner, hint.set, capName)({
|
|
98467
|
+
deviceId,
|
|
98468
|
+
on
|
|
98469
|
+
});
|
|
98470
|
+
return;
|
|
98471
|
+
case "methods":
|
|
98472
|
+
await ownerMethod(owner, on ? hint.on : hint.off, capName)({ deviceId });
|
|
98473
|
+
return;
|
|
98474
|
+
case "mode": {
|
|
98475
|
+
const set = ownerMethod(owner, hint.set, capName);
|
|
98476
|
+
if (!on) {
|
|
98477
|
+
await set({
|
|
98478
|
+
deviceId,
|
|
98479
|
+
mode: hint.offValue
|
|
98480
|
+
});
|
|
98481
|
+
return;
|
|
98482
|
+
}
|
|
98483
|
+
const mode = await lastNonOffMode(host, capName, hint);
|
|
98484
|
+
if (mode === null) throw new PowerSwitchRefusedError("no-last-mode", `device ${deviceId}: ${capName} has no last non-off mode to restore; pick a mode instead`);
|
|
98485
|
+
await set({
|
|
98486
|
+
deviceId,
|
|
98487
|
+
mode
|
|
98488
|
+
});
|
|
98489
|
+
return;
|
|
98490
|
+
}
|
|
98491
|
+
}
|
|
98492
|
+
}
|
|
98493
|
+
function hintedMethods(hint) {
|
|
98494
|
+
return hint.kind === "methods" ? [hint.on, hint.off] : [hint.set];
|
|
98495
|
+
}
|
|
98496
|
+
/** Whether `owner` serves every method its cap's power hint names. A media
|
|
98497
|
+
* player whose `turnOn` / `turnOff` are not served has no power, so no switch. */
|
|
98498
|
+
function powerServedBy(hint, owner) {
|
|
98499
|
+
if (hint === void 0) return false;
|
|
98500
|
+
return hintedMethods(hint).every((name) => typeof Reflect.get(owner, name) === "function");
|
|
98501
|
+
}
|
|
98502
|
+
/** Builds the `switch` provider and starts the slice projection. Exported
|
|
98503
|
+
* for the specs; providers call `registerPowerSwitch`. */
|
|
98504
|
+
function buildPowerSwitch(host, cap, owner) {
|
|
98505
|
+
const hint = cap.power;
|
|
98506
|
+
if (hint === void 0) throw new Error(`capability '${cap.name}' has no power hint: it has no switch to project`);
|
|
98507
|
+
const read = () => powerOnFromSlice(hint, host.runtimeState.getCapState(cap.name));
|
|
98508
|
+
const project = () => {
|
|
98509
|
+
const on = read();
|
|
98510
|
+
const current = host.runtimeState.getCapState(switchCapability.name);
|
|
98511
|
+
if (current !== void 0 && current["on"] === on) return;
|
|
98512
|
+
const next = {
|
|
98513
|
+
on,
|
|
98514
|
+
lastChangedAt: Date.now()
|
|
98515
|
+
};
|
|
98516
|
+
host.runtimeState.setCapState(switchCapability.name, next);
|
|
98517
|
+
};
|
|
98518
|
+
const stop = host.runtimeState.subscribeCap(cap.name, project);
|
|
98519
|
+
if (host.runtimeState.getCapState(cap.name) !== void 0) project();
|
|
98520
|
+
return {
|
|
98521
|
+
provider: {
|
|
98522
|
+
getStatus: async () => {
|
|
98523
|
+
const current = host.runtimeState.getCapState(switchCapability.name);
|
|
98524
|
+
const lastChangedAt = typeof current?.["lastChangedAt"] === "number" ? current["lastChangedAt"] : 0;
|
|
98525
|
+
return {
|
|
98526
|
+
on: read(),
|
|
98527
|
+
lastChangedAt
|
|
98528
|
+
};
|
|
98529
|
+
},
|
|
98530
|
+
setState: async ({ on }) => applyPower(host, cap.name, hint, owner, on)
|
|
98531
|
+
},
|
|
98532
|
+
dispose: stop
|
|
98533
|
+
};
|
|
98534
|
+
}
|
|
98535
|
+
/**
|
|
98536
|
+
* Registers `switch` for a device whose `cap` carries a `power` hint, built
|
|
98537
|
+
* from `owner` (the provider the device registered for `cap`). Call it right
|
|
98538
|
+
* after `registerNativeCap(cap, owner)`.
|
|
98539
|
+
*/
|
|
98540
|
+
function registerPowerSwitch(device, cap, owner) {
|
|
98541
|
+
const deviceId = device.ctx.id;
|
|
98542
|
+
if (cap.power !== void 0 && !powerServedBy(cap.power, owner)) {
|
|
98543
|
+
device.ctx.logger.debug("power switch: owner serves no power control, no switch registered", {
|
|
98544
|
+
tags: { deviceId },
|
|
98545
|
+
meta: { capName: cap.name }
|
|
98546
|
+
});
|
|
98547
|
+
return () => {};
|
|
98548
|
+
}
|
|
98549
|
+
const host = {
|
|
98550
|
+
deviceId,
|
|
98551
|
+
runtimeState: device.runtimeState,
|
|
98552
|
+
registerNativeCap: (c, p) => device.ctx.registerNativeCap(c, p),
|
|
98553
|
+
readHubSlice: async (capName) => {
|
|
98554
|
+
try {
|
|
98555
|
+
return await device.ctx.api.deviceState.getCapSlice.query({
|
|
98556
|
+
deviceId,
|
|
98557
|
+
capName
|
|
98558
|
+
});
|
|
98559
|
+
} catch (err) {
|
|
98560
|
+
device.ctx.logger.warn("power switch: hub slice read failed", {
|
|
98561
|
+
tags: { deviceId },
|
|
98562
|
+
meta: {
|
|
98563
|
+
capName,
|
|
98564
|
+
error: err instanceof Error ? err.message : String(err)
|
|
98565
|
+
}
|
|
98566
|
+
});
|
|
98567
|
+
return null;
|
|
98568
|
+
}
|
|
98569
|
+
}
|
|
98570
|
+
};
|
|
98571
|
+
const built = buildPowerSwitch(host, cap, owner);
|
|
98572
|
+
host.registerNativeCap(switchCapability, built.provider);
|
|
98573
|
+
return built.dispose;
|
|
98574
|
+
}
|
|
97707
98575
|
Object.freeze({
|
|
97708
98576
|
"accessories.getStatus": [{
|
|
97709
98577
|
name: "deviceId",
|
|
@@ -98697,6 +99565,11 @@ Object.freeze({
|
|
|
98697
99565
|
form: "single",
|
|
98698
99566
|
optional: false
|
|
98699
99567
|
}],
|
|
99568
|
+
"fanControl.setOscillationAxis": [{
|
|
99569
|
+
name: "deviceId",
|
|
99570
|
+
form: "single",
|
|
99571
|
+
optional: false
|
|
99572
|
+
}],
|
|
98700
99573
|
"fanControl.setPercentage": [{
|
|
98701
99574
|
name: "deviceId",
|
|
98702
99575
|
form: "single",
|
|
@@ -102326,4 +103199,4 @@ function keepBodyKeys(provider, keys) {
|
|
|
102326
103199
|
};
|
|
102327
103200
|
}
|
|
102328
103201
|
//#endregion
|
|
102329
|
-
export {
|
|
103202
|
+
export { DeviceType as $, temperatureSensorCapability as A, rowsDepsOver as B, plainEventVocabulary as C, runningFromDishwasherOperation as D, registerPowerSwitch as E, defineCustomActions as F, literal as G, _null as H, defineEntryKind as I, record as J, number$1 as K, entryBody as L, RecordIntegrationInstances as M, createEvent as N, runningStateCapability as O, customAction as P, DeviceRole as Q, isSecretMarker as R, numericSensorCapability as S, quantityFromUnit as T, array as U, _enum as V, boolean as W, union as X, string as Y, unknown as Z, heatPumpControlCapability as _, BaseDevice as a, ingestReading as b, RUNNING_STATE_COLD as c, climateControlCapability as d, errMsg as et, consumablesCapability as f, eventEmitterCapability as g, enumSensorCapability as h, testBoschInstance as i, waterHeaterCapability as j, switchCapability as k, batteryCapability as l, dishwasherCapability as m, boschRowsPorts as n, BaseDeviceProvider as o, deviceAdoptionCapability as p, object as q, keepBodyKeys as r, DishwasherOperationStateSchema as s, boschRowsMigration as t, EventCategory as tt, binaryCapability as u, heatPumpStatusCapability as v, powerMeterCapability as w, integrationInstancesCapability as x, heatingValveCapability as y, isSecretMarkerLike as z };
|