@camstack/addon-provider-ecowitt 0.2.155 → 0.2.156

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 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.2.156] - 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.2.155] - 2026-11-04
11
17
 
12
18
  ### Changed
package/dist/addon.js CHANGED
@@ -5984,7 +5984,7 @@ var ZodIssueCode$1 = {
5984
5984
  var ZodFirstPartyTypeKind$1;
5985
5985
  ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
5986
5986
  //#endregion
5987
- //#region ../types/dist/sleep-D1jjWAr8.mjs
5987
+ //#region ../types/dist/sleep-9TIHFj1w.mjs
5988
5988
  /**
5989
5989
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5990
5990
  * window to float samples (D455).
@@ -7928,10 +7928,9 @@ var BaseAddon = class {
7928
7928
  *
7929
7929
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
7930
7930
  * needs its connection fields, the default integration the addon's own sections. It is the
7931
- * SHAPE of the form only — the values live in `integrations.getSettings`, and the write path is
7932
- * unchanged (`integrations.setSettings` for an account integration).
7931
+ * SHAPE of the form only — the values live on the instance record.
7933
7932
  */
7934
- async integrationSettingsSchema(_instance) {
7933
+ async integrationFormSchema(_instance) {
7935
7934
  return null;
7936
7935
  }
7937
7936
  /**
@@ -7977,7 +7976,7 @@ var BaseAddon = class {
7977
7976
  * per-instance schema when it declares one, else its global schema (what they always showed).
7978
7977
  */
7979
7978
  async getIntegrationFormSchema(instance, nodeId) {
7980
- const scoped = await this.integrationSettingsSchema(instance);
7979
+ const scoped = await this.integrationFormSchema(instance);
7981
7980
  if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
7982
7981
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
7983
7982
  }
@@ -11951,7 +11950,7 @@ function userError(code, params, englishFallback) {
11951
11950
  return new CamstackError(code, params, englishFallback);
11952
11951
  }
11953
11952
  //#endregion
11954
- //#region ../types/dist/composition-CRNwcFYY.mjs
11953
+ //#region ../types/dist/composition-pOHbTzRB.mjs
11955
11954
  /**
11956
11955
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11957
11956
  *
@@ -14659,20 +14658,28 @@ async function moveIntegrationRowsToRecords(ports, spec) {
14659
14658
  };
14660
14659
  }
14661
14660
  /**
14662
- * The addon-side ports of `moveIntegrationRowsToRecords` (planning#416): an addon runs the move
14663
- * from its own process, so the legacy rows are reached through the hub (`ctx.api.integrations`:
14664
- * `list`, `getSettings` - which answers the OWNER its secrets resolved - and `clearSettings`) and
14665
- * the new record through the addon's own records handle. The hub deletes the rows and their
14666
- * keychain values; the addon never touches the registry.
14661
+ * The addon-side ports of `moveIntegrationRowsToRecords` (planning#416). An addon runs the move
14662
+ * from its own process, so the leftover rows are reached through the migration-only door
14663
+ * (`migrations.readLegacyIntegrationRows` / `clearLegacyIntegrationRows`) on the hub's connection.
14664
+ * The door returns markers. This process is the owner, so it resolves and releases them with
14665
+ * `resolveSecretMarkers` — the same call the old settings door used — and only then asks the
14666
+ * door to delete the rows. The addon's own record kind is its records handle.
14667
14667
  */
14668
+ async function resolvedRows(stored, secrets) {
14669
+ if (secrets === null) return stored;
14670
+ const { value, unresolved } = await resolveSecretMarkers(stored, secrets);
14671
+ if (unresolved.length > 0) throw new Error(`${unresolved.length} secret(s) cannot be read from the keychain`);
14672
+ return value;
14673
+ }
14668
14674
  function integrationRowsPortsOverApi(deps) {
14675
+ const secrets = deps.secrets ?? null;
14669
14676
  return {
14670
14677
  warn: (message) => deps.log.warn(message),
14671
14678
  listIntegrations: async (addonId) => (await deps.api.list()).filter((i) => i.addonId === addonId).map((i) => ({
14672
14679
  id: i.id,
14673
14680
  name: i.name
14674
14681
  })),
14675
- readRows: (integrationId) => deps.api.getSettings({ id: integrationId }),
14682
+ readRows: async (integrationId) => resolvedRows(await deps.api.readLegacyIntegrationRows({ integrationId }), secrets),
14676
14683
  putRecord: async (id, body) => {
14677
14684
  if (await deps.records.get(id) !== null) return "exists";
14678
14685
  try {
@@ -14691,15 +14698,17 @@ function integrationRowsPortsOverApi(deps) {
14691
14698
  await deps.records.delete(id);
14692
14699
  },
14693
14700
  clearRows: async (integrationId) => {
14694
- await deps.api.clearSettings({ id: integrationId });
14701
+ const refs = collectSecretMarkerRefs(await deps.api.readLegacyIntegrationRows({ integrationId }));
14702
+ if (refs.length > 0 && secrets !== null) await secrets.delete(refs);
14703
+ await deps.api.clearLegacyIntegrationRows({ integrationId });
14695
14704
  }
14696
14705
  };
14697
14706
  }
14698
14707
  function defaultIntegrationApiOver(api) {
14699
14708
  return {
14700
14709
  list: () => api.integrations.list.query(),
14701
- getSettings: (input) => api.integrations.getSettings.query(input),
14702
- clearSettings: (input) => api.integrations.clearSettings.mutate(input),
14710
+ readLegacyIntegrationRows: (input) => api.migrations.readLegacyIntegrationRows.query(input),
14711
+ clearLegacyIntegrationRows: (input) => api.migrations.clearLegacyIntegrationRows.mutate(input),
14703
14712
  listDevices: (addonId) => api.deviceManager.listAll.query({
14704
14713
  addonId,
14705
14714
  projection: "slim"
@@ -50377,6 +50386,9 @@ var RunningProposalEvidenceSchema = object({
50377
50386
  runsFound: number().int().nonnegative(),
50378
50387
  pausesFound: number().int().nonnegative(),
50379
50388
  longestPauseMs: number().nonnegative(),
50389
+ /** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
50390
+ pulsesFound: number().int().nonnegative(),
50391
+ longestPulseMs: number().nonnegative(),
50380
50392
  idleFloorW: number(),
50381
50393
  noiseCeilingW: number(),
50382
50394
  plateauW: number(),
@@ -56243,11 +56255,7 @@ var CreateIntegrationInputSchema = object({
56243
56255
  info: record(string(), unknown()).optional(),
56244
56256
  /** `stableId` of the discovery candidate the form was seeded from (D984). */
56245
56257
  originStableId: string().optional(),
56246
- settings: record(string(), unknown()).optional(),
56247
- /** Keys of `settings` the form's schema declares secret (by type) — kept in
56248
- * the keychain, not the table (D787). */
56249
- secretKeys: array(string()).optional(),
56250
- skipRestart: boolean().optional()
56258
+ settings: record(string(), unknown()).optional()
56251
56259
  });
56252
56260
  var UpdateIntegrationInputSchema = object({
56253
56261
  id: string(),
@@ -56268,22 +56276,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
56268
56276
  }), method(object({ id: string() }), DeleteIntegrationResultSchema, {
56269
56277
  kind: "mutation",
56270
56278
  auth: "admin"
56271
- }), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
56272
- id: string(),
56273
- settings: record(string(), unknown()),
56274
- /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
56275
- secretKeys: array(string()).optional(),
56276
- /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
56277
- skipRestart: boolean().optional()
56278
- }), object({ success: boolean() }), {
56279
- kind: "mutation",
56280
- auth: "admin"
56281
- }), method(object({ id: string() }), object({
56282
- rows: number().int().nonnegative(),
56283
- secrets: number().int().nonnegative()
56284
- }), {
56285
- kind: "mutation",
56286
- auth: "admin"
56287
56279
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
56288
56280
  addonId: string(),
56289
56281
  settings: record(string(), unknown())
@@ -59260,6 +59252,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
59260
59252
  }), {
59261
59253
  kind: "mutation",
59262
59254
  auth: "admin"
59255
+ }), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
59256
+ rows: number().int().nonnegative(),
59257
+ secrets: number().int().nonnegative()
59258
+ }), {
59259
+ kind: "mutation",
59260
+ auth: "admin"
59261
+ }), method(object({
59262
+ integrationId: string().min(1),
59263
+ patch: record(string(), unknown())
59264
+ }), object({ written: boolean() }), {
59265
+ kind: "mutation",
59266
+ auth: "admin"
59267
+ }), method(object({
59268
+ integrationId: string().min(1),
59269
+ keys: array(string().min(1))
59270
+ }), object({
59271
+ present: boolean(),
59272
+ removed: record(string(), unknown())
59273
+ }), {
59274
+ kind: "mutation",
59275
+ auth: "admin"
59263
59276
  });
59264
59277
  method(object({
59265
59278
  sourceUrl: string(),
@@ -80678,11 +80691,15 @@ discriminatedUnion("kind", [object({
80678
80691
  * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
80679
80692
  * near F. A device that is not near one level most of the time has no floor.
80680
80693
  * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
80681
- * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
80682
- * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
80694
+ * 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
80695
+ * display waking): it is neither a run nor a pause. Among the remaining
80696
+ * episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
80697
+ * two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
80698
+ * a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
80699
+ * is no run.
80683
80700
  * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
80684
- * grid a person would type; stop dwell covers the longest pause; start dwell
80685
- * covers the longest standby blip above T.
80701
+ * grid a person would type; stop dwell covers the longest pause and the
80702
+ * kind's floor; start dwell covers the longest pulse and standby blip above T.
80686
80703
  * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
80687
80704
  * run must hold exactly one true interval and nothing may flap outside.
80688
80705
  */
@@ -80693,6 +80710,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
80693
80710
  30 * SECOND_MS;
80694
80711
  60 * SECOND_MS;
80695
80712
  20 * MINUTE_MS$1;
80713
+ 90 * SECOND_MS;
80714
+ 30 * MINUTE_MS$1;
80715
+ 10 * MINUTE_MS$1;
80716
+ DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
80696
80717
  10 * SECOND_MS;
80697
80718
  10 * SECOND_MS;
80698
80719
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
@@ -85977,13 +85998,6 @@ Object.freeze({
85977
85998
  access: "create",
85978
85999
  role: "admin"
85979
86000
  },
85980
- "integrations.clearSettings": {
85981
- capName: "integrations",
85982
- capScope: "system",
85983
- addonId: null,
85984
- access: "delete",
85985
- role: "admin"
85986
- },
85987
86001
  "integrations.create": {
85988
86002
  capName: "integrations",
85989
86003
  capScope: "system",
@@ -86019,13 +86033,6 @@ Object.freeze({
86019
86033
  access: "view",
86020
86034
  role: "admin"
86021
86035
  },
86022
- "integrations.getSettings": {
86023
- capName: "integrations",
86024
- capScope: "system",
86025
- addonId: null,
86026
- access: "view",
86027
- role: "admin"
86028
- },
86029
86036
  "integrations.list": {
86030
86037
  capName: "integrations",
86031
86038
  capScope: "system",
@@ -86033,13 +86040,6 @@ Object.freeze({
86033
86040
  access: "view",
86034
86041
  role: "admin"
86035
86042
  },
86036
- "integrations.setSettings": {
86037
- capName: "integrations",
86038
- capScope: "system",
86039
- addonId: null,
86040
- access: "create",
86041
- role: "admin"
86042
- },
86043
86043
  "integrations.testConnection": {
86044
86044
  capName: "integrations",
86045
86045
  capScope: "system",
@@ -87034,6 +87034,13 @@ Object.freeze({
87034
87034
  access: "view",
87035
87035
  role: "admin"
87036
87036
  },
87037
+ "migrations.clearLegacyIntegrationRows": {
87038
+ capName: "migrations",
87039
+ capScope: "system",
87040
+ addonId: null,
87041
+ access: "delete",
87042
+ role: "admin"
87043
+ },
87037
87044
  "migrations.declareOwner": {
87038
87045
  capName: "migrations",
87039
87046
  capScope: "system",
@@ -87041,6 +87048,13 @@ Object.freeze({
87041
87048
  access: "create",
87042
87049
  role: "admin"
87043
87050
  },
87051
+ "migrations.deleteLegacyIntegrationRowKeys": {
87052
+ capName: "migrations",
87053
+ capScope: "system",
87054
+ addonId: null,
87055
+ access: "delete",
87056
+ role: "admin"
87057
+ },
87044
87058
  "migrations.dryRun": {
87045
87059
  capName: "migrations",
87046
87060
  capScope: "system",
@@ -87076,6 +87090,20 @@ Object.freeze({
87076
87090
  access: "create",
87077
87091
  role: "admin"
87078
87092
  },
87093
+ "migrations.patchLegacyIntegrationRows": {
87094
+ capName: "migrations",
87095
+ capScope: "system",
87096
+ addonId: null,
87097
+ access: "create",
87098
+ role: "admin"
87099
+ },
87100
+ "migrations.readLegacyIntegrationRows": {
87101
+ capName: "migrations",
87102
+ capScope: "system",
87103
+ addonId: null,
87104
+ access: "view",
87105
+ role: "admin"
87106
+ },
87079
87107
  "migrations.restorePreMigration": {
87080
87108
  capName: "migrations",
87081
87109
  capScope: "system",
@@ -105324,6 +105352,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
105324
105352
  if (records === void 0) throw new Error("provider-ecowitt: ctx.records is required (hub-only addon)");
105325
105353
  const rowsPorts = integrationRowsPortsOverApi({
105326
105354
  api: defaultIntegrationApiOver(this.ctx.api),
105355
+ secrets: this.ctx.secrets ?? null,
105327
105356
  records: records.kind({
105328
105357
  name: ecowittConnectionsEntryKind.name,
105329
105358
  declaration: ecowittConnectionsEntryKind.declaration,
package/dist/addon.mjs CHANGED
@@ -5983,7 +5983,7 @@ var ZodIssueCode$1 = {
5983
5983
  var ZodFirstPartyTypeKind$1;
5984
5984
  ZodFirstPartyTypeKind$1 || (ZodFirstPartyTypeKind$1 = {});
5985
5985
  //#endregion
5986
- //#region ../types/dist/sleep-D1jjWAr8.mjs
5986
+ //#region ../types/dist/sleep-9TIHFj1w.mjs
5987
5987
  /**
5988
5988
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5989
5989
  * window to float samples (D455).
@@ -7927,10 +7927,9 @@ var BaseAddon = class {
7927
7927
  *
7928
7928
  * Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
7929
7929
  * needs its connection fields, the default integration the addon's own sections. It is the
7930
- * SHAPE of the form only — the values live in `integrations.getSettings`, and the write path is
7931
- * unchanged (`integrations.setSettings` for an account integration).
7930
+ * SHAPE of the form only — the values live on the instance record.
7932
7931
  */
7933
- async integrationSettingsSchema(_instance) {
7932
+ async integrationFormSchema(_instance) {
7934
7933
  return null;
7935
7934
  }
7936
7935
  /**
@@ -7976,7 +7975,7 @@ var BaseAddon = class {
7976
7975
  * per-instance schema when it declares one, else its global schema (what they always showed).
7977
7976
  */
7978
7977
  async getIntegrationFormSchema(instance, nodeId) {
7979
- const scoped = await this.integrationSettingsSchema(instance);
7978
+ const scoped = await this.integrationFormSchema(instance);
7980
7979
  if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
7981
7980
  return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
7982
7981
  }
@@ -11950,7 +11949,7 @@ function userError(code, params, englishFallback) {
11950
11949
  return new CamstackError(code, params, englishFallback);
11951
11950
  }
11952
11951
  //#endregion
11953
- //#region ../types/dist/composition-CRNwcFYY.mjs
11952
+ //#region ../types/dist/composition-pOHbTzRB.mjs
11954
11953
  /**
11955
11954
  * The ffmpeg build every CamStack node downloads, per platform and arch.
11956
11955
  *
@@ -14658,20 +14657,28 @@ async function moveIntegrationRowsToRecords(ports, spec) {
14658
14657
  };
14659
14658
  }
14660
14659
  /**
14661
- * The addon-side ports of `moveIntegrationRowsToRecords` (planning#416): an addon runs the move
14662
- * from its own process, so the legacy rows are reached through the hub (`ctx.api.integrations`:
14663
- * `list`, `getSettings` - which answers the OWNER its secrets resolved - and `clearSettings`) and
14664
- * the new record through the addon's own records handle. The hub deletes the rows and their
14665
- * keychain values; the addon never touches the registry.
14660
+ * The addon-side ports of `moveIntegrationRowsToRecords` (planning#416). An addon runs the move
14661
+ * from its own process, so the leftover rows are reached through the migration-only door
14662
+ * (`migrations.readLegacyIntegrationRows` / `clearLegacyIntegrationRows`) on the hub's connection.
14663
+ * The door returns markers. This process is the owner, so it resolves and releases them with
14664
+ * `resolveSecretMarkers` — the same call the old settings door used — and only then asks the
14665
+ * door to delete the rows. The addon's own record kind is its records handle.
14666
14666
  */
14667
+ async function resolvedRows(stored, secrets) {
14668
+ if (secrets === null) return stored;
14669
+ const { value, unresolved } = await resolveSecretMarkers(stored, secrets);
14670
+ if (unresolved.length > 0) throw new Error(`${unresolved.length} secret(s) cannot be read from the keychain`);
14671
+ return value;
14672
+ }
14667
14673
  function integrationRowsPortsOverApi(deps) {
14674
+ const secrets = deps.secrets ?? null;
14668
14675
  return {
14669
14676
  warn: (message) => deps.log.warn(message),
14670
14677
  listIntegrations: async (addonId) => (await deps.api.list()).filter((i) => i.addonId === addonId).map((i) => ({
14671
14678
  id: i.id,
14672
14679
  name: i.name
14673
14680
  })),
14674
- readRows: (integrationId) => deps.api.getSettings({ id: integrationId }),
14681
+ readRows: async (integrationId) => resolvedRows(await deps.api.readLegacyIntegrationRows({ integrationId }), secrets),
14675
14682
  putRecord: async (id, body) => {
14676
14683
  if (await deps.records.get(id) !== null) return "exists";
14677
14684
  try {
@@ -14690,15 +14697,17 @@ function integrationRowsPortsOverApi(deps) {
14690
14697
  await deps.records.delete(id);
14691
14698
  },
14692
14699
  clearRows: async (integrationId) => {
14693
- await deps.api.clearSettings({ id: integrationId });
14700
+ const refs = collectSecretMarkerRefs(await deps.api.readLegacyIntegrationRows({ integrationId }));
14701
+ if (refs.length > 0 && secrets !== null) await secrets.delete(refs);
14702
+ await deps.api.clearLegacyIntegrationRows({ integrationId });
14694
14703
  }
14695
14704
  };
14696
14705
  }
14697
14706
  function defaultIntegrationApiOver(api) {
14698
14707
  return {
14699
14708
  list: () => api.integrations.list.query(),
14700
- getSettings: (input) => api.integrations.getSettings.query(input),
14701
- clearSettings: (input) => api.integrations.clearSettings.mutate(input),
14709
+ readLegacyIntegrationRows: (input) => api.migrations.readLegacyIntegrationRows.query(input),
14710
+ clearLegacyIntegrationRows: (input) => api.migrations.clearLegacyIntegrationRows.mutate(input),
14702
14711
  listDevices: (addonId) => api.deviceManager.listAll.query({
14703
14712
  addonId,
14704
14713
  projection: "slim"
@@ -50376,6 +50385,9 @@ var RunningProposalEvidenceSchema = object({
50376
50385
  runsFound: number().int().nonnegative(),
50377
50386
  pausesFound: number().int().nonnegative(),
50378
50387
  longestPauseMs: number().nonnegative(),
50388
+ /** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
50389
+ pulsesFound: number().int().nonnegative(),
50390
+ longestPulseMs: number().nonnegative(),
50379
50391
  idleFloorW: number(),
50380
50392
  noiseCeilingW: number(),
50381
50393
  plateauW: number(),
@@ -56242,11 +56254,7 @@ var CreateIntegrationInputSchema = object({
56242
56254
  info: record(string(), unknown()).optional(),
56243
56255
  /** `stableId` of the discovery candidate the form was seeded from (D984). */
56244
56256
  originStableId: string().optional(),
56245
- settings: record(string(), unknown()).optional(),
56246
- /** Keys of `settings` the form's schema declares secret (by type) — kept in
56247
- * the keychain, not the table (D787). */
56248
- secretKeys: array(string()).optional(),
56249
- skipRestart: boolean().optional()
56257
+ settings: record(string(), unknown()).optional()
56250
56258
  });
56251
56259
  var UpdateIntegrationInputSchema = object({
56252
56260
  id: string(),
@@ -56267,22 +56275,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
56267
56275
  }), method(object({ id: string() }), DeleteIntegrationResultSchema, {
56268
56276
  kind: "mutation",
56269
56277
  auth: "admin"
56270
- }), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
56271
- id: string(),
56272
- settings: record(string(), unknown()),
56273
- /** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
56274
- secretKeys: array(string()).optional(),
56275
- /** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
56276
- skipRestart: boolean().optional()
56277
- }), object({ success: boolean() }), {
56278
- kind: "mutation",
56279
- auth: "admin"
56280
- }), method(object({ id: string() }), object({
56281
- rows: number().int().nonnegative(),
56282
- secrets: number().int().nonnegative()
56283
- }), {
56284
- kind: "mutation",
56285
- auth: "admin"
56286
56278
  }), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
56287
56279
  addonId: string(),
56288
56280
  settings: record(string(), unknown())
@@ -59259,6 +59251,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
59259
59251
  }), {
59260
59252
  kind: "mutation",
59261
59253
  auth: "admin"
59254
+ }), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
59255
+ rows: number().int().nonnegative(),
59256
+ secrets: number().int().nonnegative()
59257
+ }), {
59258
+ kind: "mutation",
59259
+ auth: "admin"
59260
+ }), method(object({
59261
+ integrationId: string().min(1),
59262
+ patch: record(string(), unknown())
59263
+ }), object({ written: boolean() }), {
59264
+ kind: "mutation",
59265
+ auth: "admin"
59266
+ }), method(object({
59267
+ integrationId: string().min(1),
59268
+ keys: array(string().min(1))
59269
+ }), object({
59270
+ present: boolean(),
59271
+ removed: record(string(), unknown())
59272
+ }), {
59273
+ kind: "mutation",
59274
+ auth: "admin"
59262
59275
  });
59263
59276
  method(object({
59264
59277
  sourceUrl: string(),
@@ -80677,11 +80690,15 @@ discriminatedUnion("kind", [object({
80677
80690
  * 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
80678
80691
  * near F. A device that is not near one level most of the time has no floor.
80679
80692
  * 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
80680
- * 3. gaps between episodes split in two by the largest log-scale jump (>= 3x):
80681
- * the short ones are pauses INSIDE a run (a soak), the long ones separate runs.
80693
+ * 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
80694
+ * display waking): it is neither a run nor a pause. Among the remaining
80695
+ * episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
80696
+ * two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
80697
+ * a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
80698
+ * is no run.
80682
80699
  * 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
80683
- * grid a person would type; stop dwell covers the longest pause; start dwell
80684
- * covers the longest standby blip above T.
80700
+ * grid a person would type; stop dwell covers the longest pause and the
80701
+ * kind's floor; start dwell covers the longest pulse and standby blip above T.
80685
80702
  * 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
80686
80703
  * run must hold exactly one true interval and nothing may flap outside.
80687
80704
  */
@@ -80692,6 +80709,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
80692
80709
  30 * SECOND_MS;
80693
80710
  60 * SECOND_MS;
80694
80711
  20 * MINUTE_MS$1;
80712
+ 90 * SECOND_MS;
80713
+ 30 * MINUTE_MS$1;
80714
+ 10 * MINUTE_MS$1;
80715
+ DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
80695
80716
  10 * SECOND_MS;
80696
80717
  10 * SECOND_MS;
80697
80718
  DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
@@ -85976,13 +85997,6 @@ Object.freeze({
85976
85997
  access: "create",
85977
85998
  role: "admin"
85978
85999
  },
85979
- "integrations.clearSettings": {
85980
- capName: "integrations",
85981
- capScope: "system",
85982
- addonId: null,
85983
- access: "delete",
85984
- role: "admin"
85985
- },
85986
86000
  "integrations.create": {
85987
86001
  capName: "integrations",
85988
86002
  capScope: "system",
@@ -86018,13 +86032,6 @@ Object.freeze({
86018
86032
  access: "view",
86019
86033
  role: "admin"
86020
86034
  },
86021
- "integrations.getSettings": {
86022
- capName: "integrations",
86023
- capScope: "system",
86024
- addonId: null,
86025
- access: "view",
86026
- role: "admin"
86027
- },
86028
86035
  "integrations.list": {
86029
86036
  capName: "integrations",
86030
86037
  capScope: "system",
@@ -86032,13 +86039,6 @@ Object.freeze({
86032
86039
  access: "view",
86033
86040
  role: "admin"
86034
86041
  },
86035
- "integrations.setSettings": {
86036
- capName: "integrations",
86037
- capScope: "system",
86038
- addonId: null,
86039
- access: "create",
86040
- role: "admin"
86041
- },
86042
86042
  "integrations.testConnection": {
86043
86043
  capName: "integrations",
86044
86044
  capScope: "system",
@@ -87033,6 +87033,13 @@ Object.freeze({
87033
87033
  access: "view",
87034
87034
  role: "admin"
87035
87035
  },
87036
+ "migrations.clearLegacyIntegrationRows": {
87037
+ capName: "migrations",
87038
+ capScope: "system",
87039
+ addonId: null,
87040
+ access: "delete",
87041
+ role: "admin"
87042
+ },
87036
87043
  "migrations.declareOwner": {
87037
87044
  capName: "migrations",
87038
87045
  capScope: "system",
@@ -87040,6 +87047,13 @@ Object.freeze({
87040
87047
  access: "create",
87041
87048
  role: "admin"
87042
87049
  },
87050
+ "migrations.deleteLegacyIntegrationRowKeys": {
87051
+ capName: "migrations",
87052
+ capScope: "system",
87053
+ addonId: null,
87054
+ access: "delete",
87055
+ role: "admin"
87056
+ },
87043
87057
  "migrations.dryRun": {
87044
87058
  capName: "migrations",
87045
87059
  capScope: "system",
@@ -87075,6 +87089,20 @@ Object.freeze({
87075
87089
  access: "create",
87076
87090
  role: "admin"
87077
87091
  },
87092
+ "migrations.patchLegacyIntegrationRows": {
87093
+ capName: "migrations",
87094
+ capScope: "system",
87095
+ addonId: null,
87096
+ access: "create",
87097
+ role: "admin"
87098
+ },
87099
+ "migrations.readLegacyIntegrationRows": {
87100
+ capName: "migrations",
87101
+ capScope: "system",
87102
+ addonId: null,
87103
+ access: "view",
87104
+ role: "admin"
87105
+ },
87078
87106
  "migrations.restorePreMigration": {
87079
87107
  capName: "migrations",
87080
87108
  capScope: "system",
@@ -105323,6 +105351,7 @@ var EcowittProviderAddon = class extends BaseDeviceProvider {
105323
105351
  if (records === void 0) throw new Error("provider-ecowitt: ctx.records is required (hub-only addon)");
105324
105352
  const rowsPorts = integrationRowsPortsOverApi({
105325
105353
  api: defaultIntegrationApiOver(this.ctx.api),
105354
+ secrets: this.ctx.secrets ?? null,
105326
105355
  records: records.kind({
105327
105356
  name: ecowittConnectionsEntryKind.name,
105328
105357
  declaration: ecowittConnectionsEntryKind.declaration,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-ecowitt",
3
- "version": "0.2.155",
3
+ "version": "0.2.156",
4
4
  "description": "Ecowitt weather-station device-provider addon for CamStack — wraps the @apocaliss92/nodewitt local-poll / push client",
5
5
  "keywords": [
6
6
  "camstack",
@@ -63,7 +63,7 @@
63
63
  }
64
64
  ],
65
65
  "requires": {
66
- "server": ">=1.2.765"
66
+ "server": ">=1.2.770"
67
67
  }
68
68
  },
69
69
  "files": [