@camstack/addon-provider-enpal 0.1.18 → 0.1.19
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/addon.js +580 -396
- package/dist/addon.mjs +580 -396
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -6057,7 +6057,7 @@ var ZodIssueCode = {
|
|
|
6057
6057
|
var ZodFirstPartyTypeKind;
|
|
6058
6058
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6059
6059
|
//#endregion
|
|
6060
|
-
//#region ../types/dist/sleep-
|
|
6060
|
+
//#region ../types/dist/sleep-DyIxjZZt.mjs
|
|
6061
6061
|
/**
|
|
6062
6062
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6063
6063
|
* window to float samples (D455).
|
|
@@ -15262,6 +15262,32 @@ var CAP_VOCABULARY_BY_NAME = {
|
|
|
15262
15262
|
...SYSTEM_CAP_VOCABULARY
|
|
15263
15263
|
};
|
|
15264
15264
|
/**
|
|
15265
|
+
* Vocabulary actions by id (D983). An action step names a vocabulary action
|
|
15266
|
+
* (`cover.open`), never a raw cap method; this is the one lookup that turns the
|
|
15267
|
+
* id into its cap, method and effect — and a persisted legacy `cap` + `method`
|
|
15268
|
+
* pair back into the action id when exactly one action matches.
|
|
15269
|
+
*/
|
|
15270
|
+
function buildIndex() {
|
|
15271
|
+
const byId = /* @__PURE__ */ new Map();
|
|
15272
|
+
for (const [cap, entry] of Object.entries(CAP_VOCABULARY_BY_NAME)) {
|
|
15273
|
+
if (entry.scope !== "device") continue;
|
|
15274
|
+
for (const action of entry.actions ?? []) byId.set(action.id, {
|
|
15275
|
+
cap,
|
|
15276
|
+
action
|
|
15277
|
+
});
|
|
15278
|
+
}
|
|
15279
|
+
return byId;
|
|
15280
|
+
}
|
|
15281
|
+
var index;
|
|
15282
|
+
function actionIndex() {
|
|
15283
|
+
index ??= buildIndex();
|
|
15284
|
+
return index;
|
|
15285
|
+
}
|
|
15286
|
+
/** The vocabulary action with this id, or `undefined`. */
|
|
15287
|
+
function findVocabAction(actionId) {
|
|
15288
|
+
return actionIndex().get(actionId);
|
|
15289
|
+
}
|
|
15290
|
+
/**
|
|
15265
15291
|
* `addon-pages-source` — collection cap exposing per-provider raw page
|
|
15266
15292
|
* declarations. Every addon that contributes a UI page registers a
|
|
15267
15293
|
* provider here. The hub-side singleton aggregator (`addon-pages` cap,
|
|
@@ -44947,21 +44973,23 @@ function convertLegacyWhen(half) {
|
|
|
44947
44973
|
* (instants) and state leaves (durations). It replaces the flat trigger list
|
|
44948
44974
|
* plus the separate condition tree; the firing rule is in that file and in
|
|
44949
44975
|
* ADR "one tree".
|
|
44950
|
-
* - **Actions** — the `wait` and `
|
|
44976
|
+
* - **Actions** — the `wait` and device `command` steps, plus the `{kind:'code'}`
|
|
44951
44977
|
* fragment of §3.2.2. See {@link AutomationActionSchema}.
|
|
44952
44978
|
*
|
|
44953
44979
|
* A recipe stored in the old shape (`triggers` + `conditions`) still LOADS:
|
|
44954
44980
|
* {@link AutomationRecipeSchema} (the parse-anywhere schema) converts it on parse. Nothing writes it.
|
|
44955
44981
|
*/
|
|
44956
44982
|
/**
|
|
44957
|
-
* One action step: `wait`, or a `
|
|
44983
|
+
* One action step: `wait`, or a device `command`.
|
|
44958
44984
|
*
|
|
44959
|
-
* A
|
|
44960
|
-
* `{ $var }` resolved from the run's
|
|
44961
|
-
*
|
|
44962
|
-
*
|
|
44963
|
-
*
|
|
44964
|
-
*
|
|
44985
|
+
* A command step names a VOCABULARY action (`cover.open`) and who it acts on
|
|
44986
|
+
* (D983: `devices`, a `domain`, or a `{ $var }` resolved from the run's
|
|
44987
|
+
* variables). It never names a cap method: the step runner turns the action
|
|
44988
|
+
* id into its cap + method through the vocabulary index when the step runs
|
|
44989
|
+
* (`vocabCall`), the one place that mapping happens. The step runner
|
|
44990
|
+
* (`runSteps`) owns the device-scope check, the stop-at-first-failure and the
|
|
44991
|
+
* variable substitution, once, for every caller — automations, Actions, and
|
|
44992
|
+
* the notification centre, which runs only Actions (planning#473).
|
|
44965
44993
|
*/
|
|
44966
44994
|
/** The longest one `wait` step lasts. It is cut by a restart of the run and does not survive a hub restart. */
|
|
44967
44995
|
var WAIT_MAX_SECONDS = 3600;
|
|
@@ -44970,39 +44998,22 @@ var AutomationWaitStepSchema = object({
|
|
|
44970
44998
|
kind: literal("wait"),
|
|
44971
44999
|
seconds: number$1().min(0).max(WAIT_MAX_SECONDS, WAIT_TOO_LONG)
|
|
44972
45000
|
});
|
|
44973
|
-
/** The legacy `deviceId` of a cap step: still READ, never written (D983). */
|
|
44974
|
-
var LegacyStepDeviceSchema = union([
|
|
44975
|
-
number$1().int(),
|
|
44976
|
-
array(number$1().int()).min(1),
|
|
44977
|
-
object({ $var: string().min(1) })
|
|
44978
|
-
]);
|
|
44979
45001
|
/**
|
|
44980
|
-
* The fields of a
|
|
44981
|
-
*
|
|
44982
|
-
* device); a step is {@link AutomationCapStepSchema}.
|
|
45002
|
+
* The fields of a command step, before the vocabulary check. A step is
|
|
45003
|
+
* {@link AutomationCommandStepSchema}.
|
|
44983
45004
|
*/
|
|
44984
|
-
var
|
|
44985
|
-
kind: literal("
|
|
44986
|
-
/**
|
|
44987
|
-
|
|
44988
|
-
|
|
44989
|
-
|
|
44990
|
-
* cap + method match exactly one vocabulary action.
|
|
44991
|
-
*/
|
|
44992
|
-
actionId: string().min(1).optional(),
|
|
44993
|
-
/** WHO the step acts on. The only shape that is written. */
|
|
44994
|
-
target: ActionTargetSchema.optional(),
|
|
45005
|
+
var AutomationCommandStepBaseSchema = object({
|
|
45006
|
+
kind: literal("command"),
|
|
45007
|
+
/** WHO the step acts on. */
|
|
45008
|
+
target: ActionTargetSchema,
|
|
45009
|
+
/** The vocabulary action this step performs (`cover.open`). */
|
|
45010
|
+
actionId: string().min(1),
|
|
44995
45011
|
/**
|
|
44996
|
-
*
|
|
44997
|
-
*
|
|
44998
|
-
*
|
|
45012
|
+
* The values of the action's OPEN args (`position`, `presetId`); the args
|
|
45013
|
+
* the vocabulary bakes into the action are added when the step runs. Values
|
|
45014
|
+
* may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
45015
|
+
* SUBSTITUTE and do not evaluate (§3.2.3).
|
|
44999
45016
|
*/
|
|
45000
|
-
deviceId: LegacyStepDeviceSchema.optional(),
|
|
45001
|
-
cap: string().min(1),
|
|
45002
|
-
method: string().min(1),
|
|
45003
|
-
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
45004
|
-
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
45005
|
-
* the expression leaf. */
|
|
45006
45017
|
args: record(string(), unknown()).optional(),
|
|
45007
45018
|
/**
|
|
45008
45019
|
* The unit the person picked per arg (display only). `args` always hold the
|
|
@@ -45011,18 +45022,20 @@ var AutomationCapStepBaseSchema = object({
|
|
|
45011
45022
|
*/
|
|
45012
45023
|
argUnits: record(string(), string().min(1)).optional()
|
|
45013
45024
|
});
|
|
45025
|
+
/** The vocabulary check every command step passes: the action exists. */
|
|
45026
|
+
function refineKnownAction(step, ctx) {
|
|
45027
|
+
if (findVocabAction(step.actionId) === void 0) ctx.addIssue({
|
|
45028
|
+
code: "custom",
|
|
45029
|
+
message: `"${step.actionId}" is not a vocabulary action id`,
|
|
45030
|
+
path: ["actionId"]
|
|
45031
|
+
});
|
|
45032
|
+
}
|
|
45014
45033
|
/**
|
|
45015
|
-
* The steps an internal Action may contain:
|
|
45034
|
+
* The steps an internal Action may contain: command and wait, and nothing that can
|
|
45016
45035
|
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
45017
45036
|
* This is the LEAF shape: no condition of its own.
|
|
45018
45037
|
*/
|
|
45019
|
-
var LeafActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema,
|
|
45020
|
-
message: "a cap step needs a target",
|
|
45021
|
-
path: ["target"]
|
|
45022
|
-
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
45023
|
-
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
45024
|
-
path: ["actionId"]
|
|
45025
|
-
})]);
|
|
45038
|
+
var LeafActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCommandStepBaseSchema.superRefine(refineKnownAction)]);
|
|
45026
45039
|
/**
|
|
45027
45040
|
* The optional guard of a step (#472): `if` is the ONE `when` tree (D980), read
|
|
45028
45041
|
* on the state mirror when the step is reached; `else` runs when it is false.
|
|
@@ -45043,13 +45056,7 @@ var GuardedWaitStepSchema = AutomationWaitStepSchema.extend(stepGuardShape).refi
|
|
|
45043
45056
|
* same two step kinds an automation runs, minus a guard that reads the
|
|
45044
45057
|
* automation's trigger, which such a list does not have.
|
|
45045
45058
|
*/
|
|
45046
|
-
var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema,
|
|
45047
|
-
message: "a cap step needs a target",
|
|
45048
|
-
path: ["target"]
|
|
45049
|
-
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
45050
|
-
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
45051
|
-
path: ["actionId"]
|
|
45052
|
-
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf)]).superRefine((step, ctx) => {
|
|
45059
|
+
var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, AutomationCommandStepBaseSchema.extend(stepGuardShape).superRefine(refineKnownAction).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf)]).superRefine((step, ctx) => {
|
|
45053
45060
|
if (step.if !== void 0 && hasTriggeredByLeaves(step.if)) ctx.addIssue({
|
|
45054
45061
|
code: "custom",
|
|
45055
45062
|
message: TRIGGERED_BY_NEEDS_AUTOMATION,
|
|
@@ -45064,17 +45071,11 @@ var ActionStepSchema = discriminatedUnion("kind", [GuardedWaitStepSchema, Automa
|
|
|
45064
45071
|
var continueOnErrorShape = {
|
|
45065
45072
|
/** A failure of this step is traced `failed-continued` and the next step runs. */
|
|
45066
45073
|
continueOnError: literal(true).optional() };
|
|
45067
|
-
/** An automation's
|
|
45068
|
-
var
|
|
45074
|
+
/** An automation's command step: the shared guarded command step, plus `continueOnError`. */
|
|
45075
|
+
var AutomationGuardedCommandStepSchema = AutomationCommandStepBaseSchema.extend({
|
|
45069
45076
|
...stepGuardShape,
|
|
45070
45077
|
...continueOnErrorShape
|
|
45071
|
-
}).refine((step) => step.
|
|
45072
|
-
message: "a cap step needs a target",
|
|
45073
|
-
path: ["target"]
|
|
45074
|
-
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
45075
|
-
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
45076
|
-
path: ["actionId"]
|
|
45077
|
-
}).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
45078
|
+
}).superRefine(refineKnownAction).refine((step) => step.else === void 0 || step.if !== void 0, elseNeedsIf);
|
|
45078
45079
|
/** End the run here, on purpose. `reason` may carry `{{vars.x}}` slots. */
|
|
45079
45080
|
var AutomationStopStepSchema = object({
|
|
45080
45081
|
kind: literal("stop"),
|
|
@@ -45154,13 +45155,7 @@ function nestTooDeep(issue) {
|
|
|
45154
45155
|
}
|
|
45155
45156
|
var plainStepSchemas = [
|
|
45156
45157
|
AutomationWaitStepSchema.strict(),
|
|
45157
|
-
|
|
45158
|
-
message: "a cap step needs a target",
|
|
45159
|
-
path: ["target"]
|
|
45160
|
-
}).refine((step) => step.target?.kind !== "domain" || step.actionId !== void 0, {
|
|
45161
|
-
message: "a domain target needs the vocabulary action it applies to (actionId)",
|
|
45162
|
-
path: ["actionId"]
|
|
45163
|
-
}),
|
|
45158
|
+
AutomationCommandStepBaseSchema.extend(continueOnErrorShape).strict().superRefine(refineKnownAction),
|
|
45164
45159
|
AutomationStopStepSchema.omit({ if: true }).strict(),
|
|
45165
45160
|
AutomationSetStepSchema.omit({ if: true }).strict(),
|
|
45166
45161
|
AutomationRunActionStepSchema.strict()
|
|
@@ -45171,7 +45166,7 @@ var plainStepSchemas = [
|
|
|
45171
45166
|
*/
|
|
45172
45167
|
var AutomationBranchStepSchema = discriminatedUnion("kind", plainStepSchemas, { error: nestTooDeep });
|
|
45173
45168
|
/** A step that can run beside others: it writes no variable and does not end the run. */
|
|
45174
|
-
var AutomationParallelMemberSchema = AutomationBranchStepSchema.refine((step) => step.kind === "
|
|
45169
|
+
var AutomationParallelMemberSchema = AutomationBranchStepSchema.refine((step) => step.kind === "command" || 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");
|
|
45175
45170
|
/**
|
|
45176
45171
|
* Several steps at once (#500): all are started together and all are awaited.
|
|
45177
45172
|
* A failure fails the run once the others finished, as in a sequence; a member
|
|
@@ -45251,7 +45246,7 @@ var AutomationBodyStepSchema = discriminatedUnion("kind", [
|
|
|
45251
45246
|
var AutomationRepeatStepSchema = repeatSchemaOf(AutomationBodyStepSchema);
|
|
45252
45247
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
45253
45248
|
GuardedWaitStepSchema,
|
|
45254
|
-
|
|
45249
|
+
AutomationGuardedCommandStepSchema,
|
|
45255
45250
|
AutomationStopStepSchema,
|
|
45256
45251
|
AutomationSetStepSchema,
|
|
45257
45252
|
chooseSchemaOf(AutomationBodyStepSchema),
|
|
@@ -45322,110 +45317,6 @@ function convertLegacyRecipe(legacy) {
|
|
|
45322
45317
|
};
|
|
45323
45318
|
}
|
|
45324
45319
|
var AutomationRecipeSchema = union([CurrentAutomationRecipeSchema, LegacyAutomationRecipeSchema.transform(convertLegacyRecipe)]);
|
|
45325
|
-
var DETECTION_DESCRIPTIONS = {
|
|
45326
|
-
deviceId: "the camera that saw it",
|
|
45327
|
-
className: "what it saw",
|
|
45328
|
-
trackId: "the track",
|
|
45329
|
-
score: "how sure the detector was",
|
|
45330
|
-
label: "the label it matched (a name, a plate), empty when none"
|
|
45331
|
-
};
|
|
45332
|
-
var STATIONARY_DESCRIPTIONS = {
|
|
45333
|
-
deviceId: "the camera that saw it",
|
|
45334
|
-
className: "what it saw",
|
|
45335
|
-
zones: "the zones it is in",
|
|
45336
|
-
phase: "appeared or departed",
|
|
45337
|
-
entryId: "the stopped object"
|
|
45338
|
-
};
|
|
45339
|
-
var BECOMES_DESCRIPTIONS = {
|
|
45340
|
-
deviceId: "the device that changed",
|
|
45341
|
-
from: "the value before the change, empty when it is not known",
|
|
45342
|
-
to: "the value it changed to"
|
|
45343
|
-
};
|
|
45344
|
-
/**
|
|
45345
|
-
* A fire's payload is offered too, as `data.<path>` (`{{vars.data.channel}}`):
|
|
45346
|
-
* those names depend on the device, so they are not in this list.
|
|
45347
|
-
*/
|
|
45348
|
-
var FIRED_DESCRIPTIONS = {
|
|
45349
|
-
deviceId: "the device that fired",
|
|
45350
|
-
eventType: "the event, as the device names it",
|
|
45351
|
-
sourceId: "the part of the device that fired (a button), empty when it names none",
|
|
45352
|
-
eventKind: "the kind of press (press, long-press …), empty when the device declares none"
|
|
45353
|
-
};
|
|
45354
|
-
function describe$1(descriptions) {
|
|
45355
|
-
return Object.entries(descriptions).map(([name, description]) => ({
|
|
45356
|
-
name,
|
|
45357
|
-
description
|
|
45358
|
-
}));
|
|
45359
|
-
}
|
|
45360
|
-
describe$1(DETECTION_DESCRIPTIONS);
|
|
45361
|
-
describe$1(STATIONARY_DESCRIPTIONS);
|
|
45362
|
-
describe$1(FIRED_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45363
|
-
...v,
|
|
45364
|
-
hintKey: "firedDeviceId"
|
|
45365
|
-
} : v);
|
|
45366
|
-
describe$1(BECOMES_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45367
|
-
...v,
|
|
45368
|
-
hintKey: "becomesDeviceId"
|
|
45369
|
-
} : v);
|
|
45370
|
-
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
45371
|
-
/**
|
|
45372
|
-
* Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
|
|
45373
|
-
* device. Carries the device's EXACT declared event vocabulary verbatim
|
|
45374
|
-
* (NO normalization). Two shapes:
|
|
45375
|
-
* - structured source (HA `event.*` entity) → `vocabulary` is the fixed
|
|
45376
|
-
* declared set (e.g. press_short, press_long, …).
|
|
45377
|
-
* - generic/legacy source (HA bus events, e.g. zha_event) → `vocabulary`
|
|
45378
|
-
* is [] (open) and `lastEvent.data` carries the complex payload.
|
|
45379
|
-
* `vocabulary` (D1044) names each event for a person and types it for a
|
|
45380
|
-
* rule; `eventTypes` is its list of native tokens, kept for older readers.
|
|
45381
|
-
* A fire names the part of the device it came from in `sourceId` when the
|
|
45382
|
-
* vocabulary declares sources (a remote's buttons).
|
|
45383
|
-
* `seq` is monotonic so two identical events still produce distinct slice writes.
|
|
45384
|
-
*/
|
|
45385
|
-
var EventFireSchema = object({
|
|
45386
|
-
deviceId: number$1(),
|
|
45387
|
-
eventType: string(),
|
|
45388
|
-
sourceId: string().optional(),
|
|
45389
|
-
data: record(string(), unknown()).nullable(),
|
|
45390
|
-
timestamp: number$1().meta(readingMeta("none")),
|
|
45391
|
-
seq: number$1().meta(readingMeta("count"))
|
|
45392
|
-
});
|
|
45393
|
-
var EventEmitterStatusSchema = object({
|
|
45394
|
-
eventTypes: array(string()),
|
|
45395
|
-
/**
|
|
45396
|
-
* Required of a provider by the type. A status written before the field
|
|
45397
|
-
* existed still parses, as an empty vocabulary: read it through
|
|
45398
|
-
* `readEventVocabulary`, which then answers with the plain tokens.
|
|
45399
|
-
*/
|
|
45400
|
-
vocabulary: array(EventVocabularyEntrySchema).default([]),
|
|
45401
|
-
lastEvent: EventFireSchema.nullable(),
|
|
45402
|
-
eventCountSinceStart: number$1().meta(readingMeta("count"))
|
|
45403
|
-
});
|
|
45404
|
-
var eventEmitterCapability = {
|
|
45405
|
-
name: "event-emitter",
|
|
45406
|
-
scope: "device",
|
|
45407
|
-
deviceNative: true,
|
|
45408
|
-
mode: "singleton",
|
|
45409
|
-
deviceTypes: [DeviceType.EventEmitter],
|
|
45410
|
-
methods: {},
|
|
45411
|
-
events: { onEvent: { data: EventFireSchema } },
|
|
45412
|
-
status: {
|
|
45413
|
-
schema: EventEmitterStatusSchema,
|
|
45414
|
-
kind: "push"
|
|
45415
|
-
},
|
|
45416
|
-
runtimeState: EventEmitterStatusSchema,
|
|
45417
|
-
/**
|
|
45418
|
-
* Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
|
|
45419
|
-
*
|
|
45420
|
-
* See `RuntimeStateDurability`. Enforced by
|
|
45421
|
-
* `scripts/check-runtime-state-durability.ts`.
|
|
45422
|
-
*/
|
|
45423
|
-
durability: "session",
|
|
45424
|
-
/**
|
|
45425
|
-
* History (D850): none — `eventCountSinceStart` resets with the addon; events live in ledgers.
|
|
45426
|
-
*/
|
|
45427
|
-
history: "none"
|
|
45428
|
-
};
|
|
45429
45320
|
/**
|
|
45430
45321
|
* Schedulers (D983) — an entity that performs actions over the day.
|
|
45431
45322
|
*
|
|
@@ -45824,6 +45715,110 @@ var AutomationStateDocSchema = object({
|
|
|
45824
45715
|
lastRunStartedAt: number$1().nullable(),
|
|
45825
45716
|
traces: array(AutomationRunTraceSchema)
|
|
45826
45717
|
});
|
|
45718
|
+
var DETECTION_DESCRIPTIONS = {
|
|
45719
|
+
deviceId: "the camera that saw it",
|
|
45720
|
+
className: "what it saw",
|
|
45721
|
+
trackId: "the track",
|
|
45722
|
+
score: "how sure the detector was",
|
|
45723
|
+
label: "the label it matched (a name, a plate), empty when none"
|
|
45724
|
+
};
|
|
45725
|
+
var STATIONARY_DESCRIPTIONS = {
|
|
45726
|
+
deviceId: "the camera that saw it",
|
|
45727
|
+
className: "what it saw",
|
|
45728
|
+
zones: "the zones it is in",
|
|
45729
|
+
phase: "appeared or departed",
|
|
45730
|
+
entryId: "the stopped object"
|
|
45731
|
+
};
|
|
45732
|
+
var BECOMES_DESCRIPTIONS = {
|
|
45733
|
+
deviceId: "the device that changed",
|
|
45734
|
+
from: "the value before the change, empty when it is not known",
|
|
45735
|
+
to: "the value it changed to"
|
|
45736
|
+
};
|
|
45737
|
+
/**
|
|
45738
|
+
* A fire's payload is offered too, as `data.<path>` (`{{vars.data.channel}}`):
|
|
45739
|
+
* those names depend on the device, so they are not in this list.
|
|
45740
|
+
*/
|
|
45741
|
+
var FIRED_DESCRIPTIONS = {
|
|
45742
|
+
deviceId: "the device that fired",
|
|
45743
|
+
eventType: "the event, as the device names it",
|
|
45744
|
+
sourceId: "the part of the device that fired (a button), empty when it names none",
|
|
45745
|
+
eventKind: "the kind of press (press, long-press …), empty when the device declares none"
|
|
45746
|
+
};
|
|
45747
|
+
function describe$1(descriptions) {
|
|
45748
|
+
return Object.entries(descriptions).map(([name, description]) => ({
|
|
45749
|
+
name,
|
|
45750
|
+
description
|
|
45751
|
+
}));
|
|
45752
|
+
}
|
|
45753
|
+
describe$1(DETECTION_DESCRIPTIONS);
|
|
45754
|
+
describe$1(STATIONARY_DESCRIPTIONS);
|
|
45755
|
+
describe$1(FIRED_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45756
|
+
...v,
|
|
45757
|
+
hintKey: "firedDeviceId"
|
|
45758
|
+
} : v);
|
|
45759
|
+
describe$1(BECOMES_DESCRIPTIONS).map((v) => v.name === "deviceId" ? {
|
|
45760
|
+
...v,
|
|
45761
|
+
hintKey: "becomesDeviceId"
|
|
45762
|
+
} : v);
|
|
45763
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
45764
|
+
/**
|
|
45765
|
+
* Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
|
|
45766
|
+
* device. Carries the device's EXACT declared event vocabulary verbatim
|
|
45767
|
+
* (NO normalization). Two shapes:
|
|
45768
|
+
* - structured source (HA `event.*` entity) → `vocabulary` is the fixed
|
|
45769
|
+
* declared set (e.g. press_short, press_long, …).
|
|
45770
|
+
* - generic/legacy source (HA bus events, e.g. zha_event) → `vocabulary`
|
|
45771
|
+
* is [] (open) and `lastEvent.data` carries the complex payload.
|
|
45772
|
+
* `vocabulary` (D1044) names each event for a person and types it for a
|
|
45773
|
+
* rule; `eventTypes` is its list of native tokens, kept for older readers.
|
|
45774
|
+
* A fire names the part of the device it came from in `sourceId` when the
|
|
45775
|
+
* vocabulary declares sources (a remote's buttons).
|
|
45776
|
+
* `seq` is monotonic so two identical events still produce distinct slice writes.
|
|
45777
|
+
*/
|
|
45778
|
+
var EventFireSchema = object({
|
|
45779
|
+
deviceId: number$1(),
|
|
45780
|
+
eventType: string(),
|
|
45781
|
+
sourceId: string().optional(),
|
|
45782
|
+
data: record(string(), unknown()).nullable(),
|
|
45783
|
+
timestamp: number$1().meta(readingMeta("none")),
|
|
45784
|
+
seq: number$1().meta(readingMeta("count"))
|
|
45785
|
+
});
|
|
45786
|
+
var EventEmitterStatusSchema = object({
|
|
45787
|
+
eventTypes: array(string()),
|
|
45788
|
+
/**
|
|
45789
|
+
* Required of a provider by the type. A status written before the field
|
|
45790
|
+
* existed still parses, as an empty vocabulary: read it through
|
|
45791
|
+
* `readEventVocabulary`, which then answers with the plain tokens.
|
|
45792
|
+
*/
|
|
45793
|
+
vocabulary: array(EventVocabularyEntrySchema).default([]),
|
|
45794
|
+
lastEvent: EventFireSchema.nullable(),
|
|
45795
|
+
eventCountSinceStart: number$1().meta(readingMeta("count"))
|
|
45796
|
+
});
|
|
45797
|
+
var eventEmitterCapability = {
|
|
45798
|
+
name: "event-emitter",
|
|
45799
|
+
scope: "device",
|
|
45800
|
+
deviceNative: true,
|
|
45801
|
+
mode: "singleton",
|
|
45802
|
+
deviceTypes: [DeviceType.EventEmitter],
|
|
45803
|
+
methods: {},
|
|
45804
|
+
events: { onEvent: { data: EventFireSchema } },
|
|
45805
|
+
status: {
|
|
45806
|
+
schema: EventEmitterStatusSchema,
|
|
45807
|
+
kind: "push"
|
|
45808
|
+
},
|
|
45809
|
+
runtimeState: EventEmitterStatusSchema,
|
|
45810
|
+
/**
|
|
45811
|
+
* Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
|
|
45812
|
+
*
|
|
45813
|
+
* See `RuntimeStateDurability`. Enforced by
|
|
45814
|
+
* `scripts/check-runtime-state-durability.ts`.
|
|
45815
|
+
*/
|
|
45816
|
+
durability: "session",
|
|
45817
|
+
/**
|
|
45818
|
+
* History (D850): none — `eventCountSinceStart` resets with the addon; events live in ledgers.
|
|
45819
|
+
*/
|
|
45820
|
+
history: "none"
|
|
45821
|
+
};
|
|
45827
45822
|
/**
|
|
45828
45823
|
* Actions — named, runnable things an operator (or an automation) can start,
|
|
45829
45824
|
* declared as LIST ENTRIES, never as devices.
|
|
@@ -46003,6 +45998,177 @@ var UnitWarningSchema = object({
|
|
|
46003
45998
|
assumedUnit: string(),
|
|
46004
45999
|
message: string()
|
|
46005
46000
|
});
|
|
46001
|
+
/**
|
|
46002
|
+
* `device-export` — collection cap for addons that export camstack
|
|
46003
|
+
* devices to external ecosystems (HomeAssistant via MQTT discovery,
|
|
46004
|
+
* HomeKit/HAP, Alexa Smart Home, …).
|
|
46005
|
+
*
|
|
46006
|
+
* No `ecosystem` enum — the addon id identifies the export target.
|
|
46007
|
+
* Each addon owns its mapping logic in its own settings UI. The cap
|
|
46008
|
+
* exposes only the COMMON surface: link state, supported device
|
|
46009
|
+
* kinds (so the UI can filter the device picker), and per-device
|
|
46010
|
+
* expose/unexpose.
|
|
46011
|
+
*
|
|
46012
|
+
* INSTANCES. One export addon can serve several independent targets (a
|
|
46013
|
+
* HomeKit bridge, a Home Assistant broker). `listInstances` names them.
|
|
46014
|
+
* `instanceId` on a READ is optional: absent = the whole addon (linked if any
|
|
46015
|
+
* enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
|
|
46016
|
+
* default instance's); present = that instance; an id the provider does not
|
|
46017
|
+
* serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
|
|
46018
|
+
* never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
|
|
46019
|
+
* change always names the target it changes. A single-instance provider
|
|
46020
|
+
* accepts an absent id on a read or its own id, and refuses any other.
|
|
46021
|
+
*/
|
|
46022
|
+
/** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
|
|
46023
|
+
var InstanceIdSchema$1 = string().min(1);
|
|
46024
|
+
var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
|
|
46025
|
+
/** One independent export target of an addon. */
|
|
46026
|
+
var ExportInstanceSchema = object({
|
|
46027
|
+
instanceId: InstanceIdSchema$1,
|
|
46028
|
+
name: string(),
|
|
46029
|
+
enabled: boolean(),
|
|
46030
|
+
kind: string().optional(),
|
|
46031
|
+
/**
|
|
46032
|
+
* What this instance's platform accepts as a device name, in one operator-facing sentence
|
|
46033
|
+
* (D1049). The derived "Name on <instance>" field shows it; absent = the field says nothing
|
|
46034
|
+
* about the rule.
|
|
46035
|
+
*/
|
|
46036
|
+
nameRule: string().optional()
|
|
46037
|
+
});
|
|
46038
|
+
var LinkStateSchema = _enum([
|
|
46039
|
+
"unlinked",
|
|
46040
|
+
"linked",
|
|
46041
|
+
"error"
|
|
46042
|
+
]);
|
|
46043
|
+
/**
|
|
46044
|
+
* A single label/value row surfaced in the export panel's "Setup"
|
|
46045
|
+
* section. Rendered with a copy-to-clipboard button. `secret: true`
|
|
46046
|
+
* rows are masked by default with a reveal toggle (client ids,
|
|
46047
|
+
* secrets, …).
|
|
46048
|
+
*/
|
|
46049
|
+
var ExportSetupFieldSchema = object({
|
|
46050
|
+
label: string(),
|
|
46051
|
+
value: string(),
|
|
46052
|
+
/** Mask the value by default + render a reveal toggle (client id, secrets). */
|
|
46053
|
+
secret: boolean().optional()
|
|
46054
|
+
});
|
|
46055
|
+
/**
|
|
46056
|
+
* Generic, addon-agnostic pairing/account block. Any export addon can
|
|
46057
|
+
* surface a scannable QR, a set of copyable label/value rows, and a
|
|
46058
|
+
* free-form operator note — the `DeviceExportPanel` renders whatever
|
|
46059
|
+
* the provider supplies and skips the section entirely when `setup`
|
|
46060
|
+
* is unset.
|
|
46061
|
+
*/
|
|
46062
|
+
var ExportSetupSchema = object({
|
|
46063
|
+
/** A string to render as a scannable QR — HAP `X-HM://…` URI, a pairing URL, etc. Omitted when there's nothing to scan. */
|
|
46064
|
+
qr: string().optional(),
|
|
46065
|
+
/** Label/value rows shown with a copy button (HAP setup code, OAuth URLs, client id, linked-account count, …). */
|
|
46066
|
+
fields: array(ExportSetupFieldSchema).readonly().optional(),
|
|
46067
|
+
/** Free-form operator instructions rendered above the fields. */
|
|
46068
|
+
note: string().optional()
|
|
46069
|
+
});
|
|
46070
|
+
var DeviceExportStatusSchema = object({
|
|
46071
|
+
linkState: LinkStateSchema,
|
|
46072
|
+
exposedDeviceCount: number$1(),
|
|
46073
|
+
error: string().optional(),
|
|
46074
|
+
/**
|
|
46075
|
+
* Optional pairing/account info the panel renders in a generic
|
|
46076
|
+
* "Setup" section. Addon-agnostic — the addon id identifies the
|
|
46077
|
+
* export target, never an `ecosystem` key here.
|
|
46078
|
+
*/
|
|
46079
|
+
setup: ExportSetupSchema.optional()
|
|
46080
|
+
});
|
|
46081
|
+
var DeviceKindSchema = string();
|
|
46082
|
+
/**
|
|
46083
|
+
* EXPOSED NAME. What one instance calls a device on its platform, when it should not be the
|
|
46084
|
+
* device's own name. The exporter owns it: it is stored per instance in the exporter's per-device
|
|
46085
|
+
* store (D865), validated by the exporter against its platform's naming rules, and never reaches
|
|
46086
|
+
* the exported id — a rename updates the SAME device on the platform, never a second one.
|
|
46087
|
+
* Blank or `null` = the device's own name. A name the platform would refuse is refused with
|
|
46088
|
+
* `ExportNameRefusedError` (`export-name-refused`), whose message says why.
|
|
46089
|
+
*/
|
|
46090
|
+
var ExposedDeviceSchema = object({
|
|
46091
|
+
deviceId: string(),
|
|
46092
|
+
exposedAs: string().optional(),
|
|
46093
|
+
/** The operator's name for this device on this instance; absent = the device's own name. */
|
|
46094
|
+
exposedName: string().optional(),
|
|
46095
|
+
capabilities: array(string()).optional()
|
|
46096
|
+
});
|
|
46097
|
+
var ExposeInputSchema = object({
|
|
46098
|
+
instanceId: InstanceIdSchema$1,
|
|
46099
|
+
deviceId: string(),
|
|
46100
|
+
capabilities: array(string()).optional(),
|
|
46101
|
+
/** Set the exposed name in the same call; absent = leave it as it is, blank/null = clear it. */
|
|
46102
|
+
exposedName: string().nullable().optional()
|
|
46103
|
+
});
|
|
46104
|
+
var SetExposedNameInputSchema = object({
|
|
46105
|
+
instanceId: InstanceIdSchema$1,
|
|
46106
|
+
deviceId: string(),
|
|
46107
|
+
/** Blank or null = the device's own name. */
|
|
46108
|
+
name: string().nullable(),
|
|
46109
|
+
/**
|
|
46110
|
+
* Validate only: refuse exactly as a real call would, write nothing. A caller saving several
|
|
46111
|
+
* names at once checks every one first, so one refusal writes none (D1049).
|
|
46112
|
+
*/
|
|
46113
|
+
dryRun: boolean().optional()
|
|
46114
|
+
});
|
|
46115
|
+
var UnexposeInputSchema = object({
|
|
46116
|
+
instanceId: InstanceIdSchema$1,
|
|
46117
|
+
deviceId: string()
|
|
46118
|
+
});
|
|
46119
|
+
/**
|
|
46120
|
+
* What an exporter may expose besides devices (D968). An action and a
|
|
46121
|
+
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
46122
|
+
* exporter's own selection, not the device registry.
|
|
46123
|
+
*/
|
|
46124
|
+
var ExportEntityKindSchema = _enum([
|
|
46125
|
+
"device",
|
|
46126
|
+
"action",
|
|
46127
|
+
"notifier"
|
|
46128
|
+
]);
|
|
46129
|
+
var ExportEntityRefSchema = discriminatedUnion("kind", [
|
|
46130
|
+
object({
|
|
46131
|
+
kind: literal("device"),
|
|
46132
|
+
deviceId: string()
|
|
46133
|
+
}),
|
|
46134
|
+
object({
|
|
46135
|
+
kind: literal("action"),
|
|
46136
|
+
actionId: string().min(1)
|
|
46137
|
+
}),
|
|
46138
|
+
object({
|
|
46139
|
+
kind: literal("notifier"),
|
|
46140
|
+
targetId: string().min(1)
|
|
46141
|
+
})
|
|
46142
|
+
]);
|
|
46143
|
+
var ExposedEntitySchema = object({
|
|
46144
|
+
entity: ExportEntityRefSchema,
|
|
46145
|
+
exposedAs: string().optional()
|
|
46146
|
+
});
|
|
46147
|
+
var ExposeEntityInputSchema = object({
|
|
46148
|
+
instanceId: InstanceIdSchema$1,
|
|
46149
|
+
entity: ExportEntityRefSchema
|
|
46150
|
+
});
|
|
46151
|
+
var UnexposeEntityInputSchema = object({
|
|
46152
|
+
instanceId: InstanceIdSchema$1,
|
|
46153
|
+
entity: ExportEntityRefSchema
|
|
46154
|
+
});
|
|
46155
|
+
method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
46156
|
+
kind: "mutation",
|
|
46157
|
+
auth: "admin"
|
|
46158
|
+
}), method(UnexposeInputSchema, _void(), {
|
|
46159
|
+
kind: "mutation",
|
|
46160
|
+
auth: "admin"
|
|
46161
|
+
}), method(SetExposedNameInputSchema, _void(), {
|
|
46162
|
+
kind: "mutation",
|
|
46163
|
+
auth: "admin"
|
|
46164
|
+
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
46165
|
+
kind: "mutation",
|
|
46166
|
+
auth: "admin"
|
|
46167
|
+
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
46168
|
+
kind: "mutation",
|
|
46169
|
+
auth: "admin"
|
|
46170
|
+
});
|
|
46171
|
+
record(string(), string());
|
|
46006
46172
|
_enum([
|
|
46007
46173
|
"automation",
|
|
46008
46174
|
"action",
|
|
@@ -53784,144 +53950,6 @@ method(object({
|
|
|
53784
53950
|
kind: "mutation",
|
|
53785
53951
|
auth: "admin"
|
|
53786
53952
|
});
|
|
53787
|
-
/**
|
|
53788
|
-
* `device-export` — collection cap for addons that export camstack
|
|
53789
|
-
* devices to external ecosystems (HomeAssistant via MQTT discovery,
|
|
53790
|
-
* HomeKit/HAP, Alexa Smart Home, …).
|
|
53791
|
-
*
|
|
53792
|
-
* No `ecosystem` enum — the addon id identifies the export target.
|
|
53793
|
-
* Each addon owns its mapping logic in its own settings UI. The cap
|
|
53794
|
-
* exposes only the COMMON surface: link state, supported device
|
|
53795
|
-
* kinds (so the UI can filter the device picker), and per-device
|
|
53796
|
-
* expose/unexpose.
|
|
53797
|
-
*
|
|
53798
|
-
* INSTANCES. One export addon can serve several independent targets (a
|
|
53799
|
-
* HomeKit bridge, a Home Assistant broker). `listInstances` names them.
|
|
53800
|
-
* `instanceId` on a READ is optional: absent = the whole addon (linked if any
|
|
53801
|
-
* enabled instance is, `exposedDeviceCount` = distinct devices, `setup` = the
|
|
53802
|
-
* default instance's); present = that instance; an id the provider does not
|
|
53803
|
-
* serve is refused with `ExportInstanceUnknownError` (`export-instance-unknown`),
|
|
53804
|
-
* never answered empty. `instanceId` on a WRITE is REQUIRED: a membership
|
|
53805
|
-
* change always names the target it changes. A single-instance provider
|
|
53806
|
-
* accepts an absent id on a read or its own id, and refuses any other.
|
|
53807
|
-
*/
|
|
53808
|
-
/** Which instance of an export addon a call addresses. Absent on a read = the whole addon. */
|
|
53809
|
-
var InstanceIdSchema$1 = string().min(1);
|
|
53810
|
-
var InstancePinSchema = object({ instanceId: InstanceIdSchema$1.optional() });
|
|
53811
|
-
/** One independent export target of an addon. */
|
|
53812
|
-
var ExportInstanceSchema = object({
|
|
53813
|
-
instanceId: InstanceIdSchema$1,
|
|
53814
|
-
name: string(),
|
|
53815
|
-
enabled: boolean(),
|
|
53816
|
-
kind: string().optional()
|
|
53817
|
-
});
|
|
53818
|
-
var LinkStateSchema = _enum([
|
|
53819
|
-
"unlinked",
|
|
53820
|
-
"linked",
|
|
53821
|
-
"error"
|
|
53822
|
-
]);
|
|
53823
|
-
/**
|
|
53824
|
-
* A single label/value row surfaced in the export panel's "Setup"
|
|
53825
|
-
* section. Rendered with a copy-to-clipboard button. `secret: true`
|
|
53826
|
-
* rows are masked by default with a reveal toggle (client ids,
|
|
53827
|
-
* secrets, …).
|
|
53828
|
-
*/
|
|
53829
|
-
var ExportSetupFieldSchema = object({
|
|
53830
|
-
label: string(),
|
|
53831
|
-
value: string(),
|
|
53832
|
-
/** Mask the value by default + render a reveal toggle (client id, secrets). */
|
|
53833
|
-
secret: boolean().optional()
|
|
53834
|
-
});
|
|
53835
|
-
/**
|
|
53836
|
-
* Generic, addon-agnostic pairing/account block. Any export addon can
|
|
53837
|
-
* surface a scannable QR, a set of copyable label/value rows, and a
|
|
53838
|
-
* free-form operator note — the `DeviceExportPanel` renders whatever
|
|
53839
|
-
* the provider supplies and skips the section entirely when `setup`
|
|
53840
|
-
* is unset.
|
|
53841
|
-
*/
|
|
53842
|
-
var ExportSetupSchema = object({
|
|
53843
|
-
/** A string to render as a scannable QR — HAP `X-HM://…` URI, a pairing URL, etc. Omitted when there's nothing to scan. */
|
|
53844
|
-
qr: string().optional(),
|
|
53845
|
-
/** Label/value rows shown with a copy button (HAP setup code, OAuth URLs, client id, linked-account count, …). */
|
|
53846
|
-
fields: array(ExportSetupFieldSchema).readonly().optional(),
|
|
53847
|
-
/** Free-form operator instructions rendered above the fields. */
|
|
53848
|
-
note: string().optional()
|
|
53849
|
-
});
|
|
53850
|
-
var DeviceExportStatusSchema = object({
|
|
53851
|
-
linkState: LinkStateSchema,
|
|
53852
|
-
exposedDeviceCount: number$1(),
|
|
53853
|
-
error: string().optional(),
|
|
53854
|
-
/**
|
|
53855
|
-
* Optional pairing/account info the panel renders in a generic
|
|
53856
|
-
* "Setup" section. Addon-agnostic — the addon id identifies the
|
|
53857
|
-
* export target, never an `ecosystem` key here.
|
|
53858
|
-
*/
|
|
53859
|
-
setup: ExportSetupSchema.optional()
|
|
53860
|
-
});
|
|
53861
|
-
var DeviceKindSchema = string();
|
|
53862
|
-
var ExposedDeviceSchema = object({
|
|
53863
|
-
deviceId: string(),
|
|
53864
|
-
exposedAs: string().optional(),
|
|
53865
|
-
capabilities: array(string()).optional()
|
|
53866
|
-
});
|
|
53867
|
-
var ExposeInputSchema = object({
|
|
53868
|
-
instanceId: InstanceIdSchema$1,
|
|
53869
|
-
deviceId: string(),
|
|
53870
|
-
capabilities: array(string()).optional()
|
|
53871
|
-
});
|
|
53872
|
-
var UnexposeInputSchema = object({
|
|
53873
|
-
instanceId: InstanceIdSchema$1,
|
|
53874
|
-
deviceId: string()
|
|
53875
|
-
});
|
|
53876
|
-
/**
|
|
53877
|
-
* What an exporter may expose besides devices (D968). An action and a
|
|
53878
|
-
* notification target are LIST ENTRIES, never devices (D954): they enter an
|
|
53879
|
-
* exporter's own selection, not the device registry.
|
|
53880
|
-
*/
|
|
53881
|
-
var ExportEntityKindSchema = _enum([
|
|
53882
|
-
"device",
|
|
53883
|
-
"action",
|
|
53884
|
-
"notifier"
|
|
53885
|
-
]);
|
|
53886
|
-
var ExportEntityRefSchema = discriminatedUnion("kind", [
|
|
53887
|
-
object({
|
|
53888
|
-
kind: literal("device"),
|
|
53889
|
-
deviceId: string()
|
|
53890
|
-
}),
|
|
53891
|
-
object({
|
|
53892
|
-
kind: literal("action"),
|
|
53893
|
-
actionId: string().min(1)
|
|
53894
|
-
}),
|
|
53895
|
-
object({
|
|
53896
|
-
kind: literal("notifier"),
|
|
53897
|
-
targetId: string().min(1)
|
|
53898
|
-
})
|
|
53899
|
-
]);
|
|
53900
|
-
var ExposedEntitySchema = object({
|
|
53901
|
-
entity: ExportEntityRefSchema,
|
|
53902
|
-
exposedAs: string().optional()
|
|
53903
|
-
});
|
|
53904
|
-
var ExposeEntityInputSchema = object({
|
|
53905
|
-
instanceId: InstanceIdSchema$1,
|
|
53906
|
-
entity: ExportEntityRefSchema
|
|
53907
|
-
});
|
|
53908
|
-
var UnexposeEntityInputSchema = object({
|
|
53909
|
-
instanceId: InstanceIdSchema$1,
|
|
53910
|
-
entity: ExportEntityRefSchema
|
|
53911
|
-
});
|
|
53912
|
-
method(InstancePinSchema, DeviceExportStatusSchema), method(_void(), array(ExportInstanceSchema)), method(InstancePinSchema, array(DeviceKindSchema)), method(InstancePinSchema, array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
53913
|
-
kind: "mutation",
|
|
53914
|
-
auth: "admin"
|
|
53915
|
-
}), method(UnexposeInputSchema, _void(), {
|
|
53916
|
-
kind: "mutation",
|
|
53917
|
-
auth: "admin"
|
|
53918
|
-
}), optionalMethod(_void(), array(ExportEntityKindSchema)), optionalMethod(InstancePinSchema, array(ExposedEntitySchema)), optionalMethod(ExposeEntityInputSchema, _void(), {
|
|
53919
|
-
kind: "mutation",
|
|
53920
|
-
auth: "admin"
|
|
53921
|
-
}), optionalMethod(UnexposeEntityInputSchema, _void(), {
|
|
53922
|
-
kind: "mutation",
|
|
53923
|
-
auth: "admin"
|
|
53924
|
-
});
|
|
53925
53953
|
var DeviceAliasSchema = string().max(64).regex(/^[a-z][a-z0-9_]*$/);
|
|
53926
53954
|
var DeviceTemplateSchema = string().min(1).max(4096);
|
|
53927
53955
|
/** One value a caller hands the renderer for `thisDevice`. */
|
|
@@ -58776,6 +58804,128 @@ var StateLookSchema = object({
|
|
|
58776
58804
|
background: _enum(HERO_BACKGROUNDS),
|
|
58777
58805
|
animation: HeroAnimationSchema
|
|
58778
58806
|
});
|
|
58807
|
+
/** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
|
|
58808
|
+
var HeroTextSchema = object({
|
|
58809
|
+
en: string(),
|
|
58810
|
+
it: string()
|
|
58811
|
+
});
|
|
58812
|
+
/**
|
|
58813
|
+
* The ONE measurement a simple sensor shows (card wave, phase 1).
|
|
58814
|
+
*
|
|
58815
|
+
* A sensor that measures a single quantity (a temperature, a humidity, a power,
|
|
58816
|
+
* a contact, a text state) is drawn as that reading, once, big, inside a scene
|
|
58817
|
+
* whose motion says what the number means: heat rising when hot, flakes when
|
|
58818
|
+
* cold, a wire flowing at the speed of the power, a ring while a window is
|
|
58819
|
+
* open. Everything the card draws is decided here; a client only paints it.
|
|
58820
|
+
*/
|
|
58821
|
+
var HERO_MEASUREMENT_QUANTITIES = [
|
|
58822
|
+
"temperature",
|
|
58823
|
+
"humidity",
|
|
58824
|
+
"power",
|
|
58825
|
+
"contact",
|
|
58826
|
+
"text"
|
|
58827
|
+
];
|
|
58828
|
+
/** How a temperature reads to a person; drives the heat or the cold scene. */
|
|
58829
|
+
var HERO_MEASUREMENT_BANDS = [
|
|
58830
|
+
"hot",
|
|
58831
|
+
"mild",
|
|
58832
|
+
"cold"
|
|
58833
|
+
];
|
|
58834
|
+
/** `updated` = the value was last read then ("2 min ago"); `changed` = it has held since then ("for 12 min"). */
|
|
58835
|
+
var HERO_MEASUREMENT_SINCE_KINDS = ["updated", "changed"];
|
|
58836
|
+
var HeroMeasurementReadingSchema = object({
|
|
58837
|
+
value: number$1(),
|
|
58838
|
+
quantity: ReadingQuantitySchema
|
|
58839
|
+
});
|
|
58840
|
+
var HeroMeasurementSchema = object({
|
|
58841
|
+
quantity: _enum(HERO_MEASUREMENT_QUANTITIES),
|
|
58842
|
+
reading: HeroMeasurementReadingSchema.nullable(),
|
|
58843
|
+
icon: IconKeySchema.nullable(),
|
|
58844
|
+
value: HeroTextSchema,
|
|
58845
|
+
unit: string().nullable(),
|
|
58846
|
+
band: _enum(HERO_MEASUREMENT_BANDS).nullable(),
|
|
58847
|
+
level: number$1().min(0).max(1).nullable(),
|
|
58848
|
+
word: HeroTextSchema,
|
|
58849
|
+
tone: _enum(HERO_TONES),
|
|
58850
|
+
since: number$1().nullable(),
|
|
58851
|
+
sinceKind: _enum(HERO_MEASUREMENT_SINCE_KINDS),
|
|
58852
|
+
historyKey: string().nullable()
|
|
58853
|
+
});
|
|
58854
|
+
var same$2 = (text) => t(text, text);
|
|
58855
|
+
var clamp01$1 = (n) => Math.min(1, Math.max(0, n));
|
|
58856
|
+
var trimmed = (n, digits) => String(Number(n.toFixed(digits)));
|
|
58857
|
+
function temperatureBand(celsius) {
|
|
58858
|
+
if (celsius < 12) return "cold";
|
|
58859
|
+
if (celsius > 28) return "hot";
|
|
58860
|
+
return "mild";
|
|
58861
|
+
}
|
|
58862
|
+
var BAND_WORD = {
|
|
58863
|
+
hot: t("Hot", "Caldo"),
|
|
58864
|
+
mild: t("Normal", "Normale"),
|
|
58865
|
+
cold: t("Cold", "Freddo")
|
|
58866
|
+
};
|
|
58867
|
+
var BAND_TONE = {
|
|
58868
|
+
hot: "warning",
|
|
58869
|
+
mild: "muted",
|
|
58870
|
+
cold: "info"
|
|
58871
|
+
};
|
|
58872
|
+
function temperatureMeasurement(celsius, since) {
|
|
58873
|
+
const band = temperatureBand(celsius);
|
|
58874
|
+
return {
|
|
58875
|
+
quantity: "temperature",
|
|
58876
|
+
reading: {
|
|
58877
|
+
value: celsius,
|
|
58878
|
+
quantity: "temperature"
|
|
58879
|
+
},
|
|
58880
|
+
icon: "thermometer",
|
|
58881
|
+
value: same$2(trimmed(celsius, 1)),
|
|
58882
|
+
unit: "°C",
|
|
58883
|
+
band,
|
|
58884
|
+
level: null,
|
|
58885
|
+
word: BAND_WORD[band],
|
|
58886
|
+
tone: BAND_TONE[band],
|
|
58887
|
+
since,
|
|
58888
|
+
sinceKind: "updated",
|
|
58889
|
+
historyKey: "cap:temperature-sensor.temperature"
|
|
58890
|
+
};
|
|
58891
|
+
}
|
|
58892
|
+
function humidityMeasurement(percent, since) {
|
|
58893
|
+
const dry = percent < 30;
|
|
58894
|
+
const humid = percent > 70;
|
|
58895
|
+
return {
|
|
58896
|
+
quantity: "humidity",
|
|
58897
|
+
reading: {
|
|
58898
|
+
value: percent,
|
|
58899
|
+
quantity: "humidity"
|
|
58900
|
+
},
|
|
58901
|
+
icon: "droplets",
|
|
58902
|
+
value: same$2(String(Math.round(percent))),
|
|
58903
|
+
unit: "%",
|
|
58904
|
+
band: null,
|
|
58905
|
+
level: clamp01$1(percent / 100),
|
|
58906
|
+
word: dry ? t("Dry", "Secco") : humid ? t("Humid", "Umido") : t("Normal", "Normale"),
|
|
58907
|
+
tone: dry || humid ? "warning" : "info",
|
|
58908
|
+
since,
|
|
58909
|
+
sinceKind: "updated",
|
|
58910
|
+
historyKey: "cap:humidity-sensor.humidity"
|
|
58911
|
+
};
|
|
58912
|
+
}
|
|
58913
|
+
function textMeasurement(text, since) {
|
|
58914
|
+
return {
|
|
58915
|
+
quantity: "text",
|
|
58916
|
+
reading: null,
|
|
58917
|
+
icon: null,
|
|
58918
|
+
value: same$2(text),
|
|
58919
|
+
unit: null,
|
|
58920
|
+
band: null,
|
|
58921
|
+
level: null,
|
|
58922
|
+
word: t("Changed", "Cambiato"),
|
|
58923
|
+
tone: "muted",
|
|
58924
|
+
since,
|
|
58925
|
+
sinceKind: "updated",
|
|
58926
|
+
historyKey: null
|
|
58927
|
+
};
|
|
58928
|
+
}
|
|
58779
58929
|
/** The scene as the model carries it. `null` = the device did not say (never a guess). */
|
|
58780
58930
|
var HeroSceneSchema = object({
|
|
58781
58931
|
id: _enum([
|
|
@@ -58827,11 +58977,6 @@ var HeroSceneSchema = object({
|
|
|
58827
58977
|
});
|
|
58828
58978
|
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;
|
|
58829
58979
|
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;
|
|
58830
|
-
/** A string in both languages, as the cap vocabulary writes it (D920): no `t()` in the model. */
|
|
58831
|
-
var HeroTextSchema = object({
|
|
58832
|
-
en: string(),
|
|
58833
|
-
it: string()
|
|
58834
|
-
});
|
|
58835
58980
|
var HeroRowIdSchema = _enum([
|
|
58836
58981
|
"progress",
|
|
58837
58982
|
"run-for",
|
|
@@ -58937,6 +59082,25 @@ var HeroControlSchema = discriminatedUnion("kind", [
|
|
|
58937
59082
|
optionLabels: record(string(), HeroTextSchema).optional(),
|
|
58938
59083
|
call: HeroCallSchema,
|
|
58939
59084
|
valueArg: string()
|
|
59085
|
+
}),
|
|
59086
|
+
object({
|
|
59087
|
+
kind: literal("number"),
|
|
59088
|
+
id: string(),
|
|
59089
|
+
label: HeroTextSchema,
|
|
59090
|
+
value: number$1().nullable(),
|
|
59091
|
+
/** `null` = any number; the device rounds what it does not accept. */
|
|
59092
|
+
step: number$1().nullable(),
|
|
59093
|
+
unit: string(),
|
|
59094
|
+
call: HeroCallSchema,
|
|
59095
|
+
valueArg: string()
|
|
59096
|
+
}),
|
|
59097
|
+
object({
|
|
59098
|
+
kind: literal("text"),
|
|
59099
|
+
id: string(),
|
|
59100
|
+
label: HeroTextSchema,
|
|
59101
|
+
value: string().nullable(),
|
|
59102
|
+
call: HeroCallSchema,
|
|
59103
|
+
valueArg: string()
|
|
58940
59104
|
})
|
|
58941
59105
|
]);
|
|
58942
59106
|
/** Declared by its interface: a `z.infer` of one more icon union pushes the cap's inferred type past what the compiler can print. */
|
|
@@ -59138,6 +59302,11 @@ var HeroModelSchema = object({
|
|
|
59138
59302
|
timedRun: HeroTimedRunSlotSchema.nullable(),
|
|
59139
59303
|
powerSupply: HeroPowerSupplySlotSchema.nullable(),
|
|
59140
59304
|
rows: array(HeroRowSchema),
|
|
59305
|
+
/**
|
|
59306
|
+
* The ONE measurement of a simple sensor, drawn once inside the scene in place of
|
|
59307
|
+
* the caption, tiles and value chips. Absent = the card draws as before.
|
|
59308
|
+
*/
|
|
59309
|
+
measurement: HeroMeasurementSchema.optional(),
|
|
59141
59310
|
/** Ids of parts that actuate the device, hidden from a session that may not act on it. */
|
|
59142
59311
|
gated: array(string()),
|
|
59143
59312
|
provenance: array(HeroProvenanceSchema)
|
|
@@ -59209,7 +59378,13 @@ var HeroRowModelSchema = object({
|
|
|
59209
59378
|
* the call. Absent or empty = `primary` alone.
|
|
59210
59379
|
*/
|
|
59211
59380
|
extras: array(HeroControlSchema).max(2).optional(),
|
|
59212
|
-
trailing: HeroFactSchema.nullable()
|
|
59381
|
+
trailing: HeroFactSchema.nullable(),
|
|
59382
|
+
/**
|
|
59383
|
+
* The simple sensor's one reading (the hero's `measurement`): the row draws its value
|
|
59384
|
+
* at the right, the unit small, the quantity's icon in the mini-scene. Absent = an
|
|
59385
|
+
* older server or a device that is not a simple sensor: draw `trailing`.
|
|
59386
|
+
*/
|
|
59387
|
+
measurement: HeroMeasurementSchema.optional()
|
|
59213
59388
|
});
|
|
59214
59389
|
new Set([
|
|
59215
59390
|
"temperature",
|
|
@@ -77736,10 +77911,15 @@ getStatus: method(object({ deviceId: number$1() }), RecordingSignalStatusSchema)
|
|
|
77736
77911
|
*/
|
|
77737
77912
|
history: "none"
|
|
77738
77913
|
};
|
|
77739
|
-
var RemoteBindingSchema = object({ action:
|
|
77740
|
-
|
|
77741
|
-
|
|
77742
|
-
|
|
77914
|
+
var RemoteBindingSchema = object({ action: object({
|
|
77915
|
+
kind: literal("cap"),
|
|
77916
|
+
deviceId: number$1().int().nonnegative(),
|
|
77917
|
+
cap: string().min(1),
|
|
77918
|
+
method: string().min(1),
|
|
77919
|
+
args: record(string(), unknown()).optional(),
|
|
77920
|
+
/** The unit the person picked per arg (display only); `args` always hold the cap's own unit. */
|
|
77921
|
+
argUnits: record(string(), string().min(1)).optional()
|
|
77922
|
+
}) });
|
|
77743
77923
|
var RemoteCustomButtonSchema = object({
|
|
77744
77924
|
/** The slug after `custom:` in the role. */
|
|
77745
77925
|
key: string().refine(isRemoteCustomKey, { message: "a lowercase slug" }),
|
|
@@ -78729,7 +78909,7 @@ var sceneMonitorCapability = {
|
|
|
78729
78909
|
* instead of failing silently weeks later.
|
|
78730
78910
|
*
|
|
78731
78911
|
* Reachable from three surfaces on this one mutation: the scene card, a
|
|
78732
|
-
* notification button (an `onTrigger` sequence with a `kind:'
|
|
78912
|
+
* notification button (an `onTrigger` sequence with a `kind:'command'` step —
|
|
78733
78913
|
* no new Notification-Center code at all), and tRPC for scripts.
|
|
78734
78914
|
*/
|
|
78735
78915
|
resetScene: method(object({
|
|
@@ -85722,6 +85902,13 @@ function contribution(cap, schema, build) {
|
|
|
85722
85902
|
}
|
|
85723
85903
|
};
|
|
85724
85904
|
}
|
|
85905
|
+
/** The fragment, carrying the cap's single measurement when it has one. */
|
|
85906
|
+
function measured(fragment, measurement) {
|
|
85907
|
+
return measurement === void 0 ? fragment : {
|
|
85908
|
+
...fragment,
|
|
85909
|
+
measurement
|
|
85910
|
+
};
|
|
85911
|
+
}
|
|
85725
85912
|
function action(id, label, icon, cap, method, args = {}, emphasis = "normal", confirm = null) {
|
|
85726
85913
|
return {
|
|
85727
85914
|
id,
|
|
@@ -86996,10 +87183,26 @@ var controlContribution = contribution("control", ControlStatusSchema, (slice, c
|
|
|
86996
87183
|
};
|
|
86997
87184
|
});
|
|
86998
87185
|
var CONTROL_LABEL = t("Value", "Valore");
|
|
86999
|
-
/**
|
|
87186
|
+
/**
|
|
87187
|
+
* A number with known bounds is a slider, one without a number field, a choice a picker,
|
|
87188
|
+
* free text a text field; a date stays read-only.
|
|
87189
|
+
*/
|
|
87000
87190
|
function controlRows(slice) {
|
|
87001
87191
|
if (slice.kind === "numeric" && typeof slice.value === "number") {
|
|
87002
|
-
if (slice.min === void 0 || slice.max === void 0) return [
|
|
87192
|
+
if (slice.min === void 0 || slice.max === void 0) return [row$1("control", "sliders-horizontal", null, [{
|
|
87193
|
+
kind: "number",
|
|
87194
|
+
id: "control.value",
|
|
87195
|
+
label: CONTROL_LABEL,
|
|
87196
|
+
value: slice.value,
|
|
87197
|
+
step: slice.step ?? null,
|
|
87198
|
+
unit: "",
|
|
87199
|
+
call: {
|
|
87200
|
+
cap: "control",
|
|
87201
|
+
method: "setValue",
|
|
87202
|
+
args: { control: { kind: "numeric" } }
|
|
87203
|
+
},
|
|
87204
|
+
valueArg: "control.value"
|
|
87205
|
+
}])];
|
|
87003
87206
|
return [row$1("control", "sliders-horizontal", null, [{
|
|
87004
87207
|
kind: "slider",
|
|
87005
87208
|
id: "control.value",
|
|
@@ -87030,6 +87233,18 @@ function controlRows(slice) {
|
|
|
87030
87233
|
},
|
|
87031
87234
|
valueArg: "control.value"
|
|
87032
87235
|
}])];
|
|
87236
|
+
if (slice.kind === "text") return [row$1("control", "sliders-horizontal", null, [{
|
|
87237
|
+
kind: "text",
|
|
87238
|
+
id: "control.value",
|
|
87239
|
+
label: CONTROL_LABEL,
|
|
87240
|
+
value: typeof slice.value === "string" ? slice.value : null,
|
|
87241
|
+
call: {
|
|
87242
|
+
cap: "control",
|
|
87243
|
+
method: "setValue",
|
|
87244
|
+
args: { control: { kind: "text" } }
|
|
87245
|
+
},
|
|
87246
|
+
valueArg: "control.value"
|
|
87247
|
+
}])];
|
|
87033
87248
|
return [];
|
|
87034
87249
|
}
|
|
87035
87250
|
var SENSORS_FAMILY_CONTRIBUTIONS = {
|
|
@@ -87385,15 +87600,15 @@ function sensorFragment(value, id, icon, en, it, text) {
|
|
|
87385
87600
|
tiles: { right: [tile$1(en, it, text)] }
|
|
87386
87601
|
};
|
|
87387
87602
|
}
|
|
87388
|
-
var temperatureContribution = contribution("temperature-sensor", TemperatureSensorStatusSchema, (slice) => sensorFragment(slice?.temperature ?? null, "temperature", "thermometer", "Temperature", "Temperatura", formatDegrees(slice?.temperature ?? 0)));
|
|
87389
|
-
var humidityContribution = contribution("humidity-sensor", HumiditySensorStatusSchema, (slice) => sensorFragment(slice?.humidity ?? null, "humidity", "droplets", "Humidity", "Umidità", `${Math.round(slice?.humidity ?? 0)}%`));
|
|
87603
|
+
var temperatureContribution = contribution("temperature-sensor", TemperatureSensorStatusSchema, (slice) => measured(sensorFragment(slice?.temperature ?? null, "temperature", "thermometer", "Temperature", "Temperatura", formatDegrees(slice?.temperature ?? 0)), slice?.temperature == null ? void 0 : temperatureMeasurement(slice.temperature, slice.lastFetchedAt)));
|
|
87604
|
+
var humidityContribution = contribution("humidity-sensor", HumiditySensorStatusSchema, (slice) => measured(sensorFragment(slice?.humidity ?? null, "humidity", "droplets", "Humidity", "Umidità", `${Math.round(slice?.humidity ?? 0)}%`), slice?.humidity == null ? void 0 : humidityMeasurement(slice.humidity, slice.lastFetchedAt)));
|
|
87390
87605
|
var pressureContribution = contribution("pressure-sensor", PressureSensorStatusSchema, (slice) => sensorFragment(slice?.pressure ?? null, "pressure", "gauge", "Pressure", "Pressione", `${Math.round(slice?.pressure ?? 0)} hPa`));
|
|
87391
87606
|
var illuminanceContribution = contribution("ambient-light-sensor", AmbientLightSensorStatusSchema, (slice) => sensorFragment(slice?.illuminance ?? null, "illuminance", "sun", "Light", "Luce", `${Math.round(slice?.illuminance ?? 0)} lx`));
|
|
87392
87607
|
var numericContribution = contribution("numeric-sensor", NumericSensorStatusSchema, (slice) => sensorFragment(slice?.value ?? null, "reading", "gauge", "Value", "Valore", String(Number((slice?.value ?? 0).toFixed(2)))));
|
|
87393
87608
|
var enumContribution = contribution("enum-sensor", EnumSensorStatusSchema, (slice) => {
|
|
87394
87609
|
if (slice === void 0 || slice.value === "") return { reading: reading("sensor", "unknown") };
|
|
87395
87610
|
if (slice.timestamp === true) return { reading: reading("sensor", "on") };
|
|
87396
|
-
return sensorFragment(slice.value, "reading", "gauge", "State", "Stato", slice.value);
|
|
87611
|
+
return measured(sensorFragment(slice.value, "reading", "gauge", "State", "Stato", slice.value), textMeasurement(slice.value, slice.lastFetchedAt));
|
|
87397
87612
|
});
|
|
87398
87613
|
/** Index bands: the headline of an air-quality sensor is a word, not a number. */
|
|
87399
87614
|
function airQualityWord(aqi) {
|
|
@@ -90289,6 +90504,13 @@ Object.freeze({
|
|
|
90289
90504
|
access: "view",
|
|
90290
90505
|
role: "admin"
|
|
90291
90506
|
},
|
|
90507
|
+
"deviceExport.setExposedName": {
|
|
90508
|
+
capName: "device-export",
|
|
90509
|
+
capScope: "system",
|
|
90510
|
+
addonId: null,
|
|
90511
|
+
access: "create",
|
|
90512
|
+
role: "admin"
|
|
90513
|
+
},
|
|
90292
90514
|
"deviceExport.unexposeDevice": {
|
|
90293
90515
|
capName: "device-export",
|
|
90294
90516
|
capScope: "system",
|
|
@@ -98753,48 +98975,6 @@ Object.freeze({
|
|
|
98753
98975
|
role: "admin"
|
|
98754
98976
|
}
|
|
98755
98977
|
});
|
|
98756
|
-
/**
|
|
98757
|
-
* "Do" actions an operator can pick for a device — presentation-free.
|
|
98758
|
-
*
|
|
98759
|
-
* The wire shape stays `{ cap, method, args }`. Since D920 this module no
|
|
98760
|
-
* longer HOLDS a catalogue: {@link DEVICE_DO_ACTIONS} is a projection of the
|
|
98761
|
-
* cap vocabulary (`cap-vocabulary/`), the one cap-keyed map of every action
|
|
98762
|
-
* and trigger — so a picker built on this module and one built on
|
|
98763
|
-
* `actionsForDevice` (the vocabulary resolver) can never disagree about what
|
|
98764
|
-
* a device can do. Ids, baked args and the matching of saved steps are the
|
|
98765
|
-
* vocabulary's; labels here are its English labels.
|
|
98766
|
-
*
|
|
98767
|
-
* Shared by the admin NC Sequences / rule Do editor and Automations; the
|
|
98768
|
-
* viewer can reuse the same catalogue once its step editor catches up.
|
|
98769
|
-
*/
|
|
98770
|
-
function openArgKind(offer) {
|
|
98771
|
-
if (offer.kind === "number") return "number";
|
|
98772
|
-
if (offer.kind === "dynamic" && offer.valueKind === "number") return "number";
|
|
98773
|
-
if (offer.kind === "enum" && offer.values.every((v) => typeof v.value === "number")) return "number";
|
|
98774
|
-
return "string";
|
|
98775
|
-
}
|
|
98776
|
-
function projectOpenArg(arg) {
|
|
98777
|
-
return {
|
|
98778
|
-
key: arg.key,
|
|
98779
|
-
label: arg.label.en,
|
|
98780
|
-
kind: openArgKind(arg.offer),
|
|
98781
|
-
offer: arg.offer,
|
|
98782
|
-
...arg.optional === true ? { optional: true } : {}
|
|
98783
|
-
};
|
|
98784
|
-
}
|
|
98785
|
-
function projectAction(cap, action) {
|
|
98786
|
-
const open = action.openArgs ?? [];
|
|
98787
|
-
return {
|
|
98788
|
-
id: action.id,
|
|
98789
|
-
label: action.label.en,
|
|
98790
|
-
cap,
|
|
98791
|
-
method: action.method,
|
|
98792
|
-
args: action.args ?? {},
|
|
98793
|
-
...open.length > 0 ? { openArgs: open.map(projectOpenArg) } : {}
|
|
98794
|
-
};
|
|
98795
|
-
}
|
|
98796
|
-
Object.entries(CAP_VOCABULARY_BY_NAME).flatMap(([cap, entry]) => entry.scope === "device" ? (entry.actions ?? []).map((a) => projectAction(cap, a)) : []);
|
|
98797
|
-
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;
|
|
98798
98978
|
DeviceFeature.BatteryOperated;
|
|
98799
98979
|
function bound(cap, methods = [], authority = "reported") {
|
|
98800
98980
|
return {
|
|
@@ -102240,6 +102420,15 @@ function systemEventKindsOfFamily(family) {
|
|
|
102240
102420
|
return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
|
|
102241
102421
|
}
|
|
102242
102422
|
NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
|
|
102423
|
+
new Set([
|
|
102424
|
+
"alarm.trigger",
|
|
102425
|
+
"alarm.disarm",
|
|
102426
|
+
"alarm.arm.home",
|
|
102427
|
+
"alarm.arm.away",
|
|
102428
|
+
"alarm.arm.night",
|
|
102429
|
+
"alarm.arm.vacation",
|
|
102430
|
+
"alarm.arm.custom_bypass"
|
|
102431
|
+
]);
|
|
102243
102432
|
/**
|
|
102244
102433
|
* WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
|
|
102245
102434
|
* persists (D640 "a sensor rule fires on the raise, the clear, or both").
|
|
@@ -103925,7 +104114,7 @@ function findSpec(id) {
|
|
|
103925
104114
|
function capsOfSpec(spec) {
|
|
103926
104115
|
switch (spec.kind) {
|
|
103927
104116
|
case "power-meter": return ["power-meter"];
|
|
103928
|
-
case "battery": return ["
|
|
104117
|
+
case "battery": return ["numeric-sensor", "power-meter"];
|
|
103929
104118
|
case "meter": return ["meter"];
|
|
103930
104119
|
case "numeric": return ["numeric-sensor"];
|
|
103931
104120
|
case "text": return ["enum-sensor"];
|
|
@@ -104028,11 +104217,9 @@ function computeSlices(spec, snapshot, options, now) {
|
|
|
104028
104217
|
const v = numberOf(voltage);
|
|
104029
104218
|
const a = numberOf(current);
|
|
104030
104219
|
return {
|
|
104031
|
-
|
|
104032
|
-
|
|
104033
|
-
|
|
104034
|
-
sleeping: false,
|
|
104035
|
-
lastUpdated: now
|
|
104220
|
+
numeric: {
|
|
104221
|
+
value: nullableOf(soc),
|
|
104222
|
+
lastFetchedAt: now
|
|
104036
104223
|
},
|
|
104037
104224
|
...w !== void 0 || v !== void 0 || a !== void 0 ? { powerMeter: {
|
|
104038
104225
|
...w !== void 0 ? { power: w * sign || 0 } : {},
|
|
@@ -104178,12 +104365,6 @@ var METER_CAP = meterCapability.name;
|
|
|
104178
104365
|
var NUMERIC_CAP = numericSensorCapability.name;
|
|
104179
104366
|
var ENUM_CAP = enumSensorCapability.name;
|
|
104180
104367
|
var POWER_COLD = { lastFetchedAt: 0 };
|
|
104181
|
-
var BATTERY_COLD = {
|
|
104182
|
-
level: null,
|
|
104183
|
-
charging: "unknown",
|
|
104184
|
-
sleeping: false,
|
|
104185
|
-
lastUpdated: 0
|
|
104186
|
-
};
|
|
104187
104368
|
var VALUE_COLD = {
|
|
104188
104369
|
value: null,
|
|
104189
104370
|
lastFetchedAt: 0
|
|
@@ -104231,6 +104412,7 @@ var EnpalSensorDevice = class extends BaseDevice {
|
|
|
104231
104412
|
id: `${this.integrationId}:${spec.id}`,
|
|
104232
104413
|
system: ENPAL_SYSTEM,
|
|
104233
104414
|
...spec.kind === "meter" || spec.kind === "numeric" ? { quantity: spec.quantity } : {},
|
|
104415
|
+
...spec.kind === "battery" ? { quantity: spec.level.quantity } : {},
|
|
104234
104416
|
...spec.kind === "text" && spec.timestamp === true ? { dateTimeShape: "date" } : {}
|
|
104235
104417
|
});
|
|
104236
104418
|
}
|
|
@@ -104280,7 +104462,6 @@ var EnpalSensorDevice = class extends BaseDevice {
|
|
|
104280
104462
|
energy: this.energyGuard.accept(kwh)
|
|
104281
104463
|
});
|
|
104282
104464
|
}
|
|
104283
|
-
if (slices.battery !== void 0) this.runtimeState.setCapState(BATTERY_CAP, slices.battery);
|
|
104284
104465
|
if (slices.meter !== void 0) {
|
|
104285
104466
|
const raw = slices.meter.value;
|
|
104286
104467
|
this.runtimeState.setCapState(METER_CAP, {
|
|
@@ -104297,7 +104478,8 @@ var EnpalSensorDevice = class extends BaseDevice {
|
|
|
104297
104478
|
this.registerPowerMeter();
|
|
104298
104479
|
return;
|
|
104299
104480
|
case "battery":
|
|
104300
|
-
this.
|
|
104481
|
+
this.retractBatteryCap();
|
|
104482
|
+
this.registerNumeric();
|
|
104301
104483
|
this.registerPowerMeter();
|
|
104302
104484
|
return;
|
|
104303
104485
|
case "meter":
|
|
@@ -104309,8 +104491,7 @@ var EnpalSensorDevice = class extends BaseDevice {
|
|
|
104309
104491
|
this.runtimeState.setCapState(ENUM_CAP, TEXT_COLD);
|
|
104310
104492
|
return;
|
|
104311
104493
|
case "numeric":
|
|
104312
|
-
this.
|
|
104313
|
-
this.runtimeState.setCapState(NUMERIC_CAP, VALUE_COLD);
|
|
104494
|
+
this.registerNumeric();
|
|
104314
104495
|
return;
|
|
104315
104496
|
}
|
|
104316
104497
|
}
|
|
@@ -104318,15 +104499,18 @@ var EnpalSensorDevice = class extends BaseDevice {
|
|
|
104318
104499
|
this.ctx.registerNativeCap(powerMeterCapability, { getStatus: async () => this.runtimeState.getCapState(POWER_CAP) ?? POWER_COLD });
|
|
104319
104500
|
this.runtimeState.setCapState(POWER_CAP, POWER_COLD);
|
|
104320
104501
|
}
|
|
104321
|
-
|
|
104322
|
-
this.ctx.registerNativeCap(
|
|
104323
|
-
|
|
104324
|
-
|
|
104325
|
-
|
|
104326
|
-
|
|
104327
|
-
|
|
104328
|
-
|
|
104329
|
-
|
|
104502
|
+
registerNumeric() {
|
|
104503
|
+
this.ctx.registerNativeCap(numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState(NUMERIC_CAP) ?? VALUE_COLD });
|
|
104504
|
+
this.runtimeState.setCapState(NUMERIC_CAP, VALUE_COLD);
|
|
104505
|
+
}
|
|
104506
|
+
/**
|
|
104507
|
+
* The storage battery once registered the `battery` cap (#522). This process never
|
|
104508
|
+
* registers it, so the call retracts the binding a previous incarnation left on the hub
|
|
104509
|
+
* (`unregisterNativeCap`'s stale-retract path): a restored device stops answering
|
|
104510
|
+
* `battery` and no `battery` slice is written again.
|
|
104511
|
+
*/
|
|
104512
|
+
retractBatteryCap() {
|
|
104513
|
+
this.ctx.unregisterNativeCap?.(BATTERY_CAP);
|
|
104330
104514
|
}
|
|
104331
104515
|
};
|
|
104332
104516
|
//#endregion
|