@camstack/addon-provider-bosch 0.1.51 → 0.1.52
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 +7 -0
- package/dist/{account-integration-offline-7CH3r5jI.js → account-integration-offline-BEzGeJcl.js} +1 -1
- package/dist/{account-integration-offline-B1Wgr_rT.mjs → account-integration-offline-D5vjmfia.mjs} +1 -1
- package/dist/{bosch-instances-CdvcBYye.mjs → bosch-instances-DSE6IXdU.mjs} +775 -213
- package/dist/{bosch-instances-CYBnIcHg.js → bosch-instances-LWbVnsaD.js} +780 -212
- package/dist/home-connect/addon.js +2 -2
- package/dist/home-connect/addon.mjs +2 -2
- 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-Bn346wCJ.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: [{
|
|
@@ -17444,7 +17505,7 @@ Object.freeze({
|
|
|
17444
17505
|
}
|
|
17445
17506
|
});
|
|
17446
17507
|
//#endregion
|
|
17447
|
-
//#region ../types/dist/implemented-methods-
|
|
17508
|
+
//#region ../types/dist/implemented-methods-5YMDeE3f.mjs
|
|
17448
17509
|
/**
|
|
17449
17510
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
17450
17511
|
*
|
|
@@ -44153,6 +44214,13 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
44153
44214
|
"in"
|
|
44154
44215
|
]);
|
|
44155
44216
|
/**
|
|
44217
|
+
* The stable name of an event leaf. A path (`when.0.1`) moves when a row is
|
|
44218
|
+
* reordered; the id does not, so a `triggered-by` leaf keeps pointing at the
|
|
44219
|
+
* same row. Optional: a leaf nothing refers to needs none.
|
|
44220
|
+
*/
|
|
44221
|
+
var EVENT_LEAF_ID_RE = /^[A-Za-z0-9_-]{1,40}$/;
|
|
44222
|
+
var eventLeafId = { id: string().regex(EVENT_LEAF_ID_RE).optional() };
|
|
44223
|
+
/**
|
|
44156
44224
|
* D8 compliance, and it is the reason a "becomes" leaf is not merely an event
|
|
44157
44225
|
* subscription: it evaluates against the **state mirror**, which is reconciled,
|
|
44158
44226
|
* and an event only WAKES the evaluation. A dropped event therefore DELAYS a
|
|
@@ -44160,6 +44228,7 @@ var AutomationConditionOperatorSchema = _enum([
|
|
|
44160
44228
|
*/
|
|
44161
44229
|
var DeviceBecomesLeafSchema = object({
|
|
44162
44230
|
kind: literal("device-becomes"),
|
|
44231
|
+
...eventLeafId,
|
|
44163
44232
|
deviceId: number$1().int().nonnegative(),
|
|
44164
44233
|
cap: string().min(1),
|
|
44165
44234
|
fieldPath: string().min(1),
|
|
@@ -44212,6 +44281,7 @@ var DeviceBecomesLeafSchema = object({
|
|
|
44212
44281
|
});
|
|
44213
44282
|
var DeviceEventLeafSchema = object({
|
|
44214
44283
|
kind: literal("device-event"),
|
|
44284
|
+
...eventLeafId,
|
|
44215
44285
|
/** An `EventCategory` value. */
|
|
44216
44286
|
category: string().min(1),
|
|
44217
44287
|
deviceId: number$1().int().nonnegative().optional()
|
|
@@ -44224,18 +44294,24 @@ var DeviceEventLeafSchema = object({
|
|
|
44224
44294
|
*/
|
|
44225
44295
|
var ScheduleLeafSchema = object({
|
|
44226
44296
|
kind: literal("schedule"),
|
|
44297
|
+
...eventLeafId,
|
|
44227
44298
|
at: TimeSpecSchema.optional(),
|
|
44228
44299
|
days: array(WeekdayKeySchema).min(1).max(7).optional(),
|
|
44229
44300
|
/** @deprecated legacy cron — new schedules use `at`. */
|
|
44230
44301
|
cron: string().min(1).max(120).optional()
|
|
44231
44302
|
}).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({
|
|
44303
|
+
var ManualLeafSchema = object({
|
|
44304
|
+
kind: literal("manual"),
|
|
44305
|
+
...eventLeafId
|
|
44306
|
+
});
|
|
44233
44307
|
var IntervalLeafSchema = object({
|
|
44234
44308
|
kind: literal("interval"),
|
|
44309
|
+
...eventLeafId,
|
|
44235
44310
|
minutes: number$1().int().min(1).max(1440)
|
|
44236
44311
|
});
|
|
44237
44312
|
var DetectionLeafSchema = object({
|
|
44238
44313
|
kind: literal("detection"),
|
|
44314
|
+
...eventLeafId,
|
|
44239
44315
|
/** One macro class each, optionally narrowed by a label; a track of any listed subject fires. */
|
|
44240
44316
|
subjects: array(DetectionSubjectSchema).min(1),
|
|
44241
44317
|
deviceIds: array(number$1().int().nonnegative()).min(1),
|
|
@@ -44246,6 +44322,7 @@ var DetectionLeafSchema = object({
|
|
|
44246
44322
|
});
|
|
44247
44323
|
var StationaryLeafSchema = object({
|
|
44248
44324
|
kind: literal("stationary"),
|
|
44325
|
+
...eventLeafId,
|
|
44249
44326
|
deviceIds: array(number$1().int().nonnegative()).min(1),
|
|
44250
44327
|
subjects: array(StationarySubjectSchema).min(1).optional(),
|
|
44251
44328
|
zones: array(string().min(1)).min(1).optional(),
|
|
@@ -44291,6 +44368,41 @@ var StationaryPresentLeafSchema = object({
|
|
|
44291
44368
|
minCount: number$1().int().positive().optional()
|
|
44292
44369
|
});
|
|
44293
44370
|
/**
|
|
44371
|
+
* True while the site-local clock is inside a daily window and/or on one of
|
|
44372
|
+
* the named days. `days` names the local day a window STARTS on, so
|
|
44373
|
+
* 22:00-06:00 on weekdays is still inside at 01:00 Saturday. A one-date spec
|
|
44374
|
+
* (`kind: 'date'`) is refused: only a clock time or a sun event makes a window
|
|
44375
|
+
* that repeats every day.
|
|
44376
|
+
*/
|
|
44377
|
+
var TimeIsLeafSchema = object({
|
|
44378
|
+
kind: literal("time-is"),
|
|
44379
|
+
between: object({
|
|
44380
|
+
from: TimeSpecSchema,
|
|
44381
|
+
to: TimeSpecSchema
|
|
44382
|
+
}).optional(),
|
|
44383
|
+
days: array(WeekdayKeySchema).min(1).max(7).optional()
|
|
44384
|
+
}).superRefine((leaf, ctx) => {
|
|
44385
|
+
if (leaf.between === void 0 && leaf.days === void 0) ctx.addIssue({
|
|
44386
|
+
code: "custom",
|
|
44387
|
+
message: "a time-is leaf needs a between window, days, or both"
|
|
44388
|
+
});
|
|
44389
|
+
for (const end of ["from", "to"]) if (leaf.between?.[end].kind === "date") ctx.addIssue({
|
|
44390
|
+
code: "custom",
|
|
44391
|
+
message: "a one-date time cannot bound a daily window: use a clock time or a sun event",
|
|
44392
|
+
path: ["between", end]
|
|
44393
|
+
});
|
|
44394
|
+
});
|
|
44395
|
+
/**
|
|
44396
|
+
* True for the run that one of the named event leaves started. It reads the
|
|
44397
|
+
* run, not a device: in the tree it is judged against the leaf that fired, in
|
|
44398
|
+
* a step guard against the trigger the run carries. A run no event leaf
|
|
44399
|
+
* started (a state-only tree becoming true) is false.
|
|
44400
|
+
*/
|
|
44401
|
+
var TriggeredByLeafSchema = object({
|
|
44402
|
+
kind: literal("triggered-by"),
|
|
44403
|
+
leafIds: array(string().regex(EVENT_LEAF_ID_RE)).min(1).max(20)
|
|
44404
|
+
});
|
|
44405
|
+
/**
|
|
44294
44406
|
* The escape hatch: the bounded expression engine over named device bindings.
|
|
44295
44407
|
* Declared as a plain object so a discriminated union can read `kind`; the
|
|
44296
44408
|
* author-time validation is the SAME function `ExpressionSourceSchema` runs.
|
|
@@ -44336,7 +44448,9 @@ var AutomationWhenSchema = lazy(() => discriminatedUnion("kind", [
|
|
|
44336
44448
|
StationaryLeafSchema,
|
|
44337
44449
|
DeviceIsLeafSchema,
|
|
44338
44450
|
StationaryPresentLeafSchema,
|
|
44339
|
-
ExpressionLeafSchema
|
|
44451
|
+
ExpressionLeafSchema,
|
|
44452
|
+
TimeIsLeafSchema,
|
|
44453
|
+
TriggeredByLeafSchema
|
|
44340
44454
|
]));
|
|
44341
44455
|
var EVENT_KINDS = new Set([
|
|
44342
44456
|
"device-becomes",
|
|
@@ -44390,6 +44504,28 @@ function hasEventLeaves(node) {
|
|
|
44390
44504
|
});
|
|
44391
44505
|
return found;
|
|
44392
44506
|
}
|
|
44507
|
+
/** The event leaves of the tree that carry an id, in tree order. */
|
|
44508
|
+
function identifiedEventLeaves(when) {
|
|
44509
|
+
const found = [];
|
|
44510
|
+
forEachLeaf(when, (leaf, path) => {
|
|
44511
|
+
if (isEventLeaf(leaf) && leaf.id !== void 0) found.push({
|
|
44512
|
+
id: leaf.id,
|
|
44513
|
+
path,
|
|
44514
|
+
leaf
|
|
44515
|
+
});
|
|
44516
|
+
});
|
|
44517
|
+
return found;
|
|
44518
|
+
}
|
|
44519
|
+
/** Does the tree hold a `triggered-by` leaf? */
|
|
44520
|
+
function hasTriggeredByLeaves(when) {
|
|
44521
|
+
let found = false;
|
|
44522
|
+
forEachLeaf(when, (leaf) => {
|
|
44523
|
+
if (leaf.kind === "triggered-by") found = true;
|
|
44524
|
+
});
|
|
44525
|
+
return found;
|
|
44526
|
+
}
|
|
44527
|
+
/** Why a `triggered-by` leaf is refused outside an automation: there is no trigger row to name. */
|
|
44528
|
+
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
44529
|
function hasEmptyContainer(node) {
|
|
44394
44530
|
if (isLeaf(node)) return false;
|
|
44395
44531
|
if (node.kind === "not") return hasEmptyContainer(node.child);
|
|
@@ -44448,6 +44584,11 @@ function validateWhenTree(when) {
|
|
|
44448
44584
|
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
44585
|
const leaves = countWhenLeaves(when);
|
|
44450
44586
|
if (leaves > 32) return `the rules use ${leaves} rows — the limit is 32`;
|
|
44587
|
+
const seen = /* @__PURE__ */ new Set();
|
|
44588
|
+
for (const entry of identifiedEventLeaves(when)) {
|
|
44589
|
+
if (seen.has(entry.id)) return `two trigger rows share the id "${entry.id}" — an id names one row`;
|
|
44590
|
+
seen.add(entry.id);
|
|
44591
|
+
}
|
|
44451
44592
|
const need = eventNeed(when, "when", false);
|
|
44452
44593
|
if ("error" in need) return need.error;
|
|
44453
44594
|
const [first, second] = need.paths;
|
|
@@ -44738,7 +44879,18 @@ var GuardedCapStepSchema = AutomationCapStepBaseSchema.extend(stepGuardShape).re
|
|
|
44738
44879
|
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
44739
44880
|
path: ["actionId"]
|
|
44740
44881
|
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
44741
|
-
|
|
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, GuardedCapStepSchema]).superRefine((step, ctx) => {
|
|
44888
|
+
if (step.if !== void 0 && hasTriggeredByLeaves(step.if)) ctx.addIssue({
|
|
44889
|
+
code: "custom",
|
|
44890
|
+
message: TRIGGERED_BY_NEEDS_AUTOMATION,
|
|
44891
|
+
path: ["if"]
|
|
44892
|
+
});
|
|
44893
|
+
});
|
|
44742
44894
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
44743
44895
|
GuardedWaitStepSchema,
|
|
44744
44896
|
GuardedCapStepSchema,
|
|
@@ -44835,6 +44987,11 @@ var STATIONARY_DESCRIPTIONS = {
|
|
|
44835
44987
|
phase: "appeared or departed",
|
|
44836
44988
|
entryId: "the stopped object"
|
|
44837
44989
|
};
|
|
44990
|
+
var BECOMES_DESCRIPTIONS = {
|
|
44991
|
+
deviceId: "the device that changed",
|
|
44992
|
+
from: "the value before the change, empty when it is not known",
|
|
44993
|
+
to: "the value it changed to"
|
|
44994
|
+
};
|
|
44838
44995
|
function describe$1(descriptions) {
|
|
44839
44996
|
return Object.entries(descriptions).map(([name, description]) => ({
|
|
44840
44997
|
name,
|
|
@@ -44843,6 +45000,10 @@ function describe$1(descriptions) {
|
|
|
44843
45000
|
}
|
|
44844
45001
|
describe$1(DETECTION_DESCRIPTIONS);
|
|
44845
45002
|
describe$1(STATIONARY_DESCRIPTIONS);
|
|
45003
|
+
describe$1(BECOMES_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45004
|
+
...v,
|
|
45005
|
+
hintKey: "becomesDeviceId"
|
|
45006
|
+
} : v);
|
|
44846
45007
|
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
44847
45008
|
/**
|
|
44848
45009
|
* Schedulers (D983) — an entity that performs actions over the day.
|
|
@@ -44867,6 +45028,13 @@ var SchedulerStateTreeSchema = AutomationWhenSchema.superRefine((when, ctx) => {
|
|
|
44867
45028
|
});
|
|
44868
45029
|
return;
|
|
44869
45030
|
}
|
|
45031
|
+
if (hasTriggeredByLeaves(when)) {
|
|
45032
|
+
ctx.addIssue({
|
|
45033
|
+
code: "custom",
|
|
45034
|
+
message: TRIGGERED_BY_NEEDS_AUTOMATION
|
|
45035
|
+
});
|
|
45036
|
+
return;
|
|
45037
|
+
}
|
|
44870
45038
|
const problem = validateWhenTree(when);
|
|
44871
45039
|
if (problem !== null) ctx.addIssue({
|
|
44872
45040
|
code: "custom",
|
|
@@ -45087,6 +45255,8 @@ var AutomationConditionTraceNodeSchema = object({
|
|
|
45087
45255
|
"device-is",
|
|
45088
45256
|
"expression",
|
|
45089
45257
|
"stationary-present",
|
|
45258
|
+
"time-is",
|
|
45259
|
+
"triggered-by",
|
|
45090
45260
|
"device-becomes",
|
|
45091
45261
|
"device-event",
|
|
45092
45262
|
"schedule",
|
|
@@ -45420,7 +45590,7 @@ object({
|
|
|
45420
45590
|
* is irrelevant; the array is treated as a set by consumers. */
|
|
45421
45591
|
hiddenChildIds: array(number$1()).readonly()
|
|
45422
45592
|
});
|
|
45423
|
-
DeviceType.Camera, DeviceType.Switch, DeviceType.Light, DeviceType.Sensor, DeviceType.
|
|
45593
|
+
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
45594
|
deviceId: number$1().int().nonnegative(),
|
|
45425
45595
|
childDeviceId: number$1().int().nonnegative(),
|
|
45426
45596
|
hidden: boolean()
|
|
@@ -47718,7 +47888,7 @@ var BATTERY_DEVICE_TYPES = [
|
|
|
47718
47888
|
DeviceType.Switch,
|
|
47719
47889
|
DeviceType.PetFeeder,
|
|
47720
47890
|
DeviceType.Container,
|
|
47721
|
-
DeviceType.
|
|
47891
|
+
DeviceType.Climate,
|
|
47722
47892
|
DeviceType.EventEmitter
|
|
47723
47893
|
];
|
|
47724
47894
|
/** Generic Customize target for the percentage reading (D936). */
|
|
@@ -49481,7 +49651,14 @@ var climateControlCapability = {
|
|
|
49481
49651
|
scope: "device",
|
|
49482
49652
|
deviceNative: true,
|
|
49483
49653
|
mode: "singleton",
|
|
49484
|
-
deviceTypes: [DeviceType.
|
|
49654
|
+
deviceTypes: [DeviceType.Climate],
|
|
49655
|
+
power: {
|
|
49656
|
+
kind: "mode",
|
|
49657
|
+
field: "mode",
|
|
49658
|
+
offValue: "off",
|
|
49659
|
+
set: "setMode",
|
|
49660
|
+
lastField: "lastActiveMode"
|
|
49661
|
+
},
|
|
49485
49662
|
methods: {
|
|
49486
49663
|
setMode: method(object({
|
|
49487
49664
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -49866,6 +50043,7 @@ var CompositionProblemSchema = object({
|
|
|
49866
50043
|
"nested-field",
|
|
49867
50044
|
"missing-field",
|
|
49868
50045
|
"kind-mismatch",
|
|
50046
|
+
"source-kind-mismatch",
|
|
49869
50047
|
"may-be-null",
|
|
49870
50048
|
"source-missing",
|
|
49871
50049
|
"source-unreadable",
|
|
@@ -49887,7 +50065,8 @@ var CompositionProblemSchema = object({
|
|
|
49887
50065
|
"command-method-unknown",
|
|
49888
50066
|
"command-source-unknown",
|
|
49889
50067
|
"command-input-unknown",
|
|
49890
|
-
"command-input-missing"
|
|
50068
|
+
"command-input-missing",
|
|
50069
|
+
"power-source-is-actuator"
|
|
49891
50070
|
]),
|
|
49892
50071
|
path: string(),
|
|
49893
50072
|
message: string()
|
|
@@ -50925,6 +51104,13 @@ var mediaPlayerCapability = {
|
|
|
50925
51104
|
deviceNative: true,
|
|
50926
51105
|
mode: "singleton",
|
|
50927
51106
|
deviceTypes: [DeviceType.MediaPlayer],
|
|
51107
|
+
power: {
|
|
51108
|
+
kind: "methods",
|
|
51109
|
+
field: "state",
|
|
51110
|
+
offValues: ["off", "standby"],
|
|
51111
|
+
on: "turnOn",
|
|
51112
|
+
off: "turnOff"
|
|
51113
|
+
},
|
|
50928
51114
|
methods: {
|
|
50929
51115
|
turnOn: optionalMethod(deviceOnly, _void(), {
|
|
50930
51116
|
kind: "mutation",
|
|
@@ -51055,7 +51241,7 @@ var mediaPlayerCapability = {
|
|
|
51055
51241
|
isMuted: { kind: "boolean" }
|
|
51056
51242
|
} }
|
|
51057
51243
|
};
|
|
51058
|
-
DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.
|
|
51244
|
+
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
51245
|
var NUMBER = {
|
|
51060
51246
|
kind: "fixed",
|
|
51061
51247
|
result: ["number"]
|
|
@@ -51138,7 +51324,7 @@ function numeric(input) {
|
|
|
51138
51324
|
}, ...alsoFrom === void 0 ? [] : [alsoFrom.accept]]
|
|
51139
51325
|
};
|
|
51140
51326
|
}
|
|
51141
|
-
var CLIMATE_DEVICES = [DeviceType.
|
|
51327
|
+
var CLIMATE_DEVICES = [DeviceType.Climate];
|
|
51142
51328
|
function binary(input) {
|
|
51143
51329
|
return {
|
|
51144
51330
|
...input,
|
|
@@ -51460,8 +51646,8 @@ var GROUP_TEMPLATES = [
|
|
|
51460
51646
|
actuator({
|
|
51461
51647
|
id: "climate",
|
|
51462
51648
|
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.
|
|
51649
|
+
deviceType: DeviceType.Climate,
|
|
51650
|
+
memberDeviceTypes: [DeviceType.Climate],
|
|
51465
51651
|
outputCap: "group-active",
|
|
51466
51652
|
outputField: "active",
|
|
51467
51653
|
member: {
|
|
@@ -51504,6 +51690,118 @@ function actuator(input) {
|
|
|
51504
51690
|
new Map(GROUP_TEMPLATES.map((template) => [template.id, template]));
|
|
51505
51691
|
DeviceFeature.LightColorRgb, DeviceFeature.LightColorHsv, DeviceFeature.LightColorMired;
|
|
51506
51692
|
DeviceType.Camera;
|
|
51693
|
+
var PowerSourceRefusalReasonSchema = _enum([
|
|
51694
|
+
"no-power-source",
|
|
51695
|
+
"switch-missing",
|
|
51696
|
+
"switch-offline",
|
|
51697
|
+
"switch-unknown",
|
|
51698
|
+
"restart-in-progress",
|
|
51699
|
+
"switch-call-failed",
|
|
51700
|
+
"switch-is-actuator"
|
|
51701
|
+
]);
|
|
51702
|
+
var PowerSourceActionResultSchema = discriminatedUnion("accepted", [object({ accepted: literal(true) }), object({
|
|
51703
|
+
accepted: literal(false),
|
|
51704
|
+
reason: PowerSourceRefusalReasonSchema,
|
|
51705
|
+
/** One line for the operator, naming the switch. */
|
|
51706
|
+
message: string()
|
|
51707
|
+
})]);
|
|
51708
|
+
var PowerSourceInfoSchema = object({
|
|
51709
|
+
/** False when the device has no power source linked; the rest is then empty. */
|
|
51710
|
+
linked: boolean(),
|
|
51711
|
+
switchDeviceId: number$1().nullable(),
|
|
51712
|
+
switchName: string().nullable(),
|
|
51713
|
+
/** The switch's on/off state, or null when it is unknown. Never false for unknown (D393). */
|
|
51714
|
+
switchOn: boolean().nullable(),
|
|
51715
|
+
switchOnline: boolean().nullable(),
|
|
51716
|
+
restartInProgress: boolean(),
|
|
51717
|
+
offSeconds: number$1().int()
|
|
51718
|
+
});
|
|
51719
|
+
/** The composed slice: the switch state the link mirrors, plus the stored off time. */
|
|
51720
|
+
var PowerSourceStateSchema = object({
|
|
51721
|
+
/** Null while the switch has not reported: unknown is never `false` (D393). */
|
|
51722
|
+
switchOn: boolean().nullable(),
|
|
51723
|
+
offSeconds: number$1().int(),
|
|
51724
|
+
lastFetchedAt: number$1()
|
|
51725
|
+
});
|
|
51726
|
+
var POWER_SOURCE_DEVICE_TYPES = [
|
|
51727
|
+
DeviceType.Gateway,
|
|
51728
|
+
DeviceType.Camera,
|
|
51729
|
+
DeviceType.Sensor,
|
|
51730
|
+
DeviceType.MediaPlayer,
|
|
51731
|
+
DeviceType.Vacuum,
|
|
51732
|
+
DeviceType.Generic,
|
|
51733
|
+
DeviceType.Washer,
|
|
51734
|
+
DeviceType.Dryer,
|
|
51735
|
+
DeviceType.Dishwasher,
|
|
51736
|
+
DeviceType.PetFountain,
|
|
51737
|
+
DeviceType.Fan,
|
|
51738
|
+
DeviceType.Humidifier,
|
|
51739
|
+
DeviceType.Climate,
|
|
51740
|
+
DeviceType.WaterHeater
|
|
51741
|
+
];
|
|
51742
|
+
var powerSourceCustomize = {
|
|
51743
|
+
version: 1,
|
|
51744
|
+
targets: [{
|
|
51745
|
+
id: "switch",
|
|
51746
|
+
cardinality: "scalar",
|
|
51747
|
+
vocabulary: {
|
|
51748
|
+
cap: "power-source",
|
|
51749
|
+
stateField: "switchOn"
|
|
51750
|
+
},
|
|
51751
|
+
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
51752
|
+
apply: "add",
|
|
51753
|
+
path: "switchOn",
|
|
51754
|
+
sources: [{
|
|
51755
|
+
kind: "switch",
|
|
51756
|
+
transforms: [{ kind: "identity" }]
|
|
51757
|
+
}],
|
|
51758
|
+
companions: [{
|
|
51759
|
+
kind: "constant",
|
|
51760
|
+
path: "offSeconds",
|
|
51761
|
+
value: 10,
|
|
51762
|
+
when: "add",
|
|
51763
|
+
formerValues: [0]
|
|
51764
|
+
}, {
|
|
51765
|
+
kind: "clock",
|
|
51766
|
+
path: "lastFetchedAt",
|
|
51767
|
+
when: "always"
|
|
51768
|
+
}],
|
|
51769
|
+
sourceReport: { status: "assignment-status" }
|
|
51770
|
+
}]
|
|
51771
|
+
};
|
|
51772
|
+
var deviceInput$2 = object({ deviceId: number$1().int().nonnegative() });
|
|
51773
|
+
var powerSourceCapability = {
|
|
51774
|
+
name: "power-source",
|
|
51775
|
+
scope: "device",
|
|
51776
|
+
mode: "singleton",
|
|
51777
|
+
deviceTypes: POWER_SOURCE_DEVICE_TYPES,
|
|
51778
|
+
methods: {
|
|
51779
|
+
getStatus: systemMethod(deviceInput$2, PowerSourceInfoSchema, { auth: "protected" }),
|
|
51780
|
+
powerOn: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
51781
|
+
kind: "mutation",
|
|
51782
|
+
auth: "protected"
|
|
51783
|
+
}),
|
|
51784
|
+
powerOff: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
51785
|
+
kind: "mutation",
|
|
51786
|
+
auth: "protected"
|
|
51787
|
+
}),
|
|
51788
|
+
forceRestart: systemMethod(deviceInput$2, PowerSourceActionResultSchema, {
|
|
51789
|
+
kind: "mutation",
|
|
51790
|
+
auth: "protected"
|
|
51791
|
+
})
|
|
51792
|
+
},
|
|
51793
|
+
runtimeState: PowerSourceStateSchema,
|
|
51794
|
+
customize: powerSourceCustomize,
|
|
51795
|
+
/**
|
|
51796
|
+
* Runtime-state durability: **session** — mirrored from the linked switch on
|
|
51797
|
+
* its next report; a restored value would outlive the reading it names.
|
|
51798
|
+
*
|
|
51799
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
51800
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
51801
|
+
*/
|
|
51802
|
+
durability: "session",
|
|
51803
|
+
history: "none"
|
|
51804
|
+
};
|
|
51507
51805
|
`${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
|
|
51508
51806
|
/**
|
|
51509
51807
|
* What a composition may BUILD on a device type (G2, D676): the caps a new
|
|
@@ -54038,8 +54336,14 @@ method(object({
|
|
|
54038
54336
|
"string",
|
|
54039
54337
|
"number",
|
|
54040
54338
|
"boolean",
|
|
54041
|
-
"enum"
|
|
54339
|
+
"enum",
|
|
54340
|
+
"union"
|
|
54042
54341
|
]),
|
|
54342
|
+
unionMembers: array(_enum([
|
|
54343
|
+
"string",
|
|
54344
|
+
"number",
|
|
54345
|
+
"boolean"
|
|
54346
|
+
])).optional(),
|
|
54043
54347
|
enumValues: array(string()).optional(),
|
|
54044
54348
|
item: boolean().optional(),
|
|
54045
54349
|
quantity: ReadingQuantitySchema.optional()
|
|
@@ -56667,8 +56971,85 @@ method(_void(), array(FailureContributionSchema).readonly());
|
|
|
56667
56971
|
* - `FanPreset` — preset selector
|
|
56668
56972
|
* - `FanDirection` — direction toggle (ceiling fans)
|
|
56669
56973
|
* - `FanOscillating` — oscillation toggle
|
|
56974
|
+
*
|
|
56975
|
+
* Per-axis oscillation (`oscillationAxes`, `setOscillationAxis`, D1034): an air
|
|
56976
|
+
* circulator sweeps horizontally and vertically, each axis with its own
|
|
56977
|
+
* switch, fixed angle, sweep bounds and limits. A provider may report them
|
|
56978
|
+
* natively; otherwise the OPERATOR links sibling devices to each field in
|
|
56979
|
+
* Customize (a `replace` over the fields listed in `overridable` — nothing is
|
|
56980
|
+
* linked or inferred by itself), and a write to a linked field is forwarded to
|
|
56981
|
+
* its source. `oscillating` stays the single summary flag: when axes exist it
|
|
56982
|
+
* means "any axis oscillates" (`anyAxisOscillating`).
|
|
56670
56983
|
*/
|
|
56671
56984
|
var FanDirectionSchema = _enum(["forward", "reverse"]);
|
|
56985
|
+
var FAN_OSCILLATION_AXES = ["horizontal", "vertical"];
|
|
56986
|
+
var FanOscillationAxisNameSchema = _enum(FAN_OSCILLATION_AXES);
|
|
56987
|
+
var FanOscillationAxisFieldSchema = _enum([
|
|
56988
|
+
"oscillating",
|
|
56989
|
+
"angle",
|
|
56990
|
+
"sweepFrom",
|
|
56991
|
+
"sweepTo"
|
|
56992
|
+
]);
|
|
56993
|
+
/** The limits of one axis, read from the source that serves its angle. */
|
|
56994
|
+
var FAN_OSCILLATION_AXIS_LIMITS = [
|
|
56995
|
+
"min",
|
|
56996
|
+
"max",
|
|
56997
|
+
"step"
|
|
56998
|
+
];
|
|
56999
|
+
/**
|
|
57000
|
+
* One oscillation axis. Every reading the device has not reported is `null`,
|
|
57001
|
+
* never `0` (D393): `0°` is a real position. Each leaf defaults to `null`, so
|
|
57002
|
+
* a slice that names only the leaves it knows (a Customize link of one field)
|
|
57003
|
+
* is still a whole axis.
|
|
57004
|
+
*/
|
|
57005
|
+
var FanOscillationAxisSchema = object({
|
|
57006
|
+
/** Whether the axis sweeps. */
|
|
57007
|
+
oscillating: boolean().nullable().default(null),
|
|
57008
|
+
/** The fixed position (degrees) the axis holds when it does not sweep. */
|
|
57009
|
+
angle: number$1().nullable().default(null),
|
|
57010
|
+
/** The sweep bounds (degrees): left → right, bottom → top. */
|
|
57011
|
+
sweepFrom: number$1().nullable().default(null),
|
|
57012
|
+
sweepTo: number$1().nullable().default(null),
|
|
57013
|
+
/** The angles the axis accepts. Null when the device states none. */
|
|
57014
|
+
min: number$1().nullable().default(null),
|
|
57015
|
+
max: number$1().nullable().default(null),
|
|
57016
|
+
step: number$1().nullable().default(null)
|
|
57017
|
+
});
|
|
57018
|
+
/** Both axes; an axis the device does not have is `null`. */
|
|
57019
|
+
var FanOscillationAxesSchema = object({
|
|
57020
|
+
horizontal: FanOscillationAxisSchema.nullable().default(null),
|
|
57021
|
+
vertical: FanOscillationAxisSchema.nullable().default(null)
|
|
57022
|
+
});
|
|
57023
|
+
var FanOscillationAxisRefusalSchema = _enum([
|
|
57024
|
+
"nothing-to-set",
|
|
57025
|
+
"axis-not-linked",
|
|
57026
|
+
"out-of-range",
|
|
57027
|
+
"source-gone",
|
|
57028
|
+
"source-unknown",
|
|
57029
|
+
"source-call-failed"
|
|
57030
|
+
]);
|
|
57031
|
+
var FanOscillationAxisResultSchema = discriminatedUnion("status", [object({ status: literal("applied") }), object({
|
|
57032
|
+
status: literal("refused"),
|
|
57033
|
+
reason: FanOscillationAxisRefusalSchema,
|
|
57034
|
+
/** The field at fault, when one is. */
|
|
57035
|
+
field: FanOscillationAxisFieldSchema.optional(),
|
|
57036
|
+
/** One line for the operator, naming the axis, the field and the source. */
|
|
57037
|
+
detail: string(),
|
|
57038
|
+
/** Fields that DID land before the refusal (writes run in order, stop at the first failure). */
|
|
57039
|
+
applied: array(FanOscillationAxisFieldSchema)
|
|
57040
|
+
})]);
|
|
57041
|
+
var SetOscillationAxisInputSchema = object({
|
|
57042
|
+
deviceId: number$1().int().nonnegative(),
|
|
57043
|
+
axis: FanOscillationAxisNameSchema,
|
|
57044
|
+
oscillating: boolean().optional(),
|
|
57045
|
+
angle: number$1().optional(),
|
|
57046
|
+
sweepFrom: number$1().optional(),
|
|
57047
|
+
sweepTo: number$1().optional()
|
|
57048
|
+
});
|
|
57049
|
+
/** The state path of one axis field: `oscillationAxes.<axis>.<field>`. */
|
|
57050
|
+
function fanAxisPath(axis, field) {
|
|
57051
|
+
return `oscillationAxes.${axis}.${field}`;
|
|
57052
|
+
}
|
|
56672
57053
|
var FanControlStatusSchema = object({
|
|
56673
57054
|
/** Active speed as 0..100 inclusive. Null when the device has no
|
|
56674
57055
|
* speed surface (single-speed fan). */
|
|
@@ -56687,9 +57068,102 @@ var FanControlStatusSchema = object({
|
|
|
56687
57068
|
/** Oscillation toggle. Null when the device has no oscillation
|
|
56688
57069
|
* surface. */
|
|
56689
57070
|
oscillating: boolean().nullable(),
|
|
57071
|
+
/**
|
|
57072
|
+
* Per-axis oscillation (air circulators). Absent when the device has no
|
|
57073
|
+
* per-axis surface and nothing is linked in Customize.
|
|
57074
|
+
*/
|
|
57075
|
+
oscillationAxes: FanOscillationAxesSchema.optional(),
|
|
56690
57076
|
/** Ms epoch when the slice was last updated. */
|
|
56691
57077
|
lastChangedAt: number$1()
|
|
56692
57078
|
});
|
|
57079
|
+
var FAN_CUSTOMIZE_DEVICE_TYPES = [DeviceType.Fan];
|
|
57080
|
+
/** The Customize section the axis targets are listed under. */
|
|
57081
|
+
var AXES_SECTION = "oscillation-axes";
|
|
57082
|
+
function axisOscillatingTarget(axis) {
|
|
57083
|
+
return {
|
|
57084
|
+
id: `${axis}-oscillating`,
|
|
57085
|
+
cardinality: "scalar",
|
|
57086
|
+
vocabulary: {
|
|
57087
|
+
cap: "fan-control",
|
|
57088
|
+
stateField: "oscillating"
|
|
57089
|
+
},
|
|
57090
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57091
|
+
apply: "replace",
|
|
57092
|
+
section: AXES_SECTION,
|
|
57093
|
+
path: fanAxisPath(axis, "oscillating"),
|
|
57094
|
+
sources: [{
|
|
57095
|
+
kind: "switch",
|
|
57096
|
+
transforms: [{ kind: "identity" }]
|
|
57097
|
+
}],
|
|
57098
|
+
sourceReport: { status: "assignment-status" }
|
|
57099
|
+
};
|
|
57100
|
+
}
|
|
57101
|
+
/**
|
|
57102
|
+
* A numeric axis field. The `angle` link also carries the axis limits, read
|
|
57103
|
+
* from the SAME linked control's own range (`min`/`max`/`step`), so the limits
|
|
57104
|
+
* are the source's, never guessed.
|
|
57105
|
+
*/
|
|
57106
|
+
function axisNumericTarget(axis, field) {
|
|
57107
|
+
const base = {
|
|
57108
|
+
id: `${axis}-${field}`,
|
|
57109
|
+
cardinality: "scalar",
|
|
57110
|
+
vocabulary: { cap: "fan-control" },
|
|
57111
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57112
|
+
apply: "replace",
|
|
57113
|
+
section: AXES_SECTION,
|
|
57114
|
+
path: fanAxisPath(axis, field),
|
|
57115
|
+
sources: [{
|
|
57116
|
+
kind: "numeric",
|
|
57117
|
+
transforms: [{ kind: "identity" }]
|
|
57118
|
+
}],
|
|
57119
|
+
sourceReport: { status: "assignment-status" }
|
|
57120
|
+
};
|
|
57121
|
+
if (field !== "angle") return base;
|
|
57122
|
+
return {
|
|
57123
|
+
...base,
|
|
57124
|
+
companions: FAN_OSCILLATION_AXIS_LIMITS.map((limit) => ({
|
|
57125
|
+
kind: "source-field",
|
|
57126
|
+
path: fanAxisPath(axis, limit),
|
|
57127
|
+
field: limit
|
|
57128
|
+
}))
|
|
57129
|
+
};
|
|
57130
|
+
}
|
|
57131
|
+
var AXIS_TARGETS = FAN_OSCILLATION_AXES.flatMap((axis) => [
|
|
57132
|
+
axisOscillatingTarget(axis),
|
|
57133
|
+
axisNumericTarget(axis, "angle"),
|
|
57134
|
+
axisNumericTarget(axis, "sweepFrom"),
|
|
57135
|
+
axisNumericTarget(axis, "sweepTo")
|
|
57136
|
+
]);
|
|
57137
|
+
/** A select (an enum control) the operator links to the fan's `preset`. */
|
|
57138
|
+
var PRESET_TARGET = {
|
|
57139
|
+
id: "preset",
|
|
57140
|
+
cardinality: "scalar",
|
|
57141
|
+
vocabulary: { cap: "fan-control" },
|
|
57142
|
+
deviceTypes: FAN_CUSTOMIZE_DEVICE_TYPES,
|
|
57143
|
+
apply: "replace",
|
|
57144
|
+
path: "preset",
|
|
57145
|
+
sources: [{
|
|
57146
|
+
kind: "field",
|
|
57147
|
+
valueKinds: ["string"],
|
|
57148
|
+
transforms: [{ kind: "identity" }]
|
|
57149
|
+
}],
|
|
57150
|
+
sourceReport: { status: "assignment-status" }
|
|
57151
|
+
};
|
|
57152
|
+
var FAN_CONTROL_TARGETS = [...AXIS_TARGETS, PRESET_TARGET];
|
|
57153
|
+
/**
|
|
57154
|
+
* Customize (D1034): the fan already serves `fan-control` natively, so every
|
|
57155
|
+
* target is a `replace` over a field listed in `overridable` — the axis leaves
|
|
57156
|
+
* and `preset`, nothing else. Nothing is pre-linked: the operator picks each
|
|
57157
|
+
* source (D356).
|
|
57158
|
+
*/
|
|
57159
|
+
var fanControlCustomize = {
|
|
57160
|
+
version: 1,
|
|
57161
|
+
overridable: FAN_CONTROL_TARGETS.map((target) => ({
|
|
57162
|
+
path: target.path,
|
|
57163
|
+
by: target.id
|
|
57164
|
+
})),
|
|
57165
|
+
targets: FAN_CONTROL_TARGETS
|
|
57166
|
+
};
|
|
56693
57167
|
var fanControlCapability = {
|
|
56694
57168
|
name: "fan-control",
|
|
56695
57169
|
scope: "device",
|
|
@@ -56724,6 +57198,18 @@ var fanControlCapability = {
|
|
|
56724
57198
|
}), _void(), {
|
|
56725
57199
|
kind: "mutation",
|
|
56726
57200
|
auth: "protected"
|
|
57201
|
+
}),
|
|
57202
|
+
/**
|
|
57203
|
+
* Sets one oscillation axis: its switch, fixed angle and/or sweep bounds.
|
|
57204
|
+
* Fields left out are untouched. A field linked in Customize is forwarded
|
|
57205
|
+
* to its linked source (D1034); otherwise the native provider serves it;
|
|
57206
|
+
* neither → `axis-not-linked`. Ranges are checked against the axis limits
|
|
57207
|
+
* before anything is written; writes run in order and stop at the first
|
|
57208
|
+
* failure, which the result names.
|
|
57209
|
+
*/
|
|
57210
|
+
setOscillationAxis: optionalMethod(SetOscillationAxisInputSchema, FanOscillationAxisResultSchema, {
|
|
57211
|
+
kind: "mutation",
|
|
57212
|
+
auth: "protected"
|
|
56727
57213
|
})
|
|
56728
57214
|
},
|
|
56729
57215
|
status: {
|
|
@@ -56735,6 +57221,7 @@ var fanControlCapability = {
|
|
|
56735
57221
|
* sliders read `speed` for live updates.
|
|
56736
57222
|
*/
|
|
56737
57223
|
runtimeState: FanControlStatusSchema,
|
|
57224
|
+
customize: fanControlCustomize,
|
|
56738
57225
|
/**
|
|
56739
57226
|
* Runtime-state durability: **session** — as `brightness`.
|
|
56740
57227
|
*
|
|
@@ -56754,16 +57241,33 @@ var fanControlCapability = {
|
|
|
56754
57241
|
oscillating: { kind: "boolean" }
|
|
56755
57242
|
} }
|
|
56756
57243
|
};
|
|
57244
|
+
/** One axis as a preset wants it; an absent field is left as it is. */
|
|
57245
|
+
var FanPresetAxisSchema = object({
|
|
57246
|
+
oscillating: boolean().optional(),
|
|
57247
|
+
angle: number$1().optional(),
|
|
57248
|
+
sweepFrom: number$1().optional(),
|
|
57249
|
+
sweepTo: number$1().optional()
|
|
57250
|
+
});
|
|
57251
|
+
/** The fan-control state a preset sets; an absent field is left as it is. */
|
|
57252
|
+
var FanPresetStateSchema = object({
|
|
57253
|
+
speed: number$1().min(0).max(100).optional(),
|
|
57254
|
+
/** The fan's own preset mode (or the linked select's option). */
|
|
57255
|
+
preset: string().min(1).optional(),
|
|
57256
|
+
direction: FanDirectionSchema.optional(),
|
|
57257
|
+
oscillationAxes: object({
|
|
57258
|
+
horizontal: FanPresetAxisSchema.optional(),
|
|
57259
|
+
vertical: FanPresetAxisSchema.optional()
|
|
57260
|
+
}).optional()
|
|
57261
|
+
});
|
|
56757
57262
|
var FanPresetSchema = object({
|
|
56758
57263
|
/** Stable key; never reused after a delete within one device's list. */
|
|
56759
57264
|
id: string().min(1).max(64),
|
|
56760
57265
|
/** What the operator and a voice assistant call it. */
|
|
56761
57266
|
name: string().trim().min(1).max(40),
|
|
56762
|
-
|
|
57267
|
+
state: FanPresetStateSchema
|
|
56763
57268
|
});
|
|
56764
57269
|
var FanPresetsRefusalSchema = _enum([
|
|
56765
|
-
"
|
|
56766
|
-
"bad-target",
|
|
57270
|
+
"empty-state",
|
|
56767
57271
|
"duplicate-name",
|
|
56768
57272
|
"duplicate-id",
|
|
56769
57273
|
"too-many",
|
|
@@ -56775,72 +57279,55 @@ var FanPresetsSetResultSchema = discriminatedUnion("status", [object({
|
|
|
56775
57279
|
}), object({
|
|
56776
57280
|
status: literal("refused"),
|
|
56777
57281
|
reason: FanPresetsRefusalSchema,
|
|
56778
|
-
/** Names the preset
|
|
57282
|
+
/** Names the preset at fault. */
|
|
56779
57283
|
detail: string()
|
|
56780
57284
|
})]);
|
|
56781
|
-
var
|
|
56782
|
-
|
|
57285
|
+
var FanPresetFieldResultSchema = object({
|
|
57286
|
+
/** The state path: `speed`, `preset`, `direction`, `oscillationAxes.horizontal.angle`. */
|
|
57287
|
+
field: string(),
|
|
56783
57288
|
outcome: _enum([
|
|
56784
57289
|
"applied",
|
|
56785
57290
|
"failed",
|
|
56786
57291
|
"not-run",
|
|
56787
57292
|
"refused"
|
|
56788
57293
|
]),
|
|
56789
|
-
deviceId: number$1().int().nonnegative().optional(),
|
|
56790
|
-
cap: string().optional(),
|
|
56791
|
-
method: string().optional(),
|
|
56792
57294
|
/** The cause, for every outcome but `applied`. */
|
|
56793
57295
|
message: string().optional()
|
|
56794
57296
|
});
|
|
56795
57297
|
var FanPresetApplyResultSchema = object({
|
|
56796
|
-
/** `applied`: every
|
|
57298
|
+
/** `applied`: every field landed. `partial`: some did, one did not. `refused`: nothing ran. */
|
|
56797
57299
|
status: _enum([
|
|
56798
57300
|
"applied",
|
|
56799
57301
|
"partial",
|
|
56800
57302
|
"refused"
|
|
56801
57303
|
]),
|
|
56802
|
-
|
|
57304
|
+
fields: array(FanPresetFieldResultSchema)
|
|
56803
57305
|
});
|
|
56804
57306
|
var FanCaptureFieldSchema = _enum([
|
|
56805
57307
|
"speed",
|
|
56806
|
-
"oscillating",
|
|
56807
57308
|
"direction",
|
|
56808
|
-
"preset"
|
|
57309
|
+
"preset",
|
|
57310
|
+
"oscillationAxes"
|
|
56809
57311
|
]);
|
|
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
57312
|
var FanPresetCaptureResultSchema = object({
|
|
56826
|
-
/** The
|
|
56827
|
-
|
|
56828
|
-
|
|
56829
|
-
|
|
56830
|
-
|
|
57313
|
+
/** The fan's CURRENT fan-control state, as a preset; NOT saved. */
|
|
57314
|
+
state: FanPresetStateSchema,
|
|
57315
|
+
omitted: array(object({
|
|
57316
|
+
/** A state path (`speed`, `oscillationAxes.vertical.angle`). */
|
|
57317
|
+
what: string(),
|
|
57318
|
+
/** Why no value was taken: an unknown reading is never stored as 0 (D393). */
|
|
57319
|
+
reason: string()
|
|
57320
|
+
}))
|
|
56831
57321
|
});
|
|
56832
|
-
var deviceInput$
|
|
57322
|
+
var deviceInput$1 = object({ deviceId: number$1().int().nonnegative() });
|
|
56833
57323
|
DeviceType.Fan;
|
|
56834
|
-
method(deviceInput$
|
|
57324
|
+
method(deviceInput$1, array(FanPresetSchema), { auth: "protected" }), method(deviceInput$1.extend({ presets: array(FanPresetSchema).max(12) }), FanPresetsSetResultSchema, {
|
|
56835
57325
|
kind: "mutation",
|
|
56836
57326
|
auth: "admin"
|
|
56837
|
-
}), method(deviceInput$
|
|
57327
|
+
}), method(deviceInput$1.extend({ presetId: string().min(1) }), FanPresetApplyResultSchema, {
|
|
56838
57328
|
kind: "mutation",
|
|
56839
57329
|
auth: "protected"
|
|
56840
|
-
}), method(deviceInput$
|
|
56841
|
-
fields: array(FanCaptureFieldSchema).optional(),
|
|
56842
|
-
steps: array(ActionStepSchema).max(20).default([])
|
|
56843
|
-
}), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
57330
|
+
}), method(deviceInput$1.extend({ fields: array(FanCaptureFieldSchema).optional() }), FanPresetCaptureResultSchema, { auth: "protected" });
|
|
56844
57331
|
/**
|
|
56845
57332
|
* Per-device feature/identity probe slice. Holds the runtime-resolved
|
|
56846
57333
|
* truth about what a device CAN do — which the kernel uses to:
|
|
@@ -57052,8 +57539,7 @@ var groupActiveCapability = {
|
|
|
57052
57539
|
DeviceType.Valve,
|
|
57053
57540
|
DeviceType.Lock,
|
|
57054
57541
|
DeviceType.MediaPlayer,
|
|
57055
|
-
DeviceType.Climate
|
|
57056
|
-
DeviceType.Thermostat
|
|
57542
|
+
DeviceType.Climate
|
|
57057
57543
|
],
|
|
57058
57544
|
methods: {},
|
|
57059
57545
|
status: {
|
|
@@ -57095,7 +57581,6 @@ var groupCommandsCapability = {
|
|
|
57095
57581
|
DeviceType.Valve,
|
|
57096
57582
|
DeviceType.Lock,
|
|
57097
57583
|
DeviceType.MediaPlayer,
|
|
57098
|
-
DeviceType.Thermostat,
|
|
57099
57584
|
DeviceType.Climate
|
|
57100
57585
|
],
|
|
57101
57586
|
methods: {},
|
|
@@ -57151,7 +57636,6 @@ var groupStatusCapability = {
|
|
|
57151
57636
|
DeviceType.Valve,
|
|
57152
57637
|
DeviceType.Lock,
|
|
57153
57638
|
DeviceType.MediaPlayer,
|
|
57154
|
-
DeviceType.Thermostat,
|
|
57155
57639
|
DeviceType.Climate
|
|
57156
57640
|
],
|
|
57157
57641
|
methods: {},
|
|
@@ -57507,7 +57991,7 @@ var heatingValveCapability = {
|
|
|
57507
57991
|
scope: "device",
|
|
57508
57992
|
deviceNative: true,
|
|
57509
57993
|
mode: "singleton",
|
|
57510
|
-
deviceTypes: [DeviceType.
|
|
57994
|
+
deviceTypes: [DeviceType.Climate],
|
|
57511
57995
|
methods: {},
|
|
57512
57996
|
status: {
|
|
57513
57997
|
schema: HeatingValveStatusSchema,
|
|
@@ -57537,13 +58021,18 @@ var heatingValveCapability = {
|
|
|
57537
58021
|
* retried in a loop - D49); a future one is re-armed.
|
|
57538
58022
|
*
|
|
57539
58023
|
* What a device may be run for, and for how long at most, is per device type
|
|
57540
|
-
* ({@link
|
|
58024
|
+
* and role ({@link timedRunLimitOf}): the maximum is a safety bound, not a preference,
|
|
57541
58025
|
* so a request above it is REFUSED with a typed reason, never clamped.
|
|
57542
58026
|
*/
|
|
57543
58027
|
var TimedRunUnitSchema = _enum(["s", "min"]);
|
|
57544
58028
|
_enum(["switch", "pet-fountain"]);
|
|
57545
|
-
|
|
57546
|
-
|
|
58029
|
+
/**
|
|
58030
|
+
* The device types `timed-run` may be offered on. A `switch` is narrowed by
|
|
58031
|
+
* role ({@link TIMED_RUN_DEVICE_ROLES}): only a plug-in heater runs on a timer.
|
|
58032
|
+
*/
|
|
58033
|
+
var TIMED_RUN_DEVICE_TYPES = [DeviceType.Switch, DeviceType.PetFountain];
|
|
58034
|
+
/** The roles that make a device of these types timed-run-able (D1033). */
|
|
58035
|
+
var TIMED_RUN_DEVICE_ROLES = { [DeviceType.Switch]: [DeviceRole.SwitchHeater] };
|
|
57547
58036
|
/** One duration, as the one-tap run asks for it. */
|
|
57548
58037
|
var TimedRunQuickRunSchema = object({
|
|
57549
58038
|
duration: number$1().positive().finite(),
|
|
@@ -57593,6 +58082,7 @@ var timedRunCapability = {
|
|
|
57593
58082
|
kind: "wrapper",
|
|
57594
58083
|
defaultActive: true,
|
|
57595
58084
|
deviceTypes: TIMED_RUN_DEVICE_TYPES,
|
|
58085
|
+
deviceRoles: TIMED_RUN_DEVICE_ROLES,
|
|
57596
58086
|
deviceConfig: { ui: {
|
|
57597
58087
|
kind: "derived-form",
|
|
57598
58088
|
builderId: "timed-run",
|
|
@@ -57753,8 +58243,8 @@ var HeroSceneSchema = object({
|
|
|
57753
58243
|
/** 0..1: volume, cover position, cycle progress. */
|
|
57754
58244
|
level: number$1().min(0).max(1).nullable()
|
|
57755
58245
|
});
|
|
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;
|
|
58246
|
+
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;
|
|
58247
|
+
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
58248
|
/** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
|
|
57759
58249
|
var HeroTextSchema = object({
|
|
57760
58250
|
en: string(),
|
|
@@ -58139,8 +58629,8 @@ var HeroRowModelSchema = object({
|
|
|
58139
58629
|
extras: array(HeroControlSchema).max(2).optional(),
|
|
58140
58630
|
trailing: HeroFactSchema.nullable()
|
|
58141
58631
|
});
|
|
58142
|
-
DeviceType.Climate
|
|
58143
|
-
DeviceType.Climate, DeviceType.
|
|
58632
|
+
DeviceType.Climate;
|
|
58633
|
+
DeviceType.Climate, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer;
|
|
58144
58634
|
/**
|
|
58145
58635
|
* hero-layout — what a device's hero says (D1023). Read-only: the FIXED
|
|
58146
58636
|
* layouts live in `@camstack/types` and nothing here stores or edits one.
|
|
@@ -58172,7 +58662,10 @@ method(heroInput, HeroModelSchema.nullable(), {
|
|
|
58172
58662
|
}), method(rowModelsInput, array(HeroRowModelSchema.nullable()), {
|
|
58173
58663
|
auth: "protected",
|
|
58174
58664
|
role: "viewer"
|
|
58175
|
-
}), method(object({
|
|
58665
|
+
}), method(object({
|
|
58666
|
+
deviceType: string(),
|
|
58667
|
+
role: string().nullable().optional()
|
|
58668
|
+
}), HeroLayoutRowsSchema, {
|
|
58176
58669
|
auth: "protected",
|
|
58177
58670
|
role: "viewer"
|
|
58178
58671
|
});
|
|
@@ -58219,6 +58712,11 @@ var humidifierCapability = {
|
|
|
58219
58712
|
deviceNative: true,
|
|
58220
58713
|
mode: "singleton",
|
|
58221
58714
|
deviceTypes: [DeviceType.Humidifier],
|
|
58715
|
+
power: {
|
|
58716
|
+
kind: "field",
|
|
58717
|
+
field: "on",
|
|
58718
|
+
set: "setOn"
|
|
58719
|
+
},
|
|
58222
58720
|
methods: {
|
|
58223
58721
|
setOn: method(object({
|
|
58224
58722
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -58292,7 +58790,7 @@ var humiditySensorCapability = {
|
|
|
58292
58790
|
scope: "device",
|
|
58293
58791
|
deviceNative: true,
|
|
58294
58792
|
mode: "singleton",
|
|
58295
|
-
deviceTypes: [DeviceType.Sensor, DeviceType.
|
|
58793
|
+
deviceTypes: [DeviceType.Sensor, DeviceType.Climate],
|
|
58296
58794
|
methods: {},
|
|
58297
58795
|
status: {
|
|
58298
58796
|
schema: HumiditySensorStatusSchema,
|
|
@@ -71964,6 +72462,11 @@ var petFountainCapability = {
|
|
|
71964
72462
|
deviceNative: true,
|
|
71965
72463
|
mode: "singleton",
|
|
71966
72464
|
deviceTypes: [DeviceType.PetFountain],
|
|
72465
|
+
power: {
|
|
72466
|
+
kind: "field",
|
|
72467
|
+
field: "pumpRunning",
|
|
72468
|
+
set: "setPump"
|
|
72469
|
+
},
|
|
71967
72470
|
methods: {
|
|
71968
72471
|
/** Start or stop the pump. */
|
|
71969
72472
|
setPump: method(object({
|
|
@@ -73539,7 +74042,6 @@ var POWER_METER_DEVICE_TYPES = [
|
|
|
73539
74042
|
DeviceType.Light,
|
|
73540
74043
|
DeviceType.Fan,
|
|
73541
74044
|
DeviceType.Climate,
|
|
73542
|
-
DeviceType.Thermostat,
|
|
73543
74045
|
DeviceType.Humidifier,
|
|
73544
74046
|
DeviceType.WaterHeater,
|
|
73545
74047
|
DeviceType.MediaPlayer,
|
|
@@ -73548,8 +74050,7 @@ var POWER_METER_DEVICE_TYPES = [
|
|
|
73548
74050
|
DeviceType.Dishwasher,
|
|
73549
74051
|
DeviceType.Vacuum,
|
|
73550
74052
|
DeviceType.LawnMower,
|
|
73551
|
-
DeviceType.PetFeeder
|
|
73552
|
-
DeviceType.Heater
|
|
74053
|
+
DeviceType.PetFeeder
|
|
73553
74054
|
];
|
|
73554
74055
|
/** Everything but a bare `Sensor`: a sensor that takes its reading from another sensor adds nothing. */
|
|
73555
74056
|
var POWER_METER_CUSTOMIZE_DEVICE_TYPES = POWER_METER_DEVICE_TYPES.filter((type) => type !== DeviceType.Sensor);
|
|
@@ -73662,117 +74163,6 @@ var powerMeterCapability = {
|
|
|
73662
74163
|
volatileStateFields: ["lastFetchedAt"],
|
|
73663
74164
|
customize: powerMeterCustomize
|
|
73664
74165
|
};
|
|
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
74166
|
/**
|
|
73777
74167
|
* Presence cap. Models HA `person.*` and `device_tracker.*` entities
|
|
73778
74168
|
* on `DeviceType.Presence`. Read-only — no setters: presence is
|
|
@@ -77247,9 +77637,7 @@ var RUNNING_CUSTOMIZE_DEVICE_TYPES = [
|
|
|
77247
77637
|
DeviceType.Humidifier,
|
|
77248
77638
|
DeviceType.Fan,
|
|
77249
77639
|
DeviceType.Climate,
|
|
77250
|
-
DeviceType.
|
|
77251
|
-
DeviceType.PetFeeder,
|
|
77252
|
-
DeviceType.Heater
|
|
77640
|
+
DeviceType.PetFeeder
|
|
77253
77641
|
];
|
|
77254
77642
|
var runningStateCapability = {
|
|
77255
77643
|
name: "running-state",
|
|
@@ -79337,8 +79725,7 @@ var switchCapability = {
|
|
|
79337
79725
|
DeviceType.Switch,
|
|
79338
79726
|
DeviceType.Siren,
|
|
79339
79727
|
DeviceType.Light,
|
|
79340
|
-
DeviceType.Fan
|
|
79341
|
-
DeviceType.Heater
|
|
79728
|
+
DeviceType.Fan
|
|
79342
79729
|
],
|
|
79343
79730
|
methods: { setState: method(object({
|
|
79344
79731
|
deviceId: number$1(),
|
|
@@ -80032,7 +80419,7 @@ var temperatureSensorCapability = {
|
|
|
80032
80419
|
scope: "device",
|
|
80033
80420
|
deviceNative: true,
|
|
80034
80421
|
mode: "singleton",
|
|
80035
|
-
deviceTypes: [DeviceType.Sensor, DeviceType.
|
|
80422
|
+
deviceTypes: [DeviceType.Sensor, DeviceType.Climate],
|
|
80036
80423
|
methods: {},
|
|
80037
80424
|
status: {
|
|
80038
80425
|
schema: TemperatureSensorStatusSchema,
|
|
@@ -81679,6 +82066,13 @@ var waterHeaterCapability = {
|
|
|
81679
82066
|
deviceNative: true,
|
|
81680
82067
|
mode: "singleton",
|
|
81681
82068
|
deviceTypes: [DeviceType.WaterHeater],
|
|
82069
|
+
power: {
|
|
82070
|
+
kind: "mode",
|
|
82071
|
+
field: "operationMode",
|
|
82072
|
+
offValue: "off",
|
|
82073
|
+
set: "setOperationMode",
|
|
82074
|
+
lastField: "lastActiveMode"
|
|
82075
|
+
},
|
|
81682
82076
|
methods: {
|
|
81683
82077
|
setTargetTemp: method(object({
|
|
81684
82078
|
deviceId: number$1().int().nonnegative(),
|
|
@@ -84370,15 +84764,16 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84370
84764
|
});
|
|
84371
84765
|
const meta = await this.ctx.api.deviceManager.loadMeta.query({ deviceId: id });
|
|
84372
84766
|
if (meta === null) throw new Error(`${this.providerName}: no persisted meta for "${stableId}" (id ${id}) — cannot reload`);
|
|
84373
|
-
const
|
|
84374
|
-
|
|
84767
|
+
const Class = this.deviceClassForRow({
|
|
84768
|
+
type: meta.type,
|
|
84769
|
+
stableId
|
|
84770
|
+
});
|
|
84375
84771
|
if (!Class) throw new Error(`${this.providerName}: no device class for type "${meta.type}" — "${stableId}" is an accessory child; reload its parent instead`);
|
|
84376
84772
|
await devices.create(stableId, Class, {}, meta.parentDeviceId ?? null);
|
|
84377
84773
|
const rows = await this.ctx.api.deviceManager.listPersistedByAddon.query({ addonId: this.addonId });
|
|
84378
84774
|
for (const row of rows) {
|
|
84379
84775
|
if (row.parentDeviceId !== id) continue;
|
|
84380
|
-
const
|
|
84381
|
-
const ChildClass = childType !== void 0 ? this.deviceClasses[childType] : void 0;
|
|
84776
|
+
const ChildClass = this.deviceClassForRow(row);
|
|
84382
84777
|
if (!ChildClass) continue;
|
|
84383
84778
|
try {
|
|
84384
84779
|
await devices.create(row.stableId, ChildClass, {}, id);
|
|
@@ -84405,6 +84800,16 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84405
84800
|
return { deviceId: id };
|
|
84406
84801
|
}
|
|
84407
84802
|
/**
|
|
84803
|
+
* The class a PERSISTED row is rebuilt with. Default: by type, from
|
|
84804
|
+
* `deviceClasses`. A provider whose distinct devices share one type (D1033:
|
|
84805
|
+
* a radiator valve and a room climate are both `climate`) overrides this
|
|
84806
|
+
* and tells them apart by the row's stable id or config.
|
|
84807
|
+
*/
|
|
84808
|
+
deviceClassForRow(row) {
|
|
84809
|
+
const type = Object.values(DeviceType).find((t) => t === row.type);
|
|
84810
|
+
return type !== void 0 ? this.deviceClasses[type] : void 0;
|
|
84811
|
+
}
|
|
84812
|
+
/**
|
|
84408
84813
|
* Restore devices from persisted state. Two-pass:
|
|
84409
84814
|
*
|
|
84410
84815
|
* 1. **Top-level pass** — invokes `kernel.devices.create()` for every
|
|
@@ -84465,7 +84870,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84465
84870
|
restored.add(saved.id);
|
|
84466
84871
|
return;
|
|
84467
84872
|
}
|
|
84468
|
-
const Class = this.
|
|
84873
|
+
const Class = this.deviceClassForRow(saved);
|
|
84469
84874
|
if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
|
|
84470
84875
|
if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
|
|
84471
84876
|
await this.healSavedConfig(saved, savedDevices);
|
|
@@ -84482,7 +84887,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84482
84887
|
};
|
|
84483
84888
|
const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
|
|
84484
84889
|
const restoreOne = async (saved) => {
|
|
84485
|
-
if (!this.
|
|
84890
|
+
if (!this.deviceClassForRow(saved)) {
|
|
84486
84891
|
this.ctx.logger.warn("No device class registered for restored type — skipping", {
|
|
84487
84892
|
tags: {
|
|
84488
84893
|
deviceId: saved.id,
|
|
@@ -84521,7 +84926,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84521
84926
|
const failedTopLevelIds = new Set(failures.map((failure) => failure.saved.id));
|
|
84522
84927
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
84523
84928
|
for (const saved of childRows) {
|
|
84524
|
-
if (!this.
|
|
84929
|
+
if (!this.deviceClassForRow(saved)) continue;
|
|
84525
84930
|
if (saved.parentDeviceId === null) continue;
|
|
84526
84931
|
if (restored.has(saved.parentDeviceId)) {
|
|
84527
84932
|
try {
|
|
@@ -84575,7 +84980,9 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
84575
84980
|
}
|
|
84576
84981
|
};
|
|
84577
84982
|
DeviceType.HeatPump;
|
|
84578
|
-
DeviceType.
|
|
84983
|
+
DeviceType.Climate;
|
|
84984
|
+
DeviceRole.ClimateThermostat, DeviceRole.ClimateAc, DeviceRole.ClimateRadiator, DeviceRole.ClimateFloor, DeviceRole.ClimateHeater, DeviceRole.ClimateHeatPumpCircuit;
|
|
84985
|
+
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
84986
|
new Set(Object.values(DeviceType));
|
|
84580
84987
|
/** Parses the raw slice with the cap's own schema; `undefined` when it does not parse. */
|
|
84581
84988
|
function contribution(cap, schema, build) {
|
|
@@ -86581,7 +86988,7 @@ var HEAT_PUMP_FAMILY_CONTRIBUTIONS = { "heat-pump-status": contribution("heat-pu
|
|
|
86581
86988
|
...Object.values(CLIMATE_FAMILY_CONTRIBUTIONS),
|
|
86582
86989
|
...Object.values(HEAT_PUMP_FAMILY_CONTRIBUTIONS)
|
|
86583
86990
|
];
|
|
86584
|
-
DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.
|
|
86991
|
+
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
86992
|
/**
|
|
86586
86993
|
* Who looks like what (D1023). One look per {@link DeviceType}, refined by a
|
|
86587
86994
|
* {@link DeviceRole} where the role changes how the device reads (a leak
|
|
@@ -87225,9 +87632,9 @@ lookOf({
|
|
|
87225
87632
|
idle: INFO,
|
|
87226
87633
|
standby: MUTED
|
|
87227
87634
|
});
|
|
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;
|
|
87635
|
+
DeviceType.Climate;
|
|
87636
|
+
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;
|
|
87637
|
+
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
87638
|
Object.freeze({
|
|
87232
87639
|
"accessories.getStatus": {
|
|
87233
87640
|
capName: "accessories",
|
|
@@ -90624,6 +91031,13 @@ Object.freeze({
|
|
|
90624
91031
|
access: "create",
|
|
90625
91032
|
role: "viewer"
|
|
90626
91033
|
},
|
|
91034
|
+
"fanControl.setOscillationAxis": {
|
|
91035
|
+
capName: "fan-control",
|
|
91036
|
+
capScope: "device",
|
|
91037
|
+
addonId: null,
|
|
91038
|
+
access: "create",
|
|
91039
|
+
role: "viewer"
|
|
91040
|
+
},
|
|
90627
91041
|
"fanControl.setPercentage": {
|
|
90628
91042
|
capName: "fan-control",
|
|
90629
91043
|
capScope: "device",
|
|
@@ -97645,7 +98059,7 @@ function projectAction(cap, action) {
|
|
|
97645
98059
|
};
|
|
97646
98060
|
}
|
|
97647
98061
|
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.
|
|
98062
|
+
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
98063
|
DeviceFeature.BatteryOperated;
|
|
97650
98064
|
function bound(cap, methods = [], authority = "reported") {
|
|
97651
98065
|
return {
|
|
@@ -97704,6 +98118,149 @@ function offered(received) {
|
|
|
97704
98118
|
};
|
|
97705
98119
|
}
|
|
97706
98120
|
offered(HA_AS), withheld(HOMEKIT_REASON), withheld(ALEXA_REASON), withheld(GOOGLE_REASON), withheld(MATTER_REASON);
|
|
98121
|
+
var PowerSwitchRefusedError = class extends Error {
|
|
98122
|
+
reason;
|
|
98123
|
+
constructor(reason, message) {
|
|
98124
|
+
super(message);
|
|
98125
|
+
this.name = "PowerSwitchRefusedError";
|
|
98126
|
+
this.reason = reason;
|
|
98127
|
+
}
|
|
98128
|
+
};
|
|
98129
|
+
/** `true` / `false` from the owner slice, `null` when the hinted field is
|
|
98130
|
+
* absent or not of the hinted shape. */
|
|
98131
|
+
function powerOnFromSlice(hint, slice) {
|
|
98132
|
+
const value = slice?.[hint.field];
|
|
98133
|
+
switch (hint.kind) {
|
|
98134
|
+
case "field": return typeof value === "boolean" ? value : null;
|
|
98135
|
+
case "methods": return typeof value === "string" ? !hint.offValues.includes(value) : null;
|
|
98136
|
+
case "mode": return typeof value === "string" ? value !== hint.offValue : null;
|
|
98137
|
+
}
|
|
98138
|
+
}
|
|
98139
|
+
function ownerMethod(owner, name, capName) {
|
|
98140
|
+
const fn = Reflect.get(owner, name);
|
|
98141
|
+
if (typeof fn !== "function") throw new PowerSwitchRefusedError("owner-method-missing", `${capName} provider does not serve '${name}', which its power hint names`);
|
|
98142
|
+
return async (input) => fn.call(owner, input);
|
|
98143
|
+
}
|
|
98144
|
+
async function lastNonOffMode(host, capName, hint) {
|
|
98145
|
+
const isMode = (v) => typeof v === "string" && v.length > 0 && v !== hint.offValue;
|
|
98146
|
+
const fromHub = (await host.readHubSlice(capName).catch(() => null))?.[hint.lastField];
|
|
98147
|
+
if (isMode(fromHub)) return fromHub;
|
|
98148
|
+
const local = host.runtimeState.getCapState(capName)?.[hint.lastField];
|
|
98149
|
+
return isMode(local) ? local : null;
|
|
98150
|
+
}
|
|
98151
|
+
async function applyPower(host, capName, hint, owner, on) {
|
|
98152
|
+
const deviceId = host.deviceId;
|
|
98153
|
+
switch (hint.kind) {
|
|
98154
|
+
case "field":
|
|
98155
|
+
await ownerMethod(owner, hint.set, capName)({
|
|
98156
|
+
deviceId,
|
|
98157
|
+
on
|
|
98158
|
+
});
|
|
98159
|
+
return;
|
|
98160
|
+
case "methods":
|
|
98161
|
+
await ownerMethod(owner, on ? hint.on : hint.off, capName)({ deviceId });
|
|
98162
|
+
return;
|
|
98163
|
+
case "mode": {
|
|
98164
|
+
const set = ownerMethod(owner, hint.set, capName);
|
|
98165
|
+
if (!on) {
|
|
98166
|
+
await set({
|
|
98167
|
+
deviceId,
|
|
98168
|
+
mode: hint.offValue
|
|
98169
|
+
});
|
|
98170
|
+
return;
|
|
98171
|
+
}
|
|
98172
|
+
const mode = await lastNonOffMode(host, capName, hint);
|
|
98173
|
+
if (mode === null) throw new PowerSwitchRefusedError("no-last-mode", `device ${deviceId}: ${capName} has no last non-off mode to restore; pick a mode instead`);
|
|
98174
|
+
await set({
|
|
98175
|
+
deviceId,
|
|
98176
|
+
mode
|
|
98177
|
+
});
|
|
98178
|
+
return;
|
|
98179
|
+
}
|
|
98180
|
+
}
|
|
98181
|
+
}
|
|
98182
|
+
function hintedMethods(hint) {
|
|
98183
|
+
return hint.kind === "methods" ? [hint.on, hint.off] : [hint.set];
|
|
98184
|
+
}
|
|
98185
|
+
/** Whether `owner` serves every method its cap's power hint names. A media
|
|
98186
|
+
* player whose `turnOn` / `turnOff` are not served has no power, so no switch. */
|
|
98187
|
+
function powerServedBy(hint, owner) {
|
|
98188
|
+
if (hint === void 0) return false;
|
|
98189
|
+
return hintedMethods(hint).every((name) => typeof Reflect.get(owner, name) === "function");
|
|
98190
|
+
}
|
|
98191
|
+
/** Builds the `switch` provider and starts the slice projection. Exported
|
|
98192
|
+
* for the specs; providers call `registerPowerSwitch`. */
|
|
98193
|
+
function buildPowerSwitch(host, cap, owner) {
|
|
98194
|
+
const hint = cap.power;
|
|
98195
|
+
if (hint === void 0) throw new Error(`capability '${cap.name}' has no power hint: it has no switch to project`);
|
|
98196
|
+
const read = () => powerOnFromSlice(hint, host.runtimeState.getCapState(cap.name));
|
|
98197
|
+
const project = () => {
|
|
98198
|
+
const on = read();
|
|
98199
|
+
const current = host.runtimeState.getCapState(switchCapability.name);
|
|
98200
|
+
if (current !== void 0 && current["on"] === on) return;
|
|
98201
|
+
const next = {
|
|
98202
|
+
on,
|
|
98203
|
+
lastChangedAt: Date.now()
|
|
98204
|
+
};
|
|
98205
|
+
host.runtimeState.setCapState(switchCapability.name, next);
|
|
98206
|
+
};
|
|
98207
|
+
const stop = host.runtimeState.subscribeCap(cap.name, project);
|
|
98208
|
+
if (host.runtimeState.getCapState(cap.name) !== void 0) project();
|
|
98209
|
+
return {
|
|
98210
|
+
provider: {
|
|
98211
|
+
getStatus: async () => {
|
|
98212
|
+
const current = host.runtimeState.getCapState(switchCapability.name);
|
|
98213
|
+
const lastChangedAt = typeof current?.["lastChangedAt"] === "number" ? current["lastChangedAt"] : 0;
|
|
98214
|
+
return {
|
|
98215
|
+
on: read(),
|
|
98216
|
+
lastChangedAt
|
|
98217
|
+
};
|
|
98218
|
+
},
|
|
98219
|
+
setState: async ({ on }) => applyPower(host, cap.name, hint, owner, on)
|
|
98220
|
+
},
|
|
98221
|
+
dispose: stop
|
|
98222
|
+
};
|
|
98223
|
+
}
|
|
98224
|
+
/**
|
|
98225
|
+
* Registers `switch` for a device whose `cap` carries a `power` hint, built
|
|
98226
|
+
* from `owner` (the provider the device registered for `cap`). Call it right
|
|
98227
|
+
* after `registerNativeCap(cap, owner)`.
|
|
98228
|
+
*/
|
|
98229
|
+
function registerPowerSwitch(device, cap, owner) {
|
|
98230
|
+
const deviceId = device.ctx.id;
|
|
98231
|
+
if (cap.power !== void 0 && !powerServedBy(cap.power, owner)) {
|
|
98232
|
+
device.ctx.logger.debug("power switch: owner serves no power control, no switch registered", {
|
|
98233
|
+
tags: { deviceId },
|
|
98234
|
+
meta: { capName: cap.name }
|
|
98235
|
+
});
|
|
98236
|
+
return () => {};
|
|
98237
|
+
}
|
|
98238
|
+
const host = {
|
|
98239
|
+
deviceId,
|
|
98240
|
+
runtimeState: device.runtimeState,
|
|
98241
|
+
registerNativeCap: (c, p) => device.ctx.registerNativeCap(c, p),
|
|
98242
|
+
readHubSlice: async (capName) => {
|
|
98243
|
+
try {
|
|
98244
|
+
return await device.ctx.api.deviceState.getCapSlice.query({
|
|
98245
|
+
deviceId,
|
|
98246
|
+
capName
|
|
98247
|
+
});
|
|
98248
|
+
} catch (err) {
|
|
98249
|
+
device.ctx.logger.warn("power switch: hub slice read failed", {
|
|
98250
|
+
tags: { deviceId },
|
|
98251
|
+
meta: {
|
|
98252
|
+
capName,
|
|
98253
|
+
error: err instanceof Error ? err.message : String(err)
|
|
98254
|
+
}
|
|
98255
|
+
});
|
|
98256
|
+
return null;
|
|
98257
|
+
}
|
|
98258
|
+
}
|
|
98259
|
+
};
|
|
98260
|
+
const built = buildPowerSwitch(host, cap, owner);
|
|
98261
|
+
host.registerNativeCap(switchCapability, built.provider);
|
|
98262
|
+
return built.dispose;
|
|
98263
|
+
}
|
|
97707
98264
|
Object.freeze({
|
|
97708
98265
|
"accessories.getStatus": [{
|
|
97709
98266
|
name: "deviceId",
|
|
@@ -98697,6 +99254,11 @@ Object.freeze({
|
|
|
98697
99254
|
form: "single",
|
|
98698
99255
|
optional: false
|
|
98699
99256
|
}],
|
|
99257
|
+
"fanControl.setOscillationAxis": [{
|
|
99258
|
+
name: "deviceId",
|
|
99259
|
+
form: "single",
|
|
99260
|
+
optional: false
|
|
99261
|
+
}],
|
|
98700
99262
|
"fanControl.setPercentage": [{
|
|
98701
99263
|
name: "deviceId",
|
|
98702
99264
|
form: "single",
|
|
@@ -102584,6 +103146,12 @@ Object.defineProperty(exports, "record", {
|
|
|
102584
103146
|
return record;
|
|
102585
103147
|
}
|
|
102586
103148
|
});
|
|
103149
|
+
Object.defineProperty(exports, "registerPowerSwitch", {
|
|
103150
|
+
enumerable: true,
|
|
103151
|
+
get: function() {
|
|
103152
|
+
return registerPowerSwitch;
|
|
103153
|
+
}
|
|
103154
|
+
});
|
|
102587
103155
|
Object.defineProperty(exports, "rowsDepsOver", {
|
|
102588
103156
|
enumerable: true,
|
|
102589
103157
|
get: function() {
|