@camstack/addon-import-alexa 0.2.155 → 0.2.157
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 +12 -0
- package/dist/{MotionZonesSettings-BAg3ARov.mjs → MotionZonesSettings-Z-NRG681.mjs} +2 -2
- package/dist/{PrivacyMaskSettings-C0cYJFNC.mjs → PrivacyMaskSettings-DiS2hPpi.mjs} +4 -4
- package/dist/{SceneMonitorEditor-BL8xJKp6.mjs → SceneMonitorEditor-BYYK_XwY.mjs} +3 -3
- package/dist/_stub.js +610 -638
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_import_alexa_page-DIPd9Gv5.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_import_alexa_page-B4G6E4Vs.mjs} +3 -3
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DCV5s91A.mjs +26 -0
- package/dist/addon-import-alexa.css +1 -1
- package/dist/addon.js +757 -97
- package/dist/addon.mjs +757 -97
- package/dist/{hostInit-M4qqEmdk.mjs → hostInit-DWBMKrvf.mjs} +2 -2
- package/dist/{player-overlays-COWyo9kN.mjs → player-overlays-5MrLqX2i.mjs} +1 -1
- package/dist/remoteEntry.js +1 -1
- package/dist/{responsive-CLpz8rKp.mjs → responsive-CsfyZVWS.mjs} +1 -1
- package/dist/{scene-monitor-copy-ciy3zaiU.mjs → scene-monitor-copy-DOuMmSwJ.mjs} +1 -1
- package/dist/{square-YnF15tkM.mjs → square-BGn7yRva.mjs} +1 -1
- package/dist/{trash-2-dnzK3zvI.mjs → trash-2-BiBTPv8Y.mjs} +1 -1
- package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_import_alexa_page__remoteEntry_js-FpscgjXK.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_import_alexa_page__remoteEntry_js-BniKFfAU.mjs} +1 -1
- package/package.json +1 -1
- package/dist/_virtual_mf___mfe_internal__addon_import_alexa_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BU5ExEvB.mjs +0 -26
package/dist/addon.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import { networkInterfaces, userInfo } from "node:os";
|
|
5
3
|
import * as fs from "node:fs";
|
|
6
4
|
import { existsSync, promises } from "node:fs";
|
|
7
5
|
import * as path$1 from "node:path";
|
|
8
6
|
import path, { dirname, join } from "node:path";
|
|
9
7
|
import * as fs$1 from "node:fs/promises";
|
|
8
|
+
import * as os from "node:os";
|
|
9
|
+
import { userInfo } from "node:os";
|
|
10
10
|
import { execFileSync, spawn } from "node:child_process";
|
|
11
11
|
import "node:zlib";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
@@ -45,7 +45,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
45
45
|
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
46
46
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
47
47
|
//#endregion
|
|
48
|
-
//#region ../types/dist/event-category-
|
|
48
|
+
//#region ../types/dist/event-category-hdMR7CTW.mjs
|
|
49
49
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
50
50
|
EventCategory["SystemBoot"] = "system.boot";
|
|
51
51
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -810,6 +810,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
810
810
|
*/
|
|
811
811
|
EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
|
|
812
812
|
/**
|
|
813
|
+
* An action run started or ended. Emitted by the `actions` surface.
|
|
814
|
+
* Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
|
|
815
|
+
*/
|
|
816
|
+
EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
|
|
817
|
+
/**
|
|
813
818
|
* Progress update from a running model conversion job.
|
|
814
819
|
* Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
|
|
815
820
|
* Emitted by `addon-model-studio` on the converting node.
|
|
@@ -5621,7 +5626,7 @@ var ZodIssueCode = {
|
|
|
5621
5626
|
var ZodFirstPartyTypeKind;
|
|
5622
5627
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5623
5628
|
//#endregion
|
|
5624
|
-
//#region ../types/dist/sleep
|
|
5629
|
+
//#region ../types/dist/sleep-BHjNez61.mjs
|
|
5625
5630
|
/**
|
|
5626
5631
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5627
5632
|
* window to float samples (D455).
|
|
@@ -8951,6 +8956,8 @@ Object.freeze({ "doorbell": "lastPressedAt" });
|
|
|
8951
8956
|
/** Every capability name, so addon code can check a name without the schema graph. */
|
|
8952
8957
|
var CAP_NAMES = Object.freeze([
|
|
8953
8958
|
"accessories",
|
|
8959
|
+
"action-source",
|
|
8960
|
+
"actions",
|
|
8954
8961
|
"addon-pages",
|
|
8955
8962
|
"addon-pages-source",
|
|
8956
8963
|
"addon-routes",
|
|
@@ -10253,6 +10260,18 @@ Object.freeze({
|
|
|
10253
10260
|
"path": "foodLevel",
|
|
10254
10261
|
"valueType": "number"
|
|
10255
10262
|
},
|
|
10263
|
+
{
|
|
10264
|
+
"path": "bowlFood",
|
|
10265
|
+
"valueType": "number"
|
|
10266
|
+
},
|
|
10267
|
+
{
|
|
10268
|
+
"path": "eating",
|
|
10269
|
+
"valueType": "boolean"
|
|
10270
|
+
},
|
|
10271
|
+
{
|
|
10272
|
+
"path": "supportsMarkRefilled",
|
|
10273
|
+
"valueType": "boolean"
|
|
10274
|
+
},
|
|
10256
10275
|
{
|
|
10257
10276
|
"path": "food1",
|
|
10258
10277
|
"valueType": "number"
|
|
@@ -15926,7 +15945,23 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15926
15945
|
kind: literal("schedule"),
|
|
15927
15946
|
cron: string().min(1).max(120)
|
|
15928
15947
|
}),
|
|
15929
|
-
object({ kind: literal("manual") })
|
|
15948
|
+
object({ kind: literal("manual") }),
|
|
15949
|
+
object({
|
|
15950
|
+
kind: literal("detection"),
|
|
15951
|
+
/** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
|
|
15952
|
+
* matches every raw class that maps to it; any other name matches
|
|
15953
|
+
* exactly. */
|
|
15954
|
+
classes: array(string().min(1)).min(1),
|
|
15955
|
+
deviceIds: array(number().int().nonnegative()).min(1),
|
|
15956
|
+
/** The track must have visited at least one of these zones. */
|
|
15957
|
+
zones: array(string().min(1)).min(1).optional(),
|
|
15958
|
+
/** Best-so-far confidence the track must reach. */
|
|
15959
|
+
minScore: number().min(0).max(1).optional()
|
|
15960
|
+
}),
|
|
15961
|
+
object({
|
|
15962
|
+
kind: literal("interval"),
|
|
15963
|
+
minutes: number().int().min(1).max(1440)
|
|
15964
|
+
})
|
|
15930
15965
|
]);
|
|
15931
15966
|
/**
|
|
15932
15967
|
* One action step.
|
|
@@ -15946,28 +15981,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
|
|
|
15946
15981
|
* is called. The runner still receives a number and is untouched; the
|
|
15947
15982
|
* resolution is the recipe's job, not the runner's.
|
|
15948
15983
|
*/
|
|
15984
|
+
var AutomationWaitStepSchema = object({
|
|
15985
|
+
kind: literal("wait"),
|
|
15986
|
+
seconds: number().min(0).max(300)
|
|
15987
|
+
});
|
|
15988
|
+
var AutomationCapStepSchema = object({
|
|
15989
|
+
kind: literal("cap"),
|
|
15990
|
+
/**
|
|
15991
|
+
* One device, several same-family devices (locks / climate family / …),
|
|
15992
|
+
* or `{ $var }` for the device that triggered the run. An array is
|
|
15993
|
+
* fan-out at codegen — each id gets the same cap.method call.
|
|
15994
|
+
*/
|
|
15995
|
+
deviceId: union([
|
|
15996
|
+
number().int(),
|
|
15997
|
+
array(number().int()).min(1),
|
|
15998
|
+
object({ $var: string().min(1) })
|
|
15999
|
+
]),
|
|
16000
|
+
cap: string().min(1),
|
|
16001
|
+
method: string().min(1),
|
|
16002
|
+
/** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
|
|
16003
|
+
* SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
|
|
16004
|
+
* the expression leaf. */
|
|
16005
|
+
args: record(string(), unknown()).optional()
|
|
16006
|
+
});
|
|
16007
|
+
/**
|
|
16008
|
+
* The steps an internal Action may contain: cap and wait, and nothing that can
|
|
16009
|
+
* call back into an Action — so an Action can never reach itself (no cycles).
|
|
16010
|
+
*/
|
|
16011
|
+
var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
|
|
15949
16012
|
var AutomationActionSchema = discriminatedUnion("kind", [
|
|
16013
|
+
AutomationWaitStepSchema,
|
|
16014
|
+
AutomationCapStepSchema,
|
|
15950
16015
|
object({
|
|
15951
|
-
kind: literal("
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
*/
|
|
15961
|
-
deviceId: union([
|
|
15962
|
-
number().int(),
|
|
15963
|
-
array(number().int()).min(1),
|
|
15964
|
-
object({ $var: string().min(1) })
|
|
15965
|
-
]),
|
|
15966
|
-
cap: string().min(1),
|
|
15967
|
-
method: string().min(1),
|
|
15968
|
-
/** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
|
|
15969
|
-
* evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
|
|
15970
|
-
args: record(string(), unknown()).optional()
|
|
16016
|
+
kind: literal("action"),
|
|
16017
|
+
/** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
|
|
16018
|
+
actionId: string().min(1),
|
|
16019
|
+
/** Values for the action's declared variables; the others take their default. */
|
|
16020
|
+
variables: record(string(), union([
|
|
16021
|
+
string(),
|
|
16022
|
+
number(),
|
|
16023
|
+
boolean()
|
|
16024
|
+
])).optional()
|
|
15971
16025
|
}),
|
|
15972
16026
|
object({
|
|
15973
16027
|
kind: literal("code"),
|
|
@@ -15976,10 +16030,356 @@ var AutomationActionSchema = discriminatedUnion("kind", [
|
|
|
15976
16030
|
code: string().min(1).max(2e4)
|
|
15977
16031
|
})
|
|
15978
16032
|
]);
|
|
16033
|
+
/**
|
|
16034
|
+
* How a trigger that arrives while the automation is busy (or cooling down) is
|
|
16035
|
+
* handled. One of the automation's execution modes, never a property of its
|
|
16036
|
+
* actions. Absent means `single`.
|
|
16037
|
+
*
|
|
16038
|
+
* - `single` — drop the trigger while a run is in progress (traced + logged).
|
|
16039
|
+
* - `restart` — abort the run in progress (at its next step boundary), start fresh.
|
|
16040
|
+
* - `queued` — queue up to `max` triggers, drop the rest (traced).
|
|
16041
|
+
* - `parallel` — up to `max` runs at once, drop the rest (traced).
|
|
16042
|
+
* - `cooldown` — `single`, and no new run until `minutes` after the last run
|
|
16043
|
+
* STARTED; persisted across a restart. A manual trigger bypasses only this
|
|
16044
|
+
* wait, never the single/queued limits.
|
|
16045
|
+
*/
|
|
16046
|
+
var AutomationRunModeSchema = discriminatedUnion("mode", [
|
|
16047
|
+
object({ mode: literal("single") }),
|
|
16048
|
+
object({ mode: literal("restart") }),
|
|
16049
|
+
object({
|
|
16050
|
+
mode: literal("queued"),
|
|
16051
|
+
max: number().int().min(1).max(10)
|
|
16052
|
+
}),
|
|
16053
|
+
object({
|
|
16054
|
+
mode: literal("parallel"),
|
|
16055
|
+
max: number().int().min(1).max(10)
|
|
16056
|
+
}),
|
|
16057
|
+
object({
|
|
16058
|
+
mode: literal("cooldown"),
|
|
16059
|
+
minutes: number().int().min(1).max(1440)
|
|
16060
|
+
})
|
|
16061
|
+
]);
|
|
15979
16062
|
var AutomationRecipeSchema = object({
|
|
15980
16063
|
triggers: array(AutomationTriggerSchema),
|
|
15981
16064
|
conditions: AutomationConditionSchema.optional(),
|
|
15982
|
-
actions: array(AutomationActionSchema)
|
|
16065
|
+
actions: array(AutomationActionSchema),
|
|
16066
|
+
runMode: AutomationRunModeSchema.optional(),
|
|
16067
|
+
/** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
|
|
16068
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
16069
|
+
});
|
|
16070
|
+
new Set([
|
|
16071
|
+
{
|
|
16072
|
+
id: "person",
|
|
16073
|
+
name: "Person"
|
|
16074
|
+
},
|
|
16075
|
+
{
|
|
16076
|
+
id: "vehicle",
|
|
16077
|
+
name: "Vehicle"
|
|
16078
|
+
},
|
|
16079
|
+
{
|
|
16080
|
+
id: "animal",
|
|
16081
|
+
name: "Animal"
|
|
16082
|
+
},
|
|
16083
|
+
{
|
|
16084
|
+
id: "package",
|
|
16085
|
+
name: "Package"
|
|
16086
|
+
}
|
|
16087
|
+
].map((l) => l.id));
|
|
16088
|
+
var COCO_TO_MACRO = {
|
|
16089
|
+
mapping: {
|
|
16090
|
+
person: "person",
|
|
16091
|
+
bicycle: "vehicle",
|
|
16092
|
+
car: "vehicle",
|
|
16093
|
+
motorcycle: "vehicle",
|
|
16094
|
+
airplane: "vehicle",
|
|
16095
|
+
bus: "vehicle",
|
|
16096
|
+
train: "vehicle",
|
|
16097
|
+
truck: "vehicle",
|
|
16098
|
+
boat: "vehicle",
|
|
16099
|
+
bird: "animal",
|
|
16100
|
+
cat: "animal",
|
|
16101
|
+
dog: "animal",
|
|
16102
|
+
horse: "animal",
|
|
16103
|
+
sheep: "animal",
|
|
16104
|
+
cow: "animal",
|
|
16105
|
+
elephant: "animal",
|
|
16106
|
+
bear: "animal",
|
|
16107
|
+
zebra: "animal",
|
|
16108
|
+
giraffe: "animal",
|
|
16109
|
+
suitcase: "package",
|
|
16110
|
+
backpack: "package",
|
|
16111
|
+
handbag: "package"
|
|
16112
|
+
},
|
|
16113
|
+
preserveOriginal: false
|
|
16114
|
+
};
|
|
16115
|
+
new Set(Object.values(COCO_TO_MACRO.mapping));
|
|
16116
|
+
/**
|
|
16117
|
+
* Run traces — what an automation (or, later, an Action run) DID, node by node,
|
|
16118
|
+
* so the editor can paint every block green or red with its reason.
|
|
16119
|
+
*
|
|
16120
|
+
* A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
|
|
16121
|
+
* every condition node (containers and leaves, including the ones a
|
|
16122
|
+
* short-circuit never reached), the run-mode verdict and every action step —
|
|
16123
|
+
* each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
|
|
16124
|
+
* for an Action run). Skipped
|
|
16125
|
+
* evaluations are traced too, and identical consecutive skips collapse into a
|
|
16126
|
+
* repeat count so a trigger that fails every 5 minutes cannot flush real runs.
|
|
16127
|
+
*
|
|
16128
|
+
* {@link AutomationStepTraceSchema} is deliberately free of automation
|
|
16129
|
+
* vocabulary so an Action run records the same step shape.
|
|
16130
|
+
*/
|
|
16131
|
+
/** A value a trace may show: JSON primitives and short lists, nothing deeper. */
|
|
16132
|
+
var TraceValueSchema = union([
|
|
16133
|
+
string(),
|
|
16134
|
+
number(),
|
|
16135
|
+
boolean(),
|
|
16136
|
+
_null(),
|
|
16137
|
+
array(union([string(), number()]))
|
|
16138
|
+
]);
|
|
16139
|
+
var AutomationRunOutcomeSchema = _enum([
|
|
16140
|
+
"ran",
|
|
16141
|
+
"skipped-trigger",
|
|
16142
|
+
"skipped-conditions",
|
|
16143
|
+
"skipped-cooldown",
|
|
16144
|
+
"skipped-overlap",
|
|
16145
|
+
"failed",
|
|
16146
|
+
"aborted"
|
|
16147
|
+
]);
|
|
16148
|
+
/** `not-evaluated` = a short-circuit never reached the node. */
|
|
16149
|
+
var TraceNodeOutcomeSchema = _enum([
|
|
16150
|
+
"pass",
|
|
16151
|
+
"fail",
|
|
16152
|
+
"not-evaluated"
|
|
16153
|
+
]);
|
|
16154
|
+
var AutomationTriggerTraceSchema = object({
|
|
16155
|
+
kind: string().min(1),
|
|
16156
|
+
outcome: _enum(["pass", "fail"]),
|
|
16157
|
+
/** Why an event was rejected ("class person not in [dog]") or what fired. */
|
|
16158
|
+
reason: string(),
|
|
16159
|
+
/** deviceId, className, trackId, score … — only what the trigger exposes. */
|
|
16160
|
+
vars: record(string(), TraceValueSchema)
|
|
16161
|
+
});
|
|
16162
|
+
var AutomationConditionTraceNodeSchema = object({
|
|
16163
|
+
/** `conditions` for the root, `conditions.<i>` for its i-th child, … */
|
|
16164
|
+
path: string().min(1),
|
|
16165
|
+
kind: _enum([
|
|
16166
|
+
"all",
|
|
16167
|
+
"any",
|
|
16168
|
+
"not",
|
|
16169
|
+
"condition",
|
|
16170
|
+
"expression"
|
|
16171
|
+
]),
|
|
16172
|
+
outcome: TraceNodeOutcomeSchema,
|
|
16173
|
+
reason: string(),
|
|
16174
|
+
deviceId: number().int().optional(),
|
|
16175
|
+
cap: string().optional(),
|
|
16176
|
+
fieldPath: string().optional(),
|
|
16177
|
+
operator: string().optional(),
|
|
16178
|
+
expected: TraceValueSchema.optional(),
|
|
16179
|
+
/** What the mirror held; `null` when it held nothing (unknown). */
|
|
16180
|
+
observed: TraceValueSchema.optional()
|
|
16181
|
+
});
|
|
16182
|
+
var AutomationRunVerdictSchema = object({
|
|
16183
|
+
mode: AutomationRunModeSchema,
|
|
16184
|
+
decision: _enum([
|
|
16185
|
+
"run",
|
|
16186
|
+
"queued",
|
|
16187
|
+
"restart",
|
|
16188
|
+
"drop",
|
|
16189
|
+
"cooldown",
|
|
16190
|
+
"bypass"
|
|
16191
|
+
]),
|
|
16192
|
+
reason: string()
|
|
16193
|
+
});
|
|
16194
|
+
/** One executed (or not-run) step — shared by automations and Action runs. */
|
|
16195
|
+
var AutomationStepTraceSchema = object({
|
|
16196
|
+
/** `actions.<i>` */
|
|
16197
|
+
path: string().min(1),
|
|
16198
|
+
kind: string().min(1),
|
|
16199
|
+
cap: string().optional(),
|
|
16200
|
+
method: string().optional(),
|
|
16201
|
+
deviceId: number().int().optional(),
|
|
16202
|
+
startedAt: number().optional(),
|
|
16203
|
+
endedAt: number().optional(),
|
|
16204
|
+
outcome: _enum([
|
|
16205
|
+
"ok",
|
|
16206
|
+
"error",
|
|
16207
|
+
"not-run"
|
|
16208
|
+
]),
|
|
16209
|
+
message: string().optional()
|
|
16210
|
+
});
|
|
16211
|
+
var AutomationRunTraceSchema = object({
|
|
16212
|
+
runId: string().min(1),
|
|
16213
|
+
startedAt: number(),
|
|
16214
|
+
endedAt: number(),
|
|
16215
|
+
trigger: AutomationTriggerTraceSchema,
|
|
16216
|
+
conditions: array(AutomationConditionTraceNodeSchema),
|
|
16217
|
+
verdict: AutomationRunVerdictSchema.optional(),
|
|
16218
|
+
steps: array(AutomationStepTraceSchema),
|
|
16219
|
+
outcome: AutomationRunOutcomeSchema,
|
|
16220
|
+
/** How many identical consecutive skips this trace stands for (>= 1). */
|
|
16221
|
+
repeat: number().int().min(1),
|
|
16222
|
+
/** When the latest of those repeats happened. */
|
|
16223
|
+
lastAt: number()
|
|
16224
|
+
});
|
|
16225
|
+
/**
|
|
16226
|
+
* What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
|
|
16227
|
+
* `action:<actionId>`): the cooldown stamp and the trace ring.
|
|
16228
|
+
*/
|
|
16229
|
+
var AutomationStateDocSchema = object({
|
|
16230
|
+
version: literal(1),
|
|
16231
|
+
lastRunStartedAt: number().nullable(),
|
|
16232
|
+
traces: array(AutomationRunTraceSchema)
|
|
16233
|
+
});
|
|
16234
|
+
/**
|
|
16235
|
+
* Actions — named, runnable things an operator (or an automation) can start,
|
|
16236
|
+
* declared as LIST ENTRIES, never as devices.
|
|
16237
|
+
*
|
|
16238
|
+
* An entry is either **internal** (a short list of cap/wait steps kept in the
|
|
16239
|
+
* hub's `actions` record kind) or declared by an addon through the
|
|
16240
|
+
* `action-source` collection (a Home Assistant script, a scene …). The one
|
|
16241
|
+
* surface the UI and automations talk to is the `actions` cap; it keeps no
|
|
16242
|
+
* catalog of its own (D3) — every `list` asks the providers live.
|
|
16243
|
+
*
|
|
16244
|
+
* This file is the data half: the entry schema, the variables an entry may
|
|
16245
|
+
* declare, and the pure resolution of the values a run was given. It holds no
|
|
16246
|
+
* runtime.
|
|
16247
|
+
*/
|
|
16248
|
+
/** What a variable's value is. `select` takes one of `options`. */
|
|
16249
|
+
var ActionVariableTypeSchema = _enum([
|
|
16250
|
+
"string",
|
|
16251
|
+
"number",
|
|
16252
|
+
"boolean",
|
|
16253
|
+
"select"
|
|
16254
|
+
]);
|
|
16255
|
+
var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16256
|
+
var ActionVariableValueSchema = union([
|
|
16257
|
+
string(),
|
|
16258
|
+
number(),
|
|
16259
|
+
boolean()
|
|
16260
|
+
]);
|
|
16261
|
+
/**
|
|
16262
|
+
* One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
|
|
16263
|
+
* value) or `{{vars.name}}` (inside a string) in their args.
|
|
16264
|
+
*/
|
|
16265
|
+
var ActionVariableSchema = object({
|
|
16266
|
+
name: string().regex(ACTION_VARIABLE_NAME_RE),
|
|
16267
|
+
label: string().min(1).max(120).optional(),
|
|
16268
|
+
type: ActionVariableTypeSchema,
|
|
16269
|
+
required: boolean().optional(),
|
|
16270
|
+
default: ActionVariableValueSchema.optional(),
|
|
16271
|
+
/** The allowed values of a `select`. */
|
|
16272
|
+
options: array(string().min(1)).min(1).optional()
|
|
16273
|
+
}).superRefine((v, ctx) => {
|
|
16274
|
+
if (v.type === "select" && v.options === void 0) ctx.addIssue({
|
|
16275
|
+
code: "custom",
|
|
16276
|
+
message: "a select variable needs options",
|
|
16277
|
+
path: ["options"]
|
|
16278
|
+
});
|
|
16279
|
+
if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
|
|
16280
|
+
code: "custom",
|
|
16281
|
+
message: variableValueError(v, v.default) ?? "invalid default",
|
|
16282
|
+
path: ["default"]
|
|
16283
|
+
});
|
|
16284
|
+
});
|
|
16285
|
+
/** Why `value` is not a legal value for the variable, or null when it is. */
|
|
16286
|
+
function variableValueError(variable, value) {
|
|
16287
|
+
switch (variable.type) {
|
|
16288
|
+
case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
|
|
16289
|
+
case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
|
|
16290
|
+
case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
|
|
16291
|
+
case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
|
|
16292
|
+
}
|
|
16293
|
+
}
|
|
16294
|
+
var ActionSourceKindSchema = _enum(["internal", "addon"]);
|
|
16295
|
+
function isInternalActionId(actionId) {
|
|
16296
|
+
return actionId.startsWith("internal:") && actionId.length > 9;
|
|
16297
|
+
}
|
|
16298
|
+
/** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
|
|
16299
|
+
function addonIdOfActionId(actionId) {
|
|
16300
|
+
if (isInternalActionId(actionId)) return null;
|
|
16301
|
+
const at = actionId.indexOf(":");
|
|
16302
|
+
return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
|
|
16303
|
+
}
|
|
16304
|
+
var ActionStatusSchema = object({
|
|
16305
|
+
isRunning: boolean(),
|
|
16306
|
+
/** Ms epoch the latest run STARTED; null when it never ran. */
|
|
16307
|
+
lastRunAt: number().nullable(),
|
|
16308
|
+
/** The latest run's failure; null on success or when it never ran. */
|
|
16309
|
+
lastError: string().nullable()
|
|
16310
|
+
});
|
|
16311
|
+
var ActionEntrySchema = object({
|
|
16312
|
+
/** `internal:<uuid>` or `<addonId>:<localId>`. */
|
|
16313
|
+
id: string().min(3),
|
|
16314
|
+
name: string().min(1).max(120),
|
|
16315
|
+
/** The addon that declared it; `core-blocks` for an internal one. */
|
|
16316
|
+
addonId: string().min(1),
|
|
16317
|
+
source: ActionSourceKindSchema,
|
|
16318
|
+
/** An addon-declared entry is edited where it was declared. */
|
|
16319
|
+
readOnly: boolean(),
|
|
16320
|
+
/** Internal only: cap and wait steps. No action-in-action, so no cycles. */
|
|
16321
|
+
steps: array(ActionStepSchema).min(1).max(50).optional(),
|
|
16322
|
+
variables: array(ActionVariableSchema).max(20).optional(),
|
|
16323
|
+
status: ActionStatusSchema
|
|
16324
|
+
}).superRefine((entry, ctx) => {
|
|
16325
|
+
if (entry.source === "internal") {
|
|
16326
|
+
if (!isInternalActionId(entry.id)) ctx.addIssue({
|
|
16327
|
+
code: "custom",
|
|
16328
|
+
message: "an internal action id is internal:<uuid>",
|
|
16329
|
+
path: ["id"]
|
|
16330
|
+
});
|
|
16331
|
+
return;
|
|
16332
|
+
}
|
|
16333
|
+
if (entry.steps !== void 0) ctx.addIssue({
|
|
16334
|
+
code: "custom",
|
|
16335
|
+
message: "only an internal action carries steps",
|
|
16336
|
+
path: ["steps"]
|
|
16337
|
+
});
|
|
16338
|
+
if (!entry.readOnly) ctx.addIssue({
|
|
16339
|
+
code: "custom",
|
|
16340
|
+
message: "an addon action is read-only here",
|
|
16341
|
+
path: ["readOnly"]
|
|
16342
|
+
});
|
|
16343
|
+
if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
|
|
16344
|
+
code: "custom",
|
|
16345
|
+
message: "an addon action id is <addonId>:<localId>",
|
|
16346
|
+
path: ["id"]
|
|
16347
|
+
});
|
|
16348
|
+
});
|
|
16349
|
+
/** What an operator writes for an internal action. */
|
|
16350
|
+
var ActionInputSchema = object({
|
|
16351
|
+
name: string().min(1).max(120),
|
|
16352
|
+
steps: array(ActionStepSchema).min(1).max(50),
|
|
16353
|
+
variables: array(ActionVariableSchema).max(20).optional()
|
|
16354
|
+
});
|
|
16355
|
+
/** Who started a run. Absent = the operator pressed it. */
|
|
16356
|
+
var ActionRunOriginSchema = object({
|
|
16357
|
+
/** The automation block whose step started it. */
|
|
16358
|
+
blockId: string().min(1).optional() });
|
|
16359
|
+
/** `started` = the caller did not ask to wait. */
|
|
16360
|
+
var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
|
|
16361
|
+
var ActionRunResultSchema = object({
|
|
16362
|
+
runId: string().min(1),
|
|
16363
|
+
outcome: ActionRunOutcomeSchema,
|
|
16364
|
+
/** Why it did not run, or what failed. */
|
|
16365
|
+
message: string().optional()
|
|
16366
|
+
});
|
|
16367
|
+
_enum([
|
|
16368
|
+
"not-found",
|
|
16369
|
+
"read-only",
|
|
16370
|
+
"invalid-variables",
|
|
16371
|
+
"invalid-steps",
|
|
16372
|
+
"not-linked",
|
|
16373
|
+
"busy"
|
|
16374
|
+
]);
|
|
16375
|
+
object({
|
|
16376
|
+
actionId: string(),
|
|
16377
|
+
runId: string(),
|
|
16378
|
+
isRunning: boolean(),
|
|
16379
|
+
/** Set when the run ended. */
|
|
16380
|
+
outcome: AutomationRunOutcomeSchema.optional(),
|
|
16381
|
+
lastRunAt: number().nullable(),
|
|
16382
|
+
lastError: string().nullable()
|
|
15983
16383
|
});
|
|
15984
16384
|
/**
|
|
15985
16385
|
* `addon-pages` — system-scoped singleton aggregator cap. Public-facing
|
|
@@ -19714,7 +20114,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
|
|
|
19714
20114
|
kind: "mutation",
|
|
19715
20115
|
auth: "admin",
|
|
19716
20116
|
access: "view"
|
|
19717
|
-
})
|
|
20117
|
+
}), method(object({
|
|
20118
|
+
blockId: string().min(1),
|
|
20119
|
+
trace: AutomationRunTraceSchema.optional(),
|
|
20120
|
+
runStartedAt: number().optional(),
|
|
20121
|
+
keepTraces: number().int().min(1).max(100).optional()
|
|
20122
|
+
}), _void(), {
|
|
20123
|
+
kind: "mutation",
|
|
20124
|
+
auth: "admin"
|
|
20125
|
+
}), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
|
|
20126
|
+
traces: array(AutomationRunTraceSchema),
|
|
20127
|
+
lastError: string().nullable()
|
|
20128
|
+
}), { auth: "admin" });
|
|
19718
20129
|
/**
|
|
19719
20130
|
* `custom-model-registry` — collection cap exposing operator-registered
|
|
19720
20131
|
* custom detection models. Each provider (today: `addon-model-studio`)
|
|
@@ -45038,51 +45449,6 @@ var notificationOutputCapability = {
|
|
|
45038
45449
|
})
|
|
45039
45450
|
}
|
|
45040
45451
|
};
|
|
45041
|
-
new Set([
|
|
45042
|
-
{
|
|
45043
|
-
id: "person",
|
|
45044
|
-
name: "Person"
|
|
45045
|
-
},
|
|
45046
|
-
{
|
|
45047
|
-
id: "vehicle",
|
|
45048
|
-
name: "Vehicle"
|
|
45049
|
-
},
|
|
45050
|
-
{
|
|
45051
|
-
id: "animal",
|
|
45052
|
-
name: "Animal"
|
|
45053
|
-
},
|
|
45054
|
-
{
|
|
45055
|
-
id: "package",
|
|
45056
|
-
name: "Package"
|
|
45057
|
-
}
|
|
45058
|
-
].map((l) => l.id));
|
|
45059
|
-
var COCO_TO_MACRO = {
|
|
45060
|
-
mapping: {
|
|
45061
|
-
person: "person",
|
|
45062
|
-
bicycle: "vehicle",
|
|
45063
|
-
car: "vehicle",
|
|
45064
|
-
motorcycle: "vehicle",
|
|
45065
|
-
airplane: "vehicle",
|
|
45066
|
-
bus: "vehicle",
|
|
45067
|
-
train: "vehicle",
|
|
45068
|
-
truck: "vehicle",
|
|
45069
|
-
boat: "vehicle",
|
|
45070
|
-
bird: "animal",
|
|
45071
|
-
cat: "animal",
|
|
45072
|
-
dog: "animal",
|
|
45073
|
-
horse: "animal",
|
|
45074
|
-
sheep: "animal",
|
|
45075
|
-
cow: "animal",
|
|
45076
|
-
elephant: "animal",
|
|
45077
|
-
bear: "animal",
|
|
45078
|
-
zebra: "animal",
|
|
45079
|
-
giraffe: "animal",
|
|
45080
|
-
suitcase: "package",
|
|
45081
|
-
backpack: "package",
|
|
45082
|
-
handbag: "package"
|
|
45083
|
-
},
|
|
45084
|
-
preserveOriginal: false
|
|
45085
|
-
};
|
|
45086
45452
|
var AUDIO_MACRO_LABELS = [
|
|
45087
45453
|
{
|
|
45088
45454
|
id: "speech",
|
|
@@ -57888,6 +58254,84 @@ var audioMetricsCapability = {
|
|
|
57888
58254
|
*/
|
|
57889
58255
|
history: "none"
|
|
57890
58256
|
};
|
|
58257
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58258
|
+
actionId: string().min(1),
|
|
58259
|
+
variables: record(string(), ActionVariableValueSchema).optional()
|
|
58260
|
+
}), _void(), {
|
|
58261
|
+
kind: "mutation",
|
|
58262
|
+
auth: "admin"
|
|
58263
|
+
}), method(object({ actionId: string().min(1) }), _void(), {
|
|
58264
|
+
kind: "mutation",
|
|
58265
|
+
auth: "admin"
|
|
58266
|
+
});
|
|
58267
|
+
/**
|
|
58268
|
+
* actions — the ONE surface for runnable actions, for the UI and for
|
|
58269
|
+
* automations. Provided by the core-blocks builtin on the hub.
|
|
58270
|
+
*
|
|
58271
|
+
* An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
|
|
58272
|
+
* steps, kept in the hub's `actions` record kind) or declared by an addon over
|
|
58273
|
+
* `action-source`. `list` asks every `action-source` provider LIVE and overlays
|
|
58274
|
+
* the run status from the hub's `runs` ledger; there is no catalog here to
|
|
58275
|
+
* drift (D3).
|
|
58276
|
+
*
|
|
58277
|
+
* **Permissions follow the camera, as linked devices do.** An action is linked
|
|
58278
|
+
* to cameras by the operator (`setLinks`, D356: nothing links itself). A
|
|
58279
|
+
* non-admin may `get` / `run` an action only through a camera they may see that
|
|
58280
|
+
* links it: they name the camera (`deviceId`, a top-level field so the scope
|
|
58281
|
+
* gate sees it) and the provider refuses an action that camera does not link.
|
|
58282
|
+
* An admin, and an in-process caller (an automation block), may run any action.
|
|
58283
|
+
* Every refusal is typed (`[not-linked] …`) and logged.
|
|
58284
|
+
*
|
|
58285
|
+
* A second `run` of an action that is already running is REFUSED — traced and
|
|
58286
|
+
* logged — never queued.
|
|
58287
|
+
*/
|
|
58288
|
+
var ActionIdInput = object({ actionId: string().min(1) });
|
|
58289
|
+
var ActionLinksSchema = object({
|
|
58290
|
+
deviceId: number().int().nonnegative(),
|
|
58291
|
+
actionIds: array(string().min(1)).max(100)
|
|
58292
|
+
});
|
|
58293
|
+
method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
|
|
58294
|
+
actionId: string().min(1),
|
|
58295
|
+
deviceId: number().int().nonnegative().optional()
|
|
58296
|
+
}), object({ action: ActionEntrySchema.nullable() }), {
|
|
58297
|
+
role: "viewer",
|
|
58298
|
+
auth: "protected",
|
|
58299
|
+
kind: "query",
|
|
58300
|
+
caller: "required"
|
|
58301
|
+
}), method(object({
|
|
58302
|
+
actionId: string().min(1),
|
|
58303
|
+
variables: record(string(), ActionVariableValueSchema).optional(),
|
|
58304
|
+
wait: boolean().optional(),
|
|
58305
|
+
origin: ActionRunOriginSchema.optional(),
|
|
58306
|
+
deviceId: number().int().nonnegative().optional()
|
|
58307
|
+
}), ActionRunResultSchema, {
|
|
58308
|
+
role: "viewer",
|
|
58309
|
+
auth: "protected",
|
|
58310
|
+
kind: "mutation",
|
|
58311
|
+
caller: "required"
|
|
58312
|
+
}), method(ActionIdInput, _void(), {
|
|
58313
|
+
kind: "mutation",
|
|
58314
|
+
auth: "admin"
|
|
58315
|
+
}), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
|
|
58316
|
+
kind: "mutation",
|
|
58317
|
+
auth: "admin"
|
|
58318
|
+
}), method(object({
|
|
58319
|
+
actionId: string().min(1),
|
|
58320
|
+
action: ActionInputSchema.partial()
|
|
58321
|
+
}), object({ action: ActionEntrySchema }), {
|
|
58322
|
+
kind: "mutation",
|
|
58323
|
+
auth: "admin"
|
|
58324
|
+
}), method(ActionIdInput, _void(), {
|
|
58325
|
+
kind: "mutation",
|
|
58326
|
+
auth: "admin"
|
|
58327
|
+
}), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
|
|
58328
|
+
role: "viewer",
|
|
58329
|
+
auth: "protected",
|
|
58330
|
+
kind: "query"
|
|
58331
|
+
}), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
|
|
58332
|
+
kind: "mutation",
|
|
58333
|
+
auth: "admin"
|
|
58334
|
+
});
|
|
57891
58335
|
/**
|
|
57892
58336
|
* Automation-control cap. Models HA `automation.*` entities on
|
|
57893
58337
|
* `DeviceType.Automation`. An automation is a trigger+condition+
|
|
@@ -64350,10 +64794,20 @@ var PetFeederDeviceStatusSchema = _enum([
|
|
|
64350
64794
|
]);
|
|
64351
64795
|
var gramsPortion = number().int().min(4).max(200);
|
|
64352
64796
|
var PetFeederStatusSchema = object({
|
|
64353
|
-
/** Food
|
|
64354
|
-
*
|
|
64355
|
-
* bowl
|
|
64797
|
+
/** Food in the TANK (hopper), as the feeder's own sensor reports it
|
|
64798
|
+
* (`state.food`). A coarse level code, not grams — `0` is empty. The
|
|
64799
|
+
* amount in the bowl is `bowlFood`. On dual-hopper models this is the
|
|
64800
|
+
* combined reading; per-hopper levels live in `food1`/`food2`. Null when
|
|
64801
|
+
* the device has not reported yet. */
|
|
64356
64802
|
foodLevel: number().nullable().meta(readingMeta("none")),
|
|
64803
|
+
/** Food in the BOWL as the feeder reports it (`state.bowl`), in the
|
|
64804
|
+
* feeder's own units. Null when the model reports none. */
|
|
64805
|
+
bowlFood: number().nullable().meta(readingMeta("none")),
|
|
64806
|
+
/** True while the pet is eating (`state.eating`). */
|
|
64807
|
+
eating: boolean(),
|
|
64808
|
+
/** Whether `markFoodReplenished` does anything on this model. When false
|
|
64809
|
+
* the client must not offer it. */
|
|
64810
|
+
supportsMarkRefilled: boolean(),
|
|
64357
64811
|
/** Hopper-1 food level (grams) on dual-hopper feeders; null on
|
|
64358
64812
|
* single-hopper models. */
|
|
64359
64813
|
food1: number().nullable().meta(readingMeta("none")),
|
|
@@ -64361,8 +64815,8 @@ var PetFeederStatusSchema = object({
|
|
|
64361
64815
|
* single-hopper models. */
|
|
64362
64816
|
food2: number().nullable().meta(readingMeta("none")),
|
|
64363
64817
|
/** Derived low-food flag — mirrors the HA `food_level` binary_sensor
|
|
64364
|
-
* (`device_class: problem`, on = low). True when the
|
|
64365
|
-
*
|
|
64818
|
+
* (`device_class: problem`, on = low). True when the tank reports empty,
|
|
64819
|
+
* or, on a dual-hopper model, when EITHER hopper is empty. */
|
|
64366
64820
|
lowFood: boolean(),
|
|
64367
64821
|
/** Battery charge 0..100 (%). Null on mains-powered models or when the
|
|
64368
64822
|
* device has no battery reading. */
|
|
@@ -64430,7 +64884,9 @@ var petFeederCapability = {
|
|
|
64430
64884
|
kind: "mutation",
|
|
64431
64885
|
auth: "protected"
|
|
64432
64886
|
}),
|
|
64433
|
-
/**
|
|
64887
|
+
/** Tell the PetKit cloud a hopper was refilled so it stops its low-food
|
|
64888
|
+
* reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
|
|
64889
|
+
* change any sensor reading. */
|
|
64434
64890
|
markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
|
|
64435
64891
|
kind: "mutation",
|
|
64436
64892
|
auth: "protected"
|
|
@@ -64503,6 +64959,7 @@ var petFeederCapability = {
|
|
|
64503
64959
|
*/
|
|
64504
64960
|
history: { fields: {
|
|
64505
64961
|
foodLevel: { kind: "gauge" },
|
|
64962
|
+
bowlFood: { kind: "gauge" },
|
|
64506
64963
|
food1: { kind: "gauge" },
|
|
64507
64964
|
food2: { kind: "gauge" },
|
|
64508
64965
|
batteryPower: {
|
|
@@ -72058,6 +72515,104 @@ Object.freeze({
|
|
|
72058
72515
|
access: "create",
|
|
72059
72516
|
role: "admin"
|
|
72060
72517
|
},
|
|
72518
|
+
"actions.create": {
|
|
72519
|
+
capName: "actions",
|
|
72520
|
+
capScope: "system",
|
|
72521
|
+
addonId: null,
|
|
72522
|
+
access: "create",
|
|
72523
|
+
role: "admin"
|
|
72524
|
+
},
|
|
72525
|
+
"actions.delete": {
|
|
72526
|
+
capName: "actions",
|
|
72527
|
+
capScope: "system",
|
|
72528
|
+
addonId: null,
|
|
72529
|
+
access: "delete",
|
|
72530
|
+
role: "admin"
|
|
72531
|
+
},
|
|
72532
|
+
"actions.get": {
|
|
72533
|
+
capName: "actions",
|
|
72534
|
+
capScope: "system",
|
|
72535
|
+
addonId: null,
|
|
72536
|
+
access: "view",
|
|
72537
|
+
role: "viewer"
|
|
72538
|
+
},
|
|
72539
|
+
"actions.getLinks": {
|
|
72540
|
+
capName: "actions",
|
|
72541
|
+
capScope: "system",
|
|
72542
|
+
addonId: null,
|
|
72543
|
+
access: "view",
|
|
72544
|
+
role: "admin"
|
|
72545
|
+
},
|
|
72546
|
+
"actions.getTraces": {
|
|
72547
|
+
capName: "actions",
|
|
72548
|
+
capScope: "system",
|
|
72549
|
+
addonId: null,
|
|
72550
|
+
access: "view",
|
|
72551
|
+
role: "admin"
|
|
72552
|
+
},
|
|
72553
|
+
"actions.list": {
|
|
72554
|
+
capName: "actions",
|
|
72555
|
+
capScope: "system",
|
|
72556
|
+
addonId: null,
|
|
72557
|
+
access: "view",
|
|
72558
|
+
role: "admin"
|
|
72559
|
+
},
|
|
72560
|
+
"actions.listForCamera": {
|
|
72561
|
+
capName: "actions",
|
|
72562
|
+
capScope: "system",
|
|
72563
|
+
addonId: null,
|
|
72564
|
+
access: "view",
|
|
72565
|
+
role: "viewer"
|
|
72566
|
+
},
|
|
72567
|
+
"actions.run": {
|
|
72568
|
+
capName: "actions",
|
|
72569
|
+
capScope: "system",
|
|
72570
|
+
addonId: null,
|
|
72571
|
+
access: "create",
|
|
72572
|
+
role: "viewer"
|
|
72573
|
+
},
|
|
72574
|
+
"actions.setLinks": {
|
|
72575
|
+
capName: "actions",
|
|
72576
|
+
capScope: "system",
|
|
72577
|
+
addonId: null,
|
|
72578
|
+
access: "create",
|
|
72579
|
+
role: "admin"
|
|
72580
|
+
},
|
|
72581
|
+
"actions.stop": {
|
|
72582
|
+
capName: "actions",
|
|
72583
|
+
capScope: "system",
|
|
72584
|
+
addonId: null,
|
|
72585
|
+
access: "create",
|
|
72586
|
+
role: "admin"
|
|
72587
|
+
},
|
|
72588
|
+
"actions.update": {
|
|
72589
|
+
capName: "actions",
|
|
72590
|
+
capScope: "system",
|
|
72591
|
+
addonId: null,
|
|
72592
|
+
access: "create",
|
|
72593
|
+
role: "admin"
|
|
72594
|
+
},
|
|
72595
|
+
"actionSource.listActions": {
|
|
72596
|
+
capName: "action-source",
|
|
72597
|
+
capScope: "system",
|
|
72598
|
+
addonId: null,
|
|
72599
|
+
access: "view",
|
|
72600
|
+
role: "admin"
|
|
72601
|
+
},
|
|
72602
|
+
"actionSource.runAction": {
|
|
72603
|
+
capName: "action-source",
|
|
72604
|
+
capScope: "system",
|
|
72605
|
+
addonId: null,
|
|
72606
|
+
access: "create",
|
|
72607
|
+
role: "admin"
|
|
72608
|
+
},
|
|
72609
|
+
"actionSource.stopAction": {
|
|
72610
|
+
capName: "action-source",
|
|
72611
|
+
capScope: "system",
|
|
72612
|
+
addonId: null,
|
|
72613
|
+
access: "create",
|
|
72614
|
+
role: "admin"
|
|
72615
|
+
},
|
|
72061
72616
|
"addonPages.listPages": {
|
|
72062
72617
|
capName: "addon-pages",
|
|
72063
72618
|
capScope: "system",
|
|
@@ -73339,6 +73894,13 @@ Object.freeze({
|
|
|
73339
73894
|
access: "view",
|
|
73340
73895
|
role: "admin"
|
|
73341
73896
|
},
|
|
73897
|
+
"coreBlocks.getAutomationRunState": {
|
|
73898
|
+
capName: "core-blocks",
|
|
73899
|
+
capScope: "system",
|
|
73900
|
+
addonId: null,
|
|
73901
|
+
access: "view",
|
|
73902
|
+
role: "admin"
|
|
73903
|
+
},
|
|
73342
73904
|
"coreBlocks.getBlockSettings": {
|
|
73343
73905
|
capName: "core-blocks",
|
|
73344
73906
|
capScope: "system",
|
|
@@ -73360,6 +73922,13 @@ Object.freeze({
|
|
|
73360
73922
|
access: "view",
|
|
73361
73923
|
role: "admin"
|
|
73362
73924
|
},
|
|
73925
|
+
"coreBlocks.getTraces": {
|
|
73926
|
+
capName: "core-blocks",
|
|
73927
|
+
capScope: "system",
|
|
73928
|
+
addonId: null,
|
|
73929
|
+
access: "view",
|
|
73930
|
+
role: "admin"
|
|
73931
|
+
},
|
|
73363
73932
|
"coreBlocks.getTypeDefs": {
|
|
73364
73933
|
capName: "core-blocks",
|
|
73365
73934
|
capScope: "system",
|
|
@@ -73395,6 +73964,13 @@ Object.freeze({
|
|
|
73395
73964
|
access: "create",
|
|
73396
73965
|
role: "admin"
|
|
73397
73966
|
},
|
|
73967
|
+
"coreBlocks.reportAutomationRun": {
|
|
73968
|
+
capName: "core-blocks",
|
|
73969
|
+
capScope: "system",
|
|
73970
|
+
addonId: null,
|
|
73971
|
+
access: "create",
|
|
73972
|
+
role: "admin"
|
|
73973
|
+
},
|
|
73398
73974
|
"coreBlocks.restart": {
|
|
73399
73975
|
capName: "core-blocks",
|
|
73400
73976
|
capScope: "system",
|
|
@@ -85549,6 +86125,8 @@ var ML = "model and training machinery — no device action";
|
|
|
85549
86125
|
var INTERNAL = "internal plumbing between addons — never operator-facing";
|
|
85550
86126
|
/** Every system-scoped cap, with why it is not a device's vocabulary. */
|
|
85551
86127
|
var SYSTEM_CAP_VOCABULARY = {
|
|
86128
|
+
"action-source": system(INTERNAL),
|
|
86129
|
+
actions: system("declared actions — run from the Actions page and from the cameras that link them"),
|
|
85552
86130
|
"addon-pages": system(UI_PLUMBING),
|
|
85553
86131
|
"addon-pages-source": system(UI_PLUMBING),
|
|
85554
86132
|
"addon-routes": system(UI_PLUMBING),
|
|
@@ -85745,6 +86323,31 @@ Object.freeze({
|
|
|
85745
86323
|
form: "single",
|
|
85746
86324
|
optional: false
|
|
85747
86325
|
}],
|
|
86326
|
+
"actions.get": [{
|
|
86327
|
+
name: "deviceId",
|
|
86328
|
+
form: "single",
|
|
86329
|
+
optional: true
|
|
86330
|
+
}],
|
|
86331
|
+
"actions.getLinks": [{
|
|
86332
|
+
name: "deviceId",
|
|
86333
|
+
form: "single",
|
|
86334
|
+
optional: false
|
|
86335
|
+
}],
|
|
86336
|
+
"actions.listForCamera": [{
|
|
86337
|
+
name: "deviceId",
|
|
86338
|
+
form: "single",
|
|
86339
|
+
optional: false
|
|
86340
|
+
}],
|
|
86341
|
+
"actions.run": [{
|
|
86342
|
+
name: "deviceId",
|
|
86343
|
+
form: "single",
|
|
86344
|
+
optional: true
|
|
86345
|
+
}],
|
|
86346
|
+
"actions.setLinks": [{
|
|
86347
|
+
name: "deviceId",
|
|
86348
|
+
form: "single",
|
|
86349
|
+
optional: false
|
|
86350
|
+
}],
|
|
85748
86351
|
"addonSettings.getDeviceSettings": [{
|
|
85749
86352
|
name: "deviceId",
|
|
85750
86353
|
form: "single",
|
|
@@ -90089,11 +90692,11 @@ var AlexaBrokerEntrySchema = object({
|
|
|
90089
90692
|
* operator's browser uses (alexa-cookie2 rewrites Amazon URLs to absolute
|
|
90090
90693
|
* `http://<proxyHost>:<proxyPort>/...`), so it is a directly-reachable host —
|
|
90091
90694
|
* the hub's LAN IP/hostname — NOT behind the hub's HTTPS data-plane mount.
|
|
90092
|
-
* Empty `proxyHost` ⇒
|
|
90093
|
-
* login-start time.
|
|
90695
|
+
* Empty `proxyHost` ⇒ the first LAN address of the hub's `appConnections`
|
|
90696
|
+
* role (local-network palette) at login-start time.
|
|
90094
90697
|
*/
|
|
90095
90698
|
var alexaProxyLoginSchema = object({
|
|
90096
|
-
proxyHost: string().default("").describe("Host/IP the browser uses to reach the login proxy (blank =
|
|
90699
|
+
proxyHost: string().default("").describe("Host/IP the browser uses to reach the login proxy (blank = the hub LAN address)"),
|
|
90097
90700
|
proxyPort: preprocess((v) => v === "" || v === null ? void 0 : v, number().int().min(1).max(65535).default(3456)).describe("Port the local Amazon-login proxy listens on (must be reachable)")
|
|
90098
90701
|
});
|
|
90099
90702
|
/**
|
|
@@ -131601,6 +132204,7 @@ var AlexaProxyLoginSession = class {
|
|
|
131601
132204
|
error: errMsg(err)
|
|
131602
132205
|
} });
|
|
131603
132206
|
}
|
|
132207
|
+
await stopProxy(remote, this.#logger, this.#opts.sessionId);
|
|
131604
132208
|
}
|
|
131605
132209
|
/** Mark cancelled + dispose. */
|
|
131606
132210
|
async cancel() {
|
|
@@ -131654,6 +132258,24 @@ var AlexaProxyLoginSession = class {
|
|
|
131654
132258
|
this.dispose();
|
|
131655
132259
|
}
|
|
131656
132260
|
};
|
|
132261
|
+
/** Close the login proxy so its port is free for the next session (idempotent). */
|
|
132262
|
+
function stopProxy(remote, logger, sessionId) {
|
|
132263
|
+
return new Promise((resolve) => {
|
|
132264
|
+
try {
|
|
132265
|
+
if (typeof remote.stopProxyServer !== "function") {
|
|
132266
|
+
resolve();
|
|
132267
|
+
return;
|
|
132268
|
+
}
|
|
132269
|
+
remote.stopProxyServer(() => resolve());
|
|
132270
|
+
} catch (err) {
|
|
132271
|
+
logger.warn("alexa proxy-login: stopProxyServer failed", { meta: {
|
|
132272
|
+
sessionId,
|
|
132273
|
+
error: errMsg(err)
|
|
132274
|
+
} });
|
|
132275
|
+
resolve();
|
|
132276
|
+
}
|
|
132277
|
+
});
|
|
132278
|
+
}
|
|
131657
132279
|
/**
|
|
131658
132280
|
* Lines emitted by alexa-cookie2 that contain raw credential material
|
|
131659
132281
|
* (cookie values, CSRF tokens, bearer tokens). Any log line matching this
|
|
@@ -131681,15 +132303,62 @@ function isStringOrObject(value) {
|
|
|
131681
132303
|
return typeof value === "string" || typeof value === "object" && value !== null;
|
|
131682
132304
|
}
|
|
131683
132305
|
//#endregion
|
|
132306
|
+
//#region src/alexa-proxy-host.ts
|
|
132307
|
+
/** Typed refusal: names WHY no proxy host could be chosen. */
|
|
132308
|
+
var AlexaProxyHostError = class extends Error {
|
|
132309
|
+
reason;
|
|
132310
|
+
constructor(reason, message, options) {
|
|
132311
|
+
super(message, options);
|
|
132312
|
+
this.name = "AlexaProxyHostError";
|
|
132313
|
+
this.reason = reason;
|
|
132314
|
+
}
|
|
132315
|
+
};
|
|
132316
|
+
var PRIVATE_IPV4 = /^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\.)/;
|
|
132317
|
+
var IPV4_LITERAL = /^\d{1,3}(\.\d{1,3}){3}$/;
|
|
132318
|
+
/** A host a browser on the hub's network reaches directly: private IPv4, `.local`, or a bare name. */
|
|
132319
|
+
function isLanHost(host) {
|
|
132320
|
+
if (host.length === 0) return false;
|
|
132321
|
+
if (IPV4_LITERAL.test(host)) return PRIVATE_IPV4.test(host);
|
|
132322
|
+
if (host.includes(":")) return false;
|
|
132323
|
+
return host.endsWith(".local") || !host.includes(".");
|
|
132324
|
+
}
|
|
132325
|
+
function lanHostOf(address) {
|
|
132326
|
+
try {
|
|
132327
|
+
const host = new URL(address).hostname;
|
|
132328
|
+
return isLanHost(host) && host !== "localhost" ? host : null;
|
|
132329
|
+
} catch {
|
|
132330
|
+
return null;
|
|
132331
|
+
}
|
|
132332
|
+
}
|
|
132333
|
+
async function resolveProxyHost(input) {
|
|
132334
|
+
const override = input.override.trim();
|
|
132335
|
+
if (override.length > 0) return override;
|
|
132336
|
+
let addresses;
|
|
132337
|
+
try {
|
|
132338
|
+
addresses = await input.readAppConnections();
|
|
132339
|
+
} catch (err) {
|
|
132340
|
+
input.logger.warn("alexa proxy-login: hub addresses unavailable from local-network", { meta: { error: errMsg(err) } });
|
|
132341
|
+
throw new AlexaProxyHostError("palette-unavailable", "alexa proxy-login: the hub address list is unavailable — set proxyHost in settings", { cause: err });
|
|
132342
|
+
}
|
|
132343
|
+
for (const address of addresses) {
|
|
132344
|
+
const host = lanHostOf(address);
|
|
132345
|
+
if (host !== null) return host;
|
|
132346
|
+
}
|
|
132347
|
+
input.logger.warn("alexa proxy-login: no LAN address among the hub appConnections", { meta: { addresses: [...addresses] } });
|
|
132348
|
+
throw new AlexaProxyHostError("no-lan-address", "alexa proxy-login: the hub has no LAN address the browser can open directly — set proxyHost in settings");
|
|
132349
|
+
}
|
|
132350
|
+
//#endregion
|
|
131684
132351
|
//#region src/alexa-proxy-login-manager.ts
|
|
131685
132352
|
var AlexaProxyLoginManager = class {
|
|
131686
132353
|
#logger;
|
|
131687
132354
|
#getProxyConfig;
|
|
132355
|
+
#getAppConnectionAddresses;
|
|
131688
132356
|
#session = null;
|
|
131689
132357
|
#captured = /* @__PURE__ */ new Map();
|
|
131690
132358
|
constructor(deps) {
|
|
131691
132359
|
this.#logger = deps.logger;
|
|
131692
132360
|
this.#getProxyConfig = deps.getProxyConfig;
|
|
132361
|
+
this.#getAppConnectionAddresses = deps.getAppConnectionAddresses;
|
|
131693
132362
|
}
|
|
131694
132363
|
/**
|
|
131695
132364
|
* Start a fresh proxy-login. Cancels any in-flight session first (single
|
|
@@ -131698,8 +132367,11 @@ var AlexaProxyLoginManager = class {
|
|
|
131698
132367
|
async start(amazonDomain) {
|
|
131699
132368
|
await this.cancelActive();
|
|
131700
132369
|
const { proxyHost, proxyPort } = this.#getProxyConfig();
|
|
131701
|
-
const host =
|
|
131702
|
-
|
|
132370
|
+
const host = await resolveProxyHost({
|
|
132371
|
+
override: proxyHost,
|
|
132372
|
+
readAppConnections: this.#getAppConnectionAddresses,
|
|
132373
|
+
logger: this.#logger
|
|
132374
|
+
});
|
|
131703
132375
|
const sessionId = randomBytes(9).toString("base64url");
|
|
131704
132376
|
const session = new AlexaProxyLoginSession({
|
|
131705
132377
|
sessionId,
|
|
@@ -131767,19 +132439,6 @@ var AlexaProxyLoginManager = class {
|
|
|
131767
132439
|
}
|
|
131768
132440
|
};
|
|
131769
132441
|
/**
|
|
131770
|
-
* Best-effort primary non-loopback IPv4 of the host. Used when the operator
|
|
131771
|
-
* leaves `proxyHost` blank. Returns '' when none found (then the operator must
|
|
131772
|
-
* set it explicitly — common on multi-homed/containerised hosts).
|
|
131773
|
-
*/
|
|
131774
|
-
function detectPrimaryIpv4() {
|
|
131775
|
-
const ifaces = networkInterfaces();
|
|
131776
|
-
for (const addrs of Object.values(ifaces)) {
|
|
131777
|
-
if (addrs === void 0) continue;
|
|
131778
|
-
for (const addr of addrs) if (addr.family === "IPv4" && !addr.internal && addr.address.length > 0) return addr.address;
|
|
131779
|
-
}
|
|
131780
|
-
return "";
|
|
131781
|
-
}
|
|
131782
|
-
/**
|
|
131783
132442
|
* Serialize the captured `cookieData` blob into the single STRING the broker
|
|
131784
132443
|
* connection stores in its `cookie` field. `AlexaRemoteClient` already parses a
|
|
131785
132444
|
* JSON-object cookie string back into the richer object form on init, so we
|
|
@@ -135476,7 +136135,8 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
135476
136135
|
proxyHost: cfg.proxyHost,
|
|
135477
136136
|
proxyPort: cfg.proxyPort
|
|
135478
136137
|
};
|
|
135479
|
-
}
|
|
136138
|
+
},
|
|
136139
|
+
getAppConnectionAddresses: async () => (await this.ctx.api.localNetwork.getAddresses.query()).roles.appConnections.resolved
|
|
135480
136140
|
});
|
|
135481
136141
|
this.ctx.logger.info("Alexa: import addon initialised", { meta: { brokerCount: this.activeBrokers.length } });
|
|
135482
136142
|
await this.reconcileIntegrationsToBrokers();
|