@camstack/addon-provider-enpal 0.1.8 → 0.1.9
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 +6 -0
- package/dist/addon.js +92 -62
- package/dist/addon.mjs +92 -62
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.9] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Copying an integration into its record reads the leftover rows through the migration-only door and resolves their secrets in this addon
|
|
15
|
+
|
|
10
16
|
## [0.1.8] - 2026-11-04
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -5981,7 +5981,7 @@ var ZodIssueCode = {
|
|
|
5981
5981
|
var ZodFirstPartyTypeKind;
|
|
5982
5982
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5983
5983
|
//#endregion
|
|
5984
|
-
//#region ../types/dist/sleep-
|
|
5984
|
+
//#region ../types/dist/sleep-9TIHFj1w.mjs
|
|
5985
5985
|
/**
|
|
5986
5986
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5987
5987
|
* window to float samples (D455).
|
|
@@ -7925,10 +7925,9 @@ var BaseAddon = class {
|
|
|
7925
7925
|
*
|
|
7926
7926
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7927
7927
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7928
|
-
* SHAPE of the form only — the values live
|
|
7929
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7928
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7930
7929
|
*/
|
|
7931
|
-
async
|
|
7930
|
+
async integrationFormSchema(_instance) {
|
|
7932
7931
|
return null;
|
|
7933
7932
|
}
|
|
7934
7933
|
/**
|
|
@@ -7974,7 +7973,7 @@ var BaseAddon = class {
|
|
|
7974
7973
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7975
7974
|
*/
|
|
7976
7975
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
7977
|
-
const scoped = await this.
|
|
7976
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
7978
7977
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
7979
7978
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
7980
7979
|
}
|
|
@@ -11934,7 +11933,7 @@ object({
|
|
|
11934
11933
|
params: record(string(), union([string(), number()])).optional()
|
|
11935
11934
|
});
|
|
11936
11935
|
//#endregion
|
|
11937
|
-
//#region ../types/dist/composition-
|
|
11936
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11938
11937
|
/**
|
|
11939
11938
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11940
11939
|
*
|
|
@@ -14642,20 +14641,28 @@ async function moveIntegrationRowsToRecords(ports, spec) {
|
|
|
14642
14641
|
};
|
|
14643
14642
|
}
|
|
14644
14643
|
/**
|
|
14645
|
-
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416)
|
|
14646
|
-
* from its own process, so the
|
|
14647
|
-
* `
|
|
14648
|
-
*
|
|
14649
|
-
*
|
|
14650
|
-
|
|
14644
|
+
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416). An addon runs the move
|
|
14645
|
+
* from its own process, so the leftover rows are reached through the migration-only door
|
|
14646
|
+
* (`migrations.readLegacyIntegrationRows` / `clearLegacyIntegrationRows`) on the hub's connection.
|
|
14647
|
+
* The door returns markers. This process is the owner, so it resolves and releases them with
|
|
14648
|
+
* `resolveSecretMarkers` — the same call the old settings door used — and only then asks the
|
|
14649
|
+
* door to delete the rows. The addon's own record kind is its records handle.
|
|
14650
|
+
*/
|
|
14651
|
+
async function resolvedRows(stored, secrets) {
|
|
14652
|
+
if (secrets === null) return stored;
|
|
14653
|
+
const { value, unresolved } = await resolveSecretMarkers(stored, secrets);
|
|
14654
|
+
if (unresolved.length > 0) throw new Error(`${unresolved.length} secret(s) cannot be read from the keychain`);
|
|
14655
|
+
return value;
|
|
14656
|
+
}
|
|
14651
14657
|
function integrationRowsPortsOverApi(deps) {
|
|
14658
|
+
const secrets = deps.secrets ?? null;
|
|
14652
14659
|
return {
|
|
14653
14660
|
warn: (message) => deps.log.warn(message),
|
|
14654
14661
|
listIntegrations: async (addonId) => (await deps.api.list()).filter((i) => i.addonId === addonId).map((i) => ({
|
|
14655
14662
|
id: i.id,
|
|
14656
14663
|
name: i.name
|
|
14657
14664
|
})),
|
|
14658
|
-
readRows: (integrationId) => deps.api.
|
|
14665
|
+
readRows: async (integrationId) => resolvedRows(await deps.api.readLegacyIntegrationRows({ integrationId }), secrets),
|
|
14659
14666
|
putRecord: async (id, body) => {
|
|
14660
14667
|
if (await deps.records.get(id) !== null) return "exists";
|
|
14661
14668
|
try {
|
|
@@ -14674,7 +14681,9 @@ function integrationRowsPortsOverApi(deps) {
|
|
|
14674
14681
|
await deps.records.delete(id);
|
|
14675
14682
|
},
|
|
14676
14683
|
clearRows: async (integrationId) => {
|
|
14677
|
-
await deps.api.
|
|
14684
|
+
const refs = collectSecretMarkerRefs(await deps.api.readLegacyIntegrationRows({ integrationId }));
|
|
14685
|
+
if (refs.length > 0 && secrets !== null) await secrets.delete(refs);
|
|
14686
|
+
await deps.api.clearLegacyIntegrationRows({ integrationId });
|
|
14678
14687
|
}
|
|
14679
14688
|
};
|
|
14680
14689
|
}
|
|
@@ -14742,6 +14751,7 @@ function integrationRowsMigration(spec) {
|
|
|
14742
14751
|
function integrationRowsPortsOver(deps) {
|
|
14743
14752
|
return integrationRowsPortsOverApi({
|
|
14744
14753
|
api: deps.api,
|
|
14754
|
+
...deps.secrets !== void 0 ? { secrets: deps.secrets } : {},
|
|
14745
14755
|
records: deps.records.kind({
|
|
14746
14756
|
name: deps.kind.name,
|
|
14747
14757
|
declaration: deps.kind.declaration,
|
|
@@ -14753,8 +14763,8 @@ function integrationRowsPortsOver(deps) {
|
|
|
14753
14763
|
function defaultIntegrationApiOver(api) {
|
|
14754
14764
|
return {
|
|
14755
14765
|
list: () => api.integrations.list.query(),
|
|
14756
|
-
|
|
14757
|
-
|
|
14766
|
+
readLegacyIntegrationRows: (input) => api.migrations.readLegacyIntegrationRows.query(input),
|
|
14767
|
+
clearLegacyIntegrationRows: (input) => api.migrations.clearLegacyIntegrationRows.mutate(input),
|
|
14758
14768
|
listDevices: (addonId) => api.deviceManager.listAll.query({
|
|
14759
14769
|
addonId,
|
|
14760
14770
|
projection: "slim"
|
|
@@ -50432,6 +50442,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
50432
50442
|
runsFound: number().int().nonnegative(),
|
|
50433
50443
|
pausesFound: number().int().nonnegative(),
|
|
50434
50444
|
longestPauseMs: number().nonnegative(),
|
|
50445
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
50446
|
+
pulsesFound: number().int().nonnegative(),
|
|
50447
|
+
longestPulseMs: number().nonnegative(),
|
|
50435
50448
|
idleFloorW: number(),
|
|
50436
50449
|
noiseCeilingW: number(),
|
|
50437
50450
|
plateauW: number(),
|
|
@@ -56298,11 +56311,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
56298
56311
|
info: record(string(), unknown()).optional(),
|
|
56299
56312
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
56300
56313
|
originStableId: string().optional(),
|
|
56301
|
-
settings: record(string(), unknown()).optional()
|
|
56302
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
56303
|
-
* the keychain, not the table (D787). */
|
|
56304
|
-
secretKeys: array(string()).optional(),
|
|
56305
|
-
skipRestart: boolean().optional()
|
|
56314
|
+
settings: record(string(), unknown()).optional()
|
|
56306
56315
|
});
|
|
56307
56316
|
var UpdateIntegrationInputSchema = object({
|
|
56308
56317
|
id: string(),
|
|
@@ -56323,22 +56332,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56323
56332
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
56324
56333
|
kind: "mutation",
|
|
56325
56334
|
auth: "admin"
|
|
56326
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
56327
|
-
id: string(),
|
|
56328
|
-
settings: record(string(), unknown()),
|
|
56329
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
56330
|
-
secretKeys: array(string()).optional(),
|
|
56331
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
56332
|
-
skipRestart: boolean().optional()
|
|
56333
|
-
}), object({ success: boolean() }), {
|
|
56334
|
-
kind: "mutation",
|
|
56335
|
-
auth: "admin"
|
|
56336
|
-
}), method(object({ id: string() }), object({
|
|
56337
|
-
rows: number().int().nonnegative(),
|
|
56338
|
-
secrets: number().int().nonnegative()
|
|
56339
|
-
}), {
|
|
56340
|
-
kind: "mutation",
|
|
56341
|
-
auth: "admin"
|
|
56342
56335
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
56343
56336
|
addonId: string(),
|
|
56344
56337
|
settings: record(string(), unknown())
|
|
@@ -59315,6 +59308,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
59315
59308
|
}), {
|
|
59316
59309
|
kind: "mutation",
|
|
59317
59310
|
auth: "admin"
|
|
59311
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
59312
|
+
rows: number().int().nonnegative(),
|
|
59313
|
+
secrets: number().int().nonnegative()
|
|
59314
|
+
}), {
|
|
59315
|
+
kind: "mutation",
|
|
59316
|
+
auth: "admin"
|
|
59317
|
+
}), method(object({
|
|
59318
|
+
integrationId: string().min(1),
|
|
59319
|
+
patch: record(string(), unknown())
|
|
59320
|
+
}), object({ written: boolean() }), {
|
|
59321
|
+
kind: "mutation",
|
|
59322
|
+
auth: "admin"
|
|
59323
|
+
}), method(object({
|
|
59324
|
+
integrationId: string().min(1),
|
|
59325
|
+
keys: array(string().min(1))
|
|
59326
|
+
}), object({
|
|
59327
|
+
present: boolean(),
|
|
59328
|
+
removed: record(string(), unknown())
|
|
59329
|
+
}), {
|
|
59330
|
+
kind: "mutation",
|
|
59331
|
+
auth: "admin"
|
|
59318
59332
|
});
|
|
59319
59333
|
method(object({
|
|
59320
59334
|
sourceUrl: string(),
|
|
@@ -80733,11 +80747,15 @@ discriminatedUnion("kind", [object({
|
|
|
80733
80747
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
80734
80748
|
* near F. A device that is not near one level most of the time has no floor.
|
|
80735
80749
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
80736
|
-
* 3.
|
|
80737
|
-
*
|
|
80750
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
80751
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
80752
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
80753
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
80754
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
80755
|
+
* is no run.
|
|
80738
80756
|
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
80739
|
-
* grid a person would type; stop dwell covers the longest pause
|
|
80740
|
-
* covers the longest standby blip above T.
|
|
80757
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
80758
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
80741
80759
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
80742
80760
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
80743
80761
|
*/
|
|
@@ -80748,6 +80766,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
80748
80766
|
30 * SECOND_MS;
|
|
80749
80767
|
60 * SECOND_MS;
|
|
80750
80768
|
20 * MINUTE_MS$1;
|
|
80769
|
+
90 * SECOND_MS;
|
|
80770
|
+
30 * MINUTE_MS$1;
|
|
80771
|
+
10 * MINUTE_MS$1;
|
|
80772
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
80751
80773
|
10 * SECOND_MS;
|
|
80752
80774
|
10 * SECOND_MS;
|
|
80753
80775
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -86032,13 +86054,6 @@ Object.freeze({
|
|
|
86032
86054
|
access: "create",
|
|
86033
86055
|
role: "admin"
|
|
86034
86056
|
},
|
|
86035
|
-
"integrations.clearSettings": {
|
|
86036
|
-
capName: "integrations",
|
|
86037
|
-
capScope: "system",
|
|
86038
|
-
addonId: null,
|
|
86039
|
-
access: "delete",
|
|
86040
|
-
role: "admin"
|
|
86041
|
-
},
|
|
86042
86057
|
"integrations.create": {
|
|
86043
86058
|
capName: "integrations",
|
|
86044
86059
|
capScope: "system",
|
|
@@ -86074,13 +86089,6 @@ Object.freeze({
|
|
|
86074
86089
|
access: "view",
|
|
86075
86090
|
role: "admin"
|
|
86076
86091
|
},
|
|
86077
|
-
"integrations.getSettings": {
|
|
86078
|
-
capName: "integrations",
|
|
86079
|
-
capScope: "system",
|
|
86080
|
-
addonId: null,
|
|
86081
|
-
access: "view",
|
|
86082
|
-
role: "admin"
|
|
86083
|
-
},
|
|
86084
86092
|
"integrations.list": {
|
|
86085
86093
|
capName: "integrations",
|
|
86086
86094
|
capScope: "system",
|
|
@@ -86088,13 +86096,6 @@ Object.freeze({
|
|
|
86088
86096
|
access: "view",
|
|
86089
86097
|
role: "admin"
|
|
86090
86098
|
},
|
|
86091
|
-
"integrations.setSettings": {
|
|
86092
|
-
capName: "integrations",
|
|
86093
|
-
capScope: "system",
|
|
86094
|
-
addonId: null,
|
|
86095
|
-
access: "create",
|
|
86096
|
-
role: "admin"
|
|
86097
|
-
},
|
|
86098
86099
|
"integrations.testConnection": {
|
|
86099
86100
|
capName: "integrations",
|
|
86100
86101
|
capScope: "system",
|
|
@@ -87089,6 +87090,13 @@ Object.freeze({
|
|
|
87089
87090
|
access: "view",
|
|
87090
87091
|
role: "admin"
|
|
87091
87092
|
},
|
|
87093
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
87094
|
+
capName: "migrations",
|
|
87095
|
+
capScope: "system",
|
|
87096
|
+
addonId: null,
|
|
87097
|
+
access: "delete",
|
|
87098
|
+
role: "admin"
|
|
87099
|
+
},
|
|
87092
87100
|
"migrations.declareOwner": {
|
|
87093
87101
|
capName: "migrations",
|
|
87094
87102
|
capScope: "system",
|
|
@@ -87096,6 +87104,13 @@ Object.freeze({
|
|
|
87096
87104
|
access: "create",
|
|
87097
87105
|
role: "admin"
|
|
87098
87106
|
},
|
|
87107
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
87108
|
+
capName: "migrations",
|
|
87109
|
+
capScope: "system",
|
|
87110
|
+
addonId: null,
|
|
87111
|
+
access: "delete",
|
|
87112
|
+
role: "admin"
|
|
87113
|
+
},
|
|
87099
87114
|
"migrations.dryRun": {
|
|
87100
87115
|
capName: "migrations",
|
|
87101
87116
|
capScope: "system",
|
|
@@ -87131,6 +87146,20 @@ Object.freeze({
|
|
|
87131
87146
|
access: "create",
|
|
87132
87147
|
role: "admin"
|
|
87133
87148
|
},
|
|
87149
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
87150
|
+
capName: "migrations",
|
|
87151
|
+
capScope: "system",
|
|
87152
|
+
addonId: null,
|
|
87153
|
+
access: "create",
|
|
87154
|
+
role: "admin"
|
|
87155
|
+
},
|
|
87156
|
+
"migrations.readLegacyIntegrationRows": {
|
|
87157
|
+
capName: "migrations",
|
|
87158
|
+
capScope: "system",
|
|
87159
|
+
addonId: null,
|
|
87160
|
+
access: "view",
|
|
87161
|
+
role: "admin"
|
|
87162
|
+
},
|
|
87134
87163
|
"migrations.restorePreMigration": {
|
|
87135
87164
|
capName: "migrations",
|
|
87136
87165
|
capScope: "system",
|
|
@@ -99232,6 +99261,7 @@ var EnpalProviderAddon = class extends BaseDeviceProvider {
|
|
|
99232
99261
|
fixtureMark: "host"
|
|
99233
99262
|
})], integrationRowsPortsOver({
|
|
99234
99263
|
api: defaultIntegrationApiOver(this.ctx.api),
|
|
99264
|
+
secrets: this.ctx.secrets ?? null,
|
|
99235
99265
|
records,
|
|
99236
99266
|
kind: enpalConnectionsEntryKind,
|
|
99237
99267
|
warn: (message) => this.ctx.logger.warn(message)
|
package/dist/addon.mjs
CHANGED
|
@@ -5980,7 +5980,7 @@ var ZodIssueCode = {
|
|
|
5980
5980
|
var ZodFirstPartyTypeKind;
|
|
5981
5981
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5982
5982
|
//#endregion
|
|
5983
|
-
//#region ../types/dist/sleep-
|
|
5983
|
+
//#region ../types/dist/sleep-9TIHFj1w.mjs
|
|
5984
5984
|
/**
|
|
5985
5985
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5986
5986
|
* window to float samples (D455).
|
|
@@ -7924,10 +7924,9 @@ var BaseAddon = class {
|
|
|
7924
7924
|
*
|
|
7925
7925
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7926
7926
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7927
|
-
* SHAPE of the form only — the values live
|
|
7928
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7927
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7929
7928
|
*/
|
|
7930
|
-
async
|
|
7929
|
+
async integrationFormSchema(_instance) {
|
|
7931
7930
|
return null;
|
|
7932
7931
|
}
|
|
7933
7932
|
/**
|
|
@@ -7973,7 +7972,7 @@ var BaseAddon = class {
|
|
|
7973
7972
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
7974
7973
|
*/
|
|
7975
7974
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
7976
|
-
const scoped = await this.
|
|
7975
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
7977
7976
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
7978
7977
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
7979
7978
|
}
|
|
@@ -11933,7 +11932,7 @@ object({
|
|
|
11933
11932
|
params: record(string(), union([string(), number()])).optional()
|
|
11934
11933
|
});
|
|
11935
11934
|
//#endregion
|
|
11936
|
-
//#region ../types/dist/composition-
|
|
11935
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11937
11936
|
/**
|
|
11938
11937
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11939
11938
|
*
|
|
@@ -14641,20 +14640,28 @@ async function moveIntegrationRowsToRecords(ports, spec) {
|
|
|
14641
14640
|
};
|
|
14642
14641
|
}
|
|
14643
14642
|
/**
|
|
14644
|
-
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416)
|
|
14645
|
-
* from its own process, so the
|
|
14646
|
-
* `
|
|
14647
|
-
*
|
|
14648
|
-
*
|
|
14649
|
-
|
|
14643
|
+
* The addon-side ports of `moveIntegrationRowsToRecords` (planning#416). An addon runs the move
|
|
14644
|
+
* from its own process, so the leftover rows are reached through the migration-only door
|
|
14645
|
+
* (`migrations.readLegacyIntegrationRows` / `clearLegacyIntegrationRows`) on the hub's connection.
|
|
14646
|
+
* The door returns markers. This process is the owner, so it resolves and releases them with
|
|
14647
|
+
* `resolveSecretMarkers` — the same call the old settings door used — and only then asks the
|
|
14648
|
+
* door to delete the rows. The addon's own record kind is its records handle.
|
|
14649
|
+
*/
|
|
14650
|
+
async function resolvedRows(stored, secrets) {
|
|
14651
|
+
if (secrets === null) return stored;
|
|
14652
|
+
const { value, unresolved } = await resolveSecretMarkers(stored, secrets);
|
|
14653
|
+
if (unresolved.length > 0) throw new Error(`${unresolved.length} secret(s) cannot be read from the keychain`);
|
|
14654
|
+
return value;
|
|
14655
|
+
}
|
|
14650
14656
|
function integrationRowsPortsOverApi(deps) {
|
|
14657
|
+
const secrets = deps.secrets ?? null;
|
|
14651
14658
|
return {
|
|
14652
14659
|
warn: (message) => deps.log.warn(message),
|
|
14653
14660
|
listIntegrations: async (addonId) => (await deps.api.list()).filter((i) => i.addonId === addonId).map((i) => ({
|
|
14654
14661
|
id: i.id,
|
|
14655
14662
|
name: i.name
|
|
14656
14663
|
})),
|
|
14657
|
-
readRows: (integrationId) => deps.api.
|
|
14664
|
+
readRows: async (integrationId) => resolvedRows(await deps.api.readLegacyIntegrationRows({ integrationId }), secrets),
|
|
14658
14665
|
putRecord: async (id, body) => {
|
|
14659
14666
|
if (await deps.records.get(id) !== null) return "exists";
|
|
14660
14667
|
try {
|
|
@@ -14673,7 +14680,9 @@ function integrationRowsPortsOverApi(deps) {
|
|
|
14673
14680
|
await deps.records.delete(id);
|
|
14674
14681
|
},
|
|
14675
14682
|
clearRows: async (integrationId) => {
|
|
14676
|
-
await deps.api.
|
|
14683
|
+
const refs = collectSecretMarkerRefs(await deps.api.readLegacyIntegrationRows({ integrationId }));
|
|
14684
|
+
if (refs.length > 0 && secrets !== null) await secrets.delete(refs);
|
|
14685
|
+
await deps.api.clearLegacyIntegrationRows({ integrationId });
|
|
14677
14686
|
}
|
|
14678
14687
|
};
|
|
14679
14688
|
}
|
|
@@ -14741,6 +14750,7 @@ function integrationRowsMigration(spec) {
|
|
|
14741
14750
|
function integrationRowsPortsOver(deps) {
|
|
14742
14751
|
return integrationRowsPortsOverApi({
|
|
14743
14752
|
api: deps.api,
|
|
14753
|
+
...deps.secrets !== void 0 ? { secrets: deps.secrets } : {},
|
|
14744
14754
|
records: deps.records.kind({
|
|
14745
14755
|
name: deps.kind.name,
|
|
14746
14756
|
declaration: deps.kind.declaration,
|
|
@@ -14752,8 +14762,8 @@ function integrationRowsPortsOver(deps) {
|
|
|
14752
14762
|
function defaultIntegrationApiOver(api) {
|
|
14753
14763
|
return {
|
|
14754
14764
|
list: () => api.integrations.list.query(),
|
|
14755
|
-
|
|
14756
|
-
|
|
14765
|
+
readLegacyIntegrationRows: (input) => api.migrations.readLegacyIntegrationRows.query(input),
|
|
14766
|
+
clearLegacyIntegrationRows: (input) => api.migrations.clearLegacyIntegrationRows.mutate(input),
|
|
14757
14767
|
listDevices: (addonId) => api.deviceManager.listAll.query({
|
|
14758
14768
|
addonId,
|
|
14759
14769
|
projection: "slim"
|
|
@@ -50431,6 +50441,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
50431
50441
|
runsFound: number().int().nonnegative(),
|
|
50432
50442
|
pausesFound: number().int().nonnegative(),
|
|
50433
50443
|
longestPauseMs: number().nonnegative(),
|
|
50444
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
50445
|
+
pulsesFound: number().int().nonnegative(),
|
|
50446
|
+
longestPulseMs: number().nonnegative(),
|
|
50434
50447
|
idleFloorW: number(),
|
|
50435
50448
|
noiseCeilingW: number(),
|
|
50436
50449
|
plateauW: number(),
|
|
@@ -56297,11 +56310,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
56297
56310
|
info: record(string(), unknown()).optional(),
|
|
56298
56311
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
56299
56312
|
originStableId: string().optional(),
|
|
56300
|
-
settings: record(string(), unknown()).optional()
|
|
56301
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
56302
|
-
* the keychain, not the table (D787). */
|
|
56303
|
-
secretKeys: array(string()).optional(),
|
|
56304
|
-
skipRestart: boolean().optional()
|
|
56313
|
+
settings: record(string(), unknown()).optional()
|
|
56305
56314
|
});
|
|
56306
56315
|
var UpdateIntegrationInputSchema = object({
|
|
56307
56316
|
id: string(),
|
|
@@ -56322,22 +56331,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56322
56331
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
56323
56332
|
kind: "mutation",
|
|
56324
56333
|
auth: "admin"
|
|
56325
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
56326
|
-
id: string(),
|
|
56327
|
-
settings: record(string(), unknown()),
|
|
56328
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
56329
|
-
secretKeys: array(string()).optional(),
|
|
56330
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
56331
|
-
skipRestart: boolean().optional()
|
|
56332
|
-
}), object({ success: boolean() }), {
|
|
56333
|
-
kind: "mutation",
|
|
56334
|
-
auth: "admin"
|
|
56335
|
-
}), method(object({ id: string() }), object({
|
|
56336
|
-
rows: number().int().nonnegative(),
|
|
56337
|
-
secrets: number().int().nonnegative()
|
|
56338
|
-
}), {
|
|
56339
|
-
kind: "mutation",
|
|
56340
|
-
auth: "admin"
|
|
56341
56334
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
56342
56335
|
addonId: string(),
|
|
56343
56336
|
settings: record(string(), unknown())
|
|
@@ -59314,6 +59307,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
59314
59307
|
}), {
|
|
59315
59308
|
kind: "mutation",
|
|
59316
59309
|
auth: "admin"
|
|
59310
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
59311
|
+
rows: number().int().nonnegative(),
|
|
59312
|
+
secrets: number().int().nonnegative()
|
|
59313
|
+
}), {
|
|
59314
|
+
kind: "mutation",
|
|
59315
|
+
auth: "admin"
|
|
59316
|
+
}), method(object({
|
|
59317
|
+
integrationId: string().min(1),
|
|
59318
|
+
patch: record(string(), unknown())
|
|
59319
|
+
}), object({ written: boolean() }), {
|
|
59320
|
+
kind: "mutation",
|
|
59321
|
+
auth: "admin"
|
|
59322
|
+
}), method(object({
|
|
59323
|
+
integrationId: string().min(1),
|
|
59324
|
+
keys: array(string().min(1))
|
|
59325
|
+
}), object({
|
|
59326
|
+
present: boolean(),
|
|
59327
|
+
removed: record(string(), unknown())
|
|
59328
|
+
}), {
|
|
59329
|
+
kind: "mutation",
|
|
59330
|
+
auth: "admin"
|
|
59317
59331
|
});
|
|
59318
59332
|
method(object({
|
|
59319
59333
|
sourceUrl: string(),
|
|
@@ -80732,11 +80746,15 @@ discriminatedUnion("kind", [object({
|
|
|
80732
80746
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
80733
80747
|
* near F. A device that is not near one level most of the time has no floor.
|
|
80734
80748
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
80735
|
-
* 3.
|
|
80736
|
-
*
|
|
80749
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
80750
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
80751
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
80752
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
80753
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
80754
|
+
* is no run.
|
|
80737
80755
|
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
80738
|
-
* grid a person would type; stop dwell covers the longest pause
|
|
80739
|
-
* covers the longest standby blip above T.
|
|
80756
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
80757
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
80740
80758
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
80741
80759
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
80742
80760
|
*/
|
|
@@ -80747,6 +80765,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
80747
80765
|
30 * SECOND_MS;
|
|
80748
80766
|
60 * SECOND_MS;
|
|
80749
80767
|
20 * MINUTE_MS$1;
|
|
80768
|
+
90 * SECOND_MS;
|
|
80769
|
+
30 * MINUTE_MS$1;
|
|
80770
|
+
10 * MINUTE_MS$1;
|
|
80771
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
80750
80772
|
10 * SECOND_MS;
|
|
80751
80773
|
10 * SECOND_MS;
|
|
80752
80774
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -86031,13 +86053,6 @@ Object.freeze({
|
|
|
86031
86053
|
access: "create",
|
|
86032
86054
|
role: "admin"
|
|
86033
86055
|
},
|
|
86034
|
-
"integrations.clearSettings": {
|
|
86035
|
-
capName: "integrations",
|
|
86036
|
-
capScope: "system",
|
|
86037
|
-
addonId: null,
|
|
86038
|
-
access: "delete",
|
|
86039
|
-
role: "admin"
|
|
86040
|
-
},
|
|
86041
86056
|
"integrations.create": {
|
|
86042
86057
|
capName: "integrations",
|
|
86043
86058
|
capScope: "system",
|
|
@@ -86073,13 +86088,6 @@ Object.freeze({
|
|
|
86073
86088
|
access: "view",
|
|
86074
86089
|
role: "admin"
|
|
86075
86090
|
},
|
|
86076
|
-
"integrations.getSettings": {
|
|
86077
|
-
capName: "integrations",
|
|
86078
|
-
capScope: "system",
|
|
86079
|
-
addonId: null,
|
|
86080
|
-
access: "view",
|
|
86081
|
-
role: "admin"
|
|
86082
|
-
},
|
|
86083
86091
|
"integrations.list": {
|
|
86084
86092
|
capName: "integrations",
|
|
86085
86093
|
capScope: "system",
|
|
@@ -86087,13 +86095,6 @@ Object.freeze({
|
|
|
86087
86095
|
access: "view",
|
|
86088
86096
|
role: "admin"
|
|
86089
86097
|
},
|
|
86090
|
-
"integrations.setSettings": {
|
|
86091
|
-
capName: "integrations",
|
|
86092
|
-
capScope: "system",
|
|
86093
|
-
addonId: null,
|
|
86094
|
-
access: "create",
|
|
86095
|
-
role: "admin"
|
|
86096
|
-
},
|
|
86097
86098
|
"integrations.testConnection": {
|
|
86098
86099
|
capName: "integrations",
|
|
86099
86100
|
capScope: "system",
|
|
@@ -87088,6 +87089,13 @@ Object.freeze({
|
|
|
87088
87089
|
access: "view",
|
|
87089
87090
|
role: "admin"
|
|
87090
87091
|
},
|
|
87092
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
87093
|
+
capName: "migrations",
|
|
87094
|
+
capScope: "system",
|
|
87095
|
+
addonId: null,
|
|
87096
|
+
access: "delete",
|
|
87097
|
+
role: "admin"
|
|
87098
|
+
},
|
|
87091
87099
|
"migrations.declareOwner": {
|
|
87092
87100
|
capName: "migrations",
|
|
87093
87101
|
capScope: "system",
|
|
@@ -87095,6 +87103,13 @@ Object.freeze({
|
|
|
87095
87103
|
access: "create",
|
|
87096
87104
|
role: "admin"
|
|
87097
87105
|
},
|
|
87106
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
87107
|
+
capName: "migrations",
|
|
87108
|
+
capScope: "system",
|
|
87109
|
+
addonId: null,
|
|
87110
|
+
access: "delete",
|
|
87111
|
+
role: "admin"
|
|
87112
|
+
},
|
|
87098
87113
|
"migrations.dryRun": {
|
|
87099
87114
|
capName: "migrations",
|
|
87100
87115
|
capScope: "system",
|
|
@@ -87130,6 +87145,20 @@ Object.freeze({
|
|
|
87130
87145
|
access: "create",
|
|
87131
87146
|
role: "admin"
|
|
87132
87147
|
},
|
|
87148
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
87149
|
+
capName: "migrations",
|
|
87150
|
+
capScope: "system",
|
|
87151
|
+
addonId: null,
|
|
87152
|
+
access: "create",
|
|
87153
|
+
role: "admin"
|
|
87154
|
+
},
|
|
87155
|
+
"migrations.readLegacyIntegrationRows": {
|
|
87156
|
+
capName: "migrations",
|
|
87157
|
+
capScope: "system",
|
|
87158
|
+
addonId: null,
|
|
87159
|
+
access: "view",
|
|
87160
|
+
role: "admin"
|
|
87161
|
+
},
|
|
87133
87162
|
"migrations.restorePreMigration": {
|
|
87134
87163
|
capName: "migrations",
|
|
87135
87164
|
capScope: "system",
|
|
@@ -99231,6 +99260,7 @@ var EnpalProviderAddon = class extends BaseDeviceProvider {
|
|
|
99231
99260
|
fixtureMark: "host"
|
|
99232
99261
|
})], integrationRowsPortsOver({
|
|
99233
99262
|
api: defaultIntegrationApiOver(this.ctx.api),
|
|
99263
|
+
secrets: this.ctx.secrets ?? null,
|
|
99234
99264
|
records,
|
|
99235
99265
|
kind: enpalConnectionsEntryKind,
|
|
99236
99266
|
warn: (message) => this.ctx.logger.warn(message)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-enpal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Enpal solar-box device-provider addon for CamStack — PV, battery, grid, house and heat-pump readings from the box's local InfluxDB (device page as fallback)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
}
|
|
64
64
|
],
|
|
65
65
|
"requires": {
|
|
66
|
-
"server": ">=1.2.
|
|
66
|
+
"server": ">=1.2.770"
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
"files": [
|