@camstack/system 1.2.483 → 1.2.485

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.
Files changed (87) hide show
  1. package/dist/{agent-https-CHZpVx3I.js → agent-https-DBP7P-ee.js} +1 -1
  2. package/dist/{agent-https-NP141VCW.mjs → agent-https-_EXvi6F8.mjs} +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  13. package/dist/builtins/calendars/calendars.addon.js +1 -1
  14. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  15. package/dist/builtins/camera-grid/index.js +1 -1
  16. package/dist/builtins/camera-grid/index.mjs +1 -1
  17. package/dist/builtins/composer/composer.addon.js +13 -5
  18. package/dist/builtins/composer/composer.addon.mjs +13 -5
  19. package/dist/builtins/console-logging/index.js +1 -1
  20. package/dist/builtins/console-logging/index.mjs +1 -1
  21. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  22. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  23. package/dist/builtins/device-history/device-history.addon.d.ts +2 -0
  24. package/dist/builtins/device-history/device-history.addon.js +248 -4
  25. package/dist/builtins/device-history/device-history.addon.mjs +248 -4
  26. package/dist/builtins/device-history/query/history-query.d.ts +5 -1
  27. package/dist/builtins/device-history/runtime/history-runtime.d.ts +18 -1
  28. package/dist/builtins/device-history/store/history-schema.d.ts +2 -0
  29. package/dist/builtins/device-history/store/history-store.d.ts +11 -0
  30. package/dist/builtins/device-history/store/series-scale.d.ts +37 -0
  31. package/dist/builtins/device-manager/device-manager.addon.js +3 -2
  32. package/dist/builtins/device-manager/device-manager.addon.mjs +3 -2
  33. package/dist/builtins/hub-forwarder/index.js +1 -1
  34. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  35. package/dist/builtins/keychain/index.js +3 -3
  36. package/dist/builtins/keychain/index.mjs +3 -3
  37. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  38. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  39. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  40. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  41. package/dist/builtins/local-network/local-network.addon.js +2 -2
  42. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  43. package/dist/builtins/loki-logging/index.js +1 -1
  44. package/dist/builtins/loki-logging/index.mjs +1 -1
  45. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  46. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  47. package/dist/builtins/platform-probe/index.js +1 -1
  48. package/dist/builtins/platform-probe/index.mjs +1 -1
  49. package/dist/builtins/record-entries/index.js +1 -1
  50. package/dist/builtins/record-entries/index.mjs +1 -1
  51. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  52. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  53. package/dist/builtins/snapshot/index.js +1 -1
  54. package/dist/builtins/snapshot/index.mjs +1 -1
  55. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  56. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  57. package/dist/builtins/sqlite-storage/index.js +1 -1
  58. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  59. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  60. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  61. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  62. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  63. package/dist/builtins/system-config/system-config.addon.js +3 -3
  64. package/dist/builtins/system-config/system-config.addon.mjs +280 -280
  65. package/dist/builtins/winston-logging/index.js +1 -1
  66. package/dist/builtins/winston-logging/index.mjs +1 -1
  67. package/dist/{composition-sources-CQxrCexr.js → composition-sources-BSQneZV9.js} +1 -1
  68. package/dist/{composition-sources-CC13wGKL.mjs → composition-sources-BnZpIA8u.mjs} +1 -1
  69. package/dist/{constants-DMB-ex5z.mjs → constants-7D7xjw0h.mjs} +1 -1
  70. package/dist/{constants-Bt0NFXDm.js → constants-CQfTeCnB.js} +1 -1
  71. package/dist/{device-ca-HQKcvofS.mjs → device-ca-Bzd6UO_c.mjs} +1 -1
  72. package/dist/{device-ca-CSLMfr4Q.js → device-ca-D_4dI5gv.js} +1 -1
  73. package/dist/{dist-DvKEKSap.mjs → dist-Df6c0RX_.mjs} +614 -522
  74. package/dist/{dist-2gZWO597.js → dist-xCNJAQMh.js} +625 -521
  75. package/dist/file-host-worker.js +1 -1
  76. package/dist/file-host-worker.mjs +1 -1
  77. package/dist/{hub-mdns-ciao-CVf6mxIZ.mjs → hub-mdns-ciao-D7GMnEz0.mjs} +2 -2
  78. package/dist/{hub-mdns-ciao-Bs3cn9C1.js → hub-mdns-ciao-DRfmLR_Z.js} +2 -2
  79. package/dist/index.js +7 -7
  80. package/dist/index.mjs +7 -7
  81. package/dist/{restore-archive-EFYeCdrG.mjs → restore-archive-Co70oRya.mjs} +78 -78
  82. package/dist/{restore-archive-_mgadWlk.js → restore-archive-DA7N6ZZA.js} +1 -1
  83. package/dist/{retention-qugQGYuY.js → retention-DNDOPW7Z.js} +1 -1
  84. package/dist/{retention-FFJW3-3P.mjs → retention-DfCW-dZS.mjs} +1 -1
  85. package/dist/{wal-maintenance-TCW_esAc.mjs → wal-maintenance-71-j48Ci.mjs} +0 -0
  86. package/dist/{wal-maintenance-DbUC_wdC.js → wal-maintenance-CA0L6WZ9.js} +0 -0
  87. package/package.json +7 -2
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-2gZWO597.js");
2
+ const require_dist = require("./dist-xCNJAQMh.js");
3
3
  const require_x509_es = require("./x509.es-WtnRNyYS.js");
4
4
  let zod = require("zod");
5
5
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { Ji as defineRecordKinds } from "./dist-DvKEKSap.mjs";
1
+ import { Ji as defineRecordKinds } from "./dist-Df6c0RX_.mjs";
2
2
  import { C as backdatedNotBefore, S as withUtcTimeEncoding, _ as NameConstraints, a as KeyUsageFlags, c as Pkcs10CertificateRequest, d as SubjectKeyIdentifierExtension, f as X509Certificate$1, g as GeneralSubtrees, h as GeneralSubtree, i as Extension, l as Pkcs10CertificateRequestGenerator, m as cryptoProvider, n as BasicConstraintsExtension, o as KeyUsagesExtension, p as X509CertificateGenerator, r as ExtendedKeyUsageExtension, s as PemConverter, t as AuthorityKeyIdentifierExtension, u as SubjectAlternativeNameExtension, v as GeneralName, x as AsnConvert } from "./x509.es-BRiJgOvu.mjs";
3
3
  import { z } from "zod";
4
4
  import { X509Certificate, createHash, createPrivateKey, randomBytes, webcrypto } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { _t as addonPagesCapability, ka as EventCategory, ni as BaseAddon, ti as errMsg } from "../../dist-DvKEKSap.mjs";
1
+ import { _t as addonPagesCapability, ja as EventCategory, ni as BaseAddon, ti as errMsg } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { ka as EventCategory, ni as BaseAddon, ti as errMsg, yt as addonWidgetsCapability } from "../../dist-DvKEKSap.mjs";
1
+ import { ja as EventCategory, ni as BaseAddon, ti as errMsg, yt as addonWidgetsCapability } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
8
8
  //#region src/builtins/alerts/alert-episode-policy.ts
9
9
  /**
@@ -1,4 +1,4 @@
1
- import { Gi as createEvent, Ji as defineRecordKinds, St as alertsCapability, bn as isAlertEpisodeSeverity, bt as alertEpisodeId, i as AlertSchema, ka as EventCategory, ni as BaseAddon, ti as errMsg, xt as alertEpisodePayload, yn as isAlertEpisodeOpen } from "../../dist-DvKEKSap.mjs";
1
+ import { Gi as createEvent, Ji as defineRecordKinds, St as alertsCapability, bn as isAlertEpisodeSeverity, bt as alertEpisodeId, i as AlertSchema, ja as EventCategory, ni as BaseAddon, ti as errMsg, xt as alertEpisodePayload, yn as isAlertEpisodeOpen } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
3
  //#region src/builtins/alerts/alert-episode-policy.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,
@@ -1,4 +1,4 @@
1
- import { Et as bareAddonId, I as FIRST_LEVEL_MACRO_CLASSES, ka as EventCategory, na as isEvent, ni as BaseAddon, rr as ptzAutotrackCapability, ti as errMsg } from "../../dist-DvKEKSap.mjs";
1
+ import { Et as bareAddonId, I as FIRST_LEVEL_MACRO_CLASSES, ja as EventCategory, ni as BaseAddon, ra as isEvent, rr as ptzAutotrackCapability, ti as errMsg } from "../../dist-Df6c0RX_.mjs";
2
2
  //#region src/builtins/autotrack/autotrack-config.ts
3
3
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
4
4
  deadZoneFraction: .5,
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
8
- const require_restore_archive = require("../../restore-archive-_mgadWlk.js");
8
+ const require_restore_archive = require("../../restore-archive-DA7N6ZZA.js");
9
9
  let node_crypto = require("node:crypto");
10
10
  let node_fs_promises = require("node:fs/promises");
11
11
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,6 +1,6 @@
1
- import { Or as storageOccupancyCapability, Tt as backupCapability, Vn as mayWriteToLocation, ka as EventCategory, ni as BaseAddon, ua as nodePin, wa as toNodeId } from "../../dist-DvKEKSap.mjs";
1
+ import { Ea as toNodeId, Or as storageOccupancyCapability, Tt as backupCapability, Vn as mayWriteToLocation, fa as nodePin, ja as EventCategory, ni as BaseAddon } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
- import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-EFYeCdrG.mjs";
3
+ import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-Co70oRya.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import * as fsp from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/calendars/calendar-color.ts
@@ -1,4 +1,4 @@
1
- import { Ji as defineRecordKinds, Mt as calendarsCapability, g as CalendarPreferencesSchema, ni as BaseAddon } from "../../dist-DvKEKSap.mjs";
1
+ import { Ji as defineRecordKinds, Mt as calendarsCapability, g as CalendarPreferencesSchema, ni as BaseAddon } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/calendars/calendar-color.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  let zod = require("zod");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ar as streamCatalogCapability, Cr as snapshotCapability, Gi as createEvent, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Qr as DeviceFeature, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Vr as buildFfmpegArgs, X as MOTION_CLOSE_AFTER_MS, a as BaseDevice, ea as hydrateSchema, ei as DeviceType, ka as EventCategory, ni as BaseAddon, qt as declarationOwnerNodeId, ti as errMsg } from "../../dist-DvKEKSap.mjs";
1
+ import { Ar as streamCatalogCapability, Cr as snapshotCapability, Gi as createEvent, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Qr as DeviceFeature, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Vr as buildFfmpegArgs, X as MOTION_CLOSE_AFTER_MS, a as BaseDevice, ei as DeviceType, ja as EventCategory, ni as BaseAddon, qt as declarationOwnerNodeId, ta as hydrateSchema, ti as errMsg } from "../../dist-Df6c0RX_.mjs";
2
2
  import { z } from "zod";
3
3
  import { FfmpegProcess } from "@camstack/types/node";
4
4
  import { join } from "node:path";
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
7
- const require_composition_sources = require("../../composition-sources-CQxrCexr.js");
8
- const require_retention = require("../../retention-qugQGYuY.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
+ const require_composition_sources = require("../../composition-sources-BSQneZV9.js");
8
+ const require_retention = require("../../retention-DNDOPW7Z.js");
9
9
  let zod = require("zod");
10
10
  //#region src/builtins/shared/device-state-changed.ts
11
11
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -569,7 +569,7 @@ function withEntry(map, key, value) {
569
569
  return new Map([...map, [key, value]]);
570
570
  }
571
571
  function nativeTakenReason(addonId, when) {
572
- return `a native provider (${addonId}) ${when} — the cap is not grafted and its claim is kept; edit or restart the block to replace the native instead`;
572
+ return `a native provider (${addonId}) ${when} — the cap is not grafted and its claim is kept`;
573
573
  }
574
574
  var ComposerGrafts = class {
575
575
  ports;
@@ -610,9 +610,10 @@ var ComposerGrafts = class {
610
610
  if (ports === void 0) return;
611
611
  const { deviceId } = input;
612
612
  const added = input.enabled && input.settled ? addedCapsOf(input.features) : [];
613
+ const nativeNow = new Set([...input.nativeCaps.entries()].filter(([, owner]) => owner !== require_dist.COMPOSER_ADDON_ID).map(([cap]) => cap));
613
614
  const replaced = new Set(input.features.filter((f) => f.mode === "replace").map((f) => f.cap));
614
615
  for (const cap of ports.grafted(deviceId)) {
615
- if (!replaced.has(cap)) continue;
616
+ if (!nativeNow.has(cap) && !replaced.has(cap)) continue;
616
617
  this.logger.info("a native provider serves a grafted cap — the graft is removed; the claim stays", {
617
618
  tags: { deviceId },
618
619
  meta: { cap }
@@ -1161,6 +1162,13 @@ function logPlanTransition(logger, previous, p, liveTags) {
1161
1162
  }
1162
1163
  });
1163
1164
  else if (p.problems.length === 0) logger.info("composition plans again", extras);
1165
+ else if (target.kind === "existing" && target.deviceId !== null && p.problems.some((line) => line.includes("native-capability-replace"))) logger.warn(`stored customization ${p.block.id} replaces a native capability — refused`, {
1166
+ tags: { deviceId: target.deviceId },
1167
+ meta: {
1168
+ ...extras.meta,
1169
+ problems: p.problems
1170
+ }
1171
+ });
1164
1172
  else logger.warn(p.declaration === null ? "composition does not plan — no device is declared for it" : "composition no longer plans — its device is kept offline with no capabilities", extras);
1165
1173
  }
1166
1174
  //#endregion
@@ -1,6 +1,6 @@
1
- import { $n as planComposition, Br as COMPOSER_ADDON_ID, Gi as createEvent, Gr as compositionFeatureEnabled, Hr as cloneStatefulExpressionMemory, It as composerCapability, Jr as compositionSourceKey, Kr as compositionItemFieldPath, Lt as compositionFieldKey, N as DeclaredDevices, Ot as buildCapabilityLookup, Ur as composedDeviceRef, Wr as composedDeviceStableId, Xr as disarmRoseMemory, Yr as createStatefulExpressionMemory, Zr as toExpressionValue, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as DeviceType, ka as EventCategory, ni as BaseAddon, qr as compositionSliceKey, r as ALL_CAPABILITY_DEFINITIONS, ti as errMsg } from "../../dist-DvKEKSap.mjs";
2
- import { n as normalizedNativeOwner } from "../../composition-sources-CC13wGKL.mjs";
3
- import { i as DAY_MS, m as capSeriesKey, t as DEFAULT_RETENTION } from "../../retention-FFJW3-3P.mjs";
1
+ import { $n as planComposition, Br as COMPOSER_ADDON_ID, Gi as createEvent, Gr as compositionFeatureEnabled, Hr as cloneStatefulExpressionMemory, It as composerCapability, Jr as compositionSourceKey, Kr as compositionItemFieldPath, Lt as compositionFieldKey, N as DeclaredDevices, Ot as buildCapabilityLookup, Ur as composedDeviceRef, Wr as composedDeviceStableId, Xr as disarmRoseMemory, Yr as createStatefulExpressionMemory, Zr as toExpressionValue, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as DeviceType, ja as EventCategory, ni as BaseAddon, qr as compositionSliceKey, r as ALL_CAPABILITY_DEFINITIONS, ti as errMsg } from "../../dist-Df6c0RX_.mjs";
2
+ import { n as normalizedNativeOwner } from "../../composition-sources-BnZpIA8u.mjs";
3
+ import { i as DAY_MS, m as capSeriesKey, t as DEFAULT_RETENTION } from "../../retention-DfCW-dZS.mjs";
4
4
  import { z } from "zod";
5
5
  //#region src/builtins/shared/device-state-changed.ts
6
6
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -564,7 +564,7 @@ function withEntry(map, key, value) {
564
564
  return new Map([...map, [key, value]]);
565
565
  }
566
566
  function nativeTakenReason(addonId, when) {
567
- return `a native provider (${addonId}) ${when} — the cap is not grafted and its claim is kept; edit or restart the block to replace the native instead`;
567
+ return `a native provider (${addonId}) ${when} — the cap is not grafted and its claim is kept`;
568
568
  }
569
569
  var ComposerGrafts = class {
570
570
  ports;
@@ -605,9 +605,10 @@ var ComposerGrafts = class {
605
605
  if (ports === void 0) return;
606
606
  const { deviceId } = input;
607
607
  const added = input.enabled && input.settled ? addedCapsOf(input.features) : [];
608
+ const nativeNow = new Set([...input.nativeCaps.entries()].filter(([, owner]) => owner !== COMPOSER_ADDON_ID).map(([cap]) => cap));
608
609
  const replaced = new Set(input.features.filter((f) => f.mode === "replace").map((f) => f.cap));
609
610
  for (const cap of ports.grafted(deviceId)) {
610
- if (!replaced.has(cap)) continue;
611
+ if (!nativeNow.has(cap) && !replaced.has(cap)) continue;
611
612
  this.logger.info("a native provider serves a grafted cap — the graft is removed; the claim stays", {
612
613
  tags: { deviceId },
613
614
  meta: { cap }
@@ -1156,6 +1157,13 @@ function logPlanTransition(logger, previous, p, liveTags) {
1156
1157
  }
1157
1158
  });
1158
1159
  else if (p.problems.length === 0) logger.info("composition plans again", extras);
1160
+ else if (target.kind === "existing" && target.deviceId !== null && p.problems.some((line) => line.includes("native-capability-replace"))) logger.warn(`stored customization ${p.block.id} replaces a native capability — refused`, {
1161
+ tags: { deviceId: target.deviceId },
1162
+ meta: {
1163
+ ...extras.meta,
1164
+ problems: p.problems
1165
+ }
1166
+ });
1159
1167
  else logger.warn(p.declaration === null ? "composition does not plan — no device is declared for it" : "composition no longer plans — its device is kept offline with no capabilities", extras);
1160
1168
  }
1161
1169
  //#endregion
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
7
  const require_formatter = require("../../formatter-xbxeV4GD.js");
8
8
  //#region src/builtins/console-logging/console-destination.ts
9
9
  var LEVEL_RANK = {
@@ -1,4 +1,4 @@
1
- import { Ln as logDestinationCapability, ni as BaseAddon } from "../../dist-DvKEKSap.mjs";
1
+ import { Ln as logDestinationCapability, ni as BaseAddon } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-DkYZ4LXp.mjs";
3
3
  //#region src/builtins/console-logging/console-destination.ts
4
4
  var LEVEL_RANK = {
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-2gZWO597.js");
3
+ const require_dist = require("../../dist-xCNJAQMh.js");
4
4
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
5
- const require_composition_sources = require("../../composition-sources-CQxrCexr.js");
5
+ const require_composition_sources = require("../../composition-sources-BSQneZV9.js");
6
6
  const require_prefix_range = require("../../prefix-range-D_KueXbQ.js");
7
7
  const require_prefix_walk = require("../../prefix-walk-Dv6MLZlM.js");
8
8
  let zod = require("zod");
@@ -1,6 +1,6 @@
1
- import { $n as planComposition, Bt as coreBlockComposition, Ea as transformSecretWrite, Ht as coreBlockSourceKind, Ji as defineRecordKinds, Jr as compositionSourceKey, Kn as normalizeCoreBlockWrite, N as DeclaredDevices, Ot as buildCapabilityLookup, Pr as validateComposition, Rt as compositionSourceRefs, Sa as secretTreeFromSchema, Ui as collectSecretMarkerRefs, Ur as composedDeviceRef, Ut as coreBlocksCapability, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, aa as isSecretMarker, dr as resolveCoreBlockFiles, ea as hydrateSchema, f as COMPOSER_RUNNER_ID, m as CORE_BLOCK_ADDON_PREFIX, ni as BaseAddon, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, sa as isSecretRefOwnedBy, sr as replacedFieldKeys, ti as errMsg, tr as problemsBlockSave, ur as resolveCoreBlockEntry, v as CoreBlockSchema, ya as resolveSecretMarkers, zt as coreBlockAddonId } from "../../dist-DvKEKSap.mjs";
1
+ import { $n as planComposition, Bt as coreBlockComposition, Ht as coreBlockSourceKind, Ji as defineRecordKinds, Jr as compositionSourceKey, Kn as normalizeCoreBlockWrite, N as DeclaredDevices, Oa as transformSecretWrite, Ot as buildCapabilityLookup, Pr as validateComposition, Rt as compositionSourceRefs, Ui as collectSecretMarkerRefs, Ur as composedDeviceRef, Ut as coreBlocksCapability, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, dr as resolveCoreBlockFiles, f as COMPOSER_RUNNER_ID, la as isSecretRefOwnedBy, m as CORE_BLOCK_ADDON_PREFIX, ni as BaseAddon, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, sa as isSecretMarker, sr as replacedFieldKeys, ta as hydrateSchema, ti as errMsg, tr as problemsBlockSave, ur as resolveCoreBlockEntry, v as CoreBlockSchema, wa as secretTreeFromSchema, xa as resolveSecretMarkers, zt as coreBlockAddonId } from "../../dist-Df6c0RX_.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
- import { t as describeCompositionSources } from "../../composition-sources-CC13wGKL.mjs";
3
+ import { t as describeCompositionSources } from "../../composition-sources-BnZpIA8u.mjs";
4
4
  import { t as prefixRange } from "../../prefix-range-Dl0znowN.mjs";
5
5
  import { n as unwrapStoredValue, t as loadPrefixed } from "../../prefix-walk-DAOBSyH1.mjs";
6
6
  import { createRequire } from "node:module";
@@ -41,6 +41,8 @@ export declare class DeviceHistoryAddon extends BaseAddon<DeviceHistoryConfig> {
41
41
  /** Readiness of both provided caps, on the bare node this runner belongs to. */
42
42
  private emitStoreReadiness;
43
43
  private retention;
44
+ /** Unraid array counters still stored in GiB. A failed listing throws to the caller. */
45
+ private legacyScales;
44
46
  private requireRuntime;
45
47
  protected onInitialize(): Promise<ProviderRegistration[]>;
46
48
  override onHubReachable(): Promise<void>;
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-2gZWO597.js");
7
- const require_retention = require("../../retention-qugQGYuY.js");
6
+ const require_dist = require("../../dist-xCNJAQMh.js");
7
+ const require_retention = require("../../retention-DNDOPW7Z.js");
8
8
  //#region src/builtins/device-history/capture/point-writer.ts
9
9
  var FLUSH_INTERVAL_MS = 1e4;
10
10
  var MAX_BUFFER_AGE_MS = 6e4;
@@ -451,6 +451,13 @@ var MAX_SCOPES = 50;
451
451
  /** The power-meter series the energy key is derived from, meter first. */
452
452
  var ENERGY_METER_KEY = require_retention.capSeriesKey("power-meter", "kwhTotal");
453
453
  var ENERGY_POWER_KEY = require_retention.capSeriesKey("power-meter", "watts");
454
+ /**
455
+ * Series whose quantity is chosen per device (D944). Weather is not here:
456
+ * each weather field has its own quantity on the capability, and the catalog
457
+ * unit is that base. A past weather point stored in another unit cannot be
458
+ * rescaled — the unit at sample time was not kept.
459
+ */
460
+ var DEVICE_QUANTITY_KEYS = new Set([require_retention.capSeriesKey("numeric-sensor", "value")]);
454
461
  function retentionMsOf(tier, policy) {
455
462
  if (tier === "raw") return policy.rawDays * require_retention.DAY_MS;
456
463
  if (tier === "5m") return policy.fiveMinuteDays * require_retention.DAY_MS;
@@ -717,12 +724,26 @@ var HistoryQuery = class {
717
724
  /** The metric's fixed unit, else the device's own (a `unitFrom` field), else unknown. */
718
725
  async unitOf(subject, series, fixed, slices) {
719
726
  if (fixed !== null || subject.kind !== "device" || !series.key.startsWith("cap:")) return fixed;
727
+ if (DEVICE_QUANTITY_KEYS.has(series.key)) {
728
+ const quantity = await this.quantityOf(subject.deviceId);
729
+ if (quantity === null || quantity === "device" || quantity === "none") return null;
730
+ return require_dist.historyUnitFor(quantity) ?? null;
731
+ }
720
732
  const spec = this.specOf(series.key);
721
733
  if (spec?.unitFrom === void 0) return null;
722
734
  const capName = series.key.slice(require_retention.CAP_KEY_PREFIX.length, series.key.indexOf("."));
723
735
  const slice = await this.sliceOf(subject.deviceId, capName, slices);
724
736
  return slice === null ? null : require_retention.readUnit(slice, spec.unitFrom);
725
737
  }
738
+ quantityOf(deviceId) {
739
+ const pending = this.quantities.get(deviceId);
740
+ if (pending !== void 0) return pending;
741
+ const read = this.deps.readDeviceQuantity;
742
+ const run = read === void 0 ? Promise.resolve(null) : read(deviceId).catch(() => null);
743
+ this.quantities.set(deviceId, run);
744
+ return run;
745
+ }
746
+ quantities = /* @__PURE__ */ new Map();
726
747
  specOf(key) {
727
748
  const body = key.slice(require_retention.CAP_KEY_PREFIX.length);
728
749
  const dot = body.indexOf(".");
@@ -882,6 +903,59 @@ var OrphanSeries = class {
882
903
  }
883
904
  };
884
905
  //#endregion
906
+ //#region src/builtins/device-history/store/series-scale.ts
907
+ /**
908
+ * One-shot rescales of series whose every stored point shared one factor
909
+ * (D944). A reading already in the base unit is at or above `legacyMax` and
910
+ * is left alone, so a second pass does not multiply it again.
911
+ *
912
+ * Rollup `sum` / `tw_sum` follow the bucket when min, max and last are still
913
+ * in the old unit. Judging the sum itself would skip a day's samples of a
914
+ * large gauge (many × GiB already exceeds the ceiling) and keep the raw
915
+ * points and the rollup in different units.
916
+ */
917
+ var GIB_TO_BYTES = 1024 ** 3;
918
+ var DAY_TO_SECONDS = 86400;
919
+ /** A homelab array in GiB is below 1 MiB; the same array in bytes is not. */
920
+ var INFORMATION_LEGACY_MAX = 1024 ** 2;
921
+ /** Desiccant life in days is below this; the same life in seconds is not. */
922
+ var DESICCANT_LEGACY_MAX_DAYS = 1e4;
923
+ var DESICCANT_SERIES_KEY = "cap:pet-feeder.desiccantLeftDays";
924
+ var DESICCANT_SCALE_ID = "pet-feeder-desiccant-days-to-seconds";
925
+ var UNRAID_INFORMATION_KEYS = [
926
+ "array_total",
927
+ "array_used",
928
+ "array_free"
929
+ ];
930
+ var NUMERIC_SENSOR_SERIES_KEY = "cap:numeric-sensor.value";
931
+ /** The stored number after one rescale, or the same number when it is already base. */
932
+ function scaledMagnitude(value, factor, legacyMax) {
933
+ if (!Number.isFinite(value) || value === 0) return value;
934
+ if (Math.abs(value) >= legacyMax) return value;
935
+ return value * factor;
936
+ }
937
+ function rollupIsLegacy(row, legacyMax) {
938
+ const samples = [
939
+ row.min,
940
+ row.max,
941
+ ...row.last === null ? [] : [row.last]
942
+ ];
943
+ if (samples.some((value) => !Number.isFinite(value))) return false;
944
+ if (samples.every((value) => value === 0)) return false;
945
+ return samples.every((value) => Math.abs(value) < legacyMax);
946
+ }
947
+ /** Unraid array capacity children. Other numeric sensors on the same key stay put. */
948
+ function unraidInformationScale(deviceId, entityKey) {
949
+ if (!UNRAID_INFORMATION_KEYS.includes(entityKey)) return null;
950
+ return {
951
+ seriesKey: NUMERIC_SENSOR_SERIES_KEY,
952
+ deviceId,
953
+ factor: GIB_TO_BYTES,
954
+ legacyMax: INFORMATION_LEGACY_MAX,
955
+ migrationId: `unraid-gib-to-bytes:${String(deviceId)}`
956
+ };
957
+ }
958
+ //#endregion
885
959
  //#region src/builtins/device-history/store/history-schema.ts
886
960
  var HISTORY_NAMESPACE = "device-history";
887
961
  var COLLECTIONS = {
@@ -892,7 +966,9 @@ var COLLECTIONS = {
892
966
  rollup1h: "rollup-1h",
893
967
  rollup1d: "rollup-1d",
894
968
  optOuts: "device-opt-outs",
895
- departures: "device-departures"
969
+ departures: "device-departures",
970
+ /** One row per applied base-unit rescale, in history.db so it travels with the points. */
971
+ appliedScales: "applied-scales"
896
972
  };
897
973
  function rollupCollectionOf(tier) {
898
974
  if (tier === "5m") return COLLECTIONS.rollup5m;
@@ -1145,6 +1221,21 @@ var HISTORY_DECLARATIONS = [
1145
1221
  columns: ["device_id"],
1146
1222
  unique: true
1147
1223
  }]
1224
+ },
1225
+ {
1226
+ namespace: HISTORY_NAMESPACE,
1227
+ collection: COLLECTIONS.appliedScales,
1228
+ database: "history",
1229
+ columns: [{
1230
+ name: "id",
1231
+ type: "TEXT",
1232
+ primaryKey: true,
1233
+ notNull: true
1234
+ }, {
1235
+ name: "applied_at",
1236
+ type: "INTEGER",
1237
+ notNull: true
1238
+ }]
1148
1239
  }
1149
1240
  ];
1150
1241
  //#endregion
@@ -1561,7 +1652,98 @@ var HistoryStore = class {
1561
1652
  filter: { where: { device_id: deviceId } }
1562
1653
  });
1563
1654
  }
1655
+ /** True when this rescale already finished. A missing row is "not yet". */
1656
+ async scaleApplied(id) {
1657
+ return (await this.door.query.query({
1658
+ namespace: HISTORY_NAMESPACE,
1659
+ collection: COLLECTIONS.appliedScales,
1660
+ filter: {
1661
+ where: { id },
1662
+ limit: 1
1663
+ }
1664
+ })).length > 0;
1665
+ }
1666
+ async markScale(id, at) {
1667
+ await this.door.upsertMany.mutate({
1668
+ namespace: HISTORY_NAMESPACE,
1669
+ collection: COLLECTIONS.appliedScales,
1670
+ conflict: ["id"],
1671
+ records: [{
1672
+ id,
1673
+ data: {
1674
+ id,
1675
+ applied_at: at
1676
+ }
1677
+ }]
1678
+ });
1679
+ }
1680
+ /**
1681
+ * Multiply one series' raw points and rollups that are still in the old
1682
+ * unit. Points already at or above `legacyMax` stay. Returns how many rows
1683
+ * changed.
1684
+ */
1685
+ async scaleSeries(seriesId, factor, legacyMax) {
1686
+ const raw = await this.scaleRaw(seriesId, factor, legacyMax);
1687
+ let rollups = 0;
1688
+ for (const tier of [
1689
+ "5m",
1690
+ "1h",
1691
+ "1d"
1692
+ ]) rollups += await this.scaleRollupTier(tier, seriesId, factor, legacyMax);
1693
+ return raw + rollups;
1694
+ }
1695
+ async scaleRaw(seriesId, factor, legacyMax) {
1696
+ let after = -1;
1697
+ let changed = 0;
1698
+ for (;;) {
1699
+ const rows = await this.readRaw(seriesId, after + 1, Number.MAX_SAFE_INTEGER, READ_PAGE, "asc");
1700
+ if (rows.length === 0) break;
1701
+ const next = rows.flatMap((row) => {
1702
+ const value = scaledMagnitude(row.value, factor, legacyMax);
1703
+ return value === row.value ? [] : [{
1704
+ seriesId,
1705
+ ts: row.ts,
1706
+ value
1707
+ }];
1708
+ });
1709
+ if (next.length > 0) {
1710
+ await this.upsertRaw(next);
1711
+ changed += next.length;
1712
+ }
1713
+ const last = rows[rows.length - 1];
1714
+ if (last === void 0 || rows.length < 5e3) break;
1715
+ after = last.ts;
1716
+ }
1717
+ return changed;
1718
+ }
1719
+ async scaleRollupTier(tier, seriesId, factor, legacyMax) {
1720
+ let after = -1;
1721
+ let changed = 0;
1722
+ for (;;) {
1723
+ const rows = await this.readRollups(tier, seriesId, after + 1, Number.MAX_SAFE_INTEGER, READ_PAGE, "asc");
1724
+ if (rows.length === 0) break;
1725
+ const next = rows.flatMap((row) => rollupIsLegacy(row, legacyMax) ? [scaleRollup(row, factor)] : []);
1726
+ if (next.length > 0) {
1727
+ await this.upsertRollups(tier, next);
1728
+ changed += next.length;
1729
+ }
1730
+ const last = rows[rows.length - 1];
1731
+ if (last === void 0 || rows.length < 5e3) break;
1732
+ after = last.bucketStart;
1733
+ }
1734
+ return changed;
1735
+ }
1564
1736
  };
1737
+ function scaleRollup(row, factor) {
1738
+ return {
1739
+ ...row,
1740
+ sum: row.sum * factor,
1741
+ min: row.min * factor,
1742
+ max: row.max * factor,
1743
+ last: row.last === null ? null : row.last * factor,
1744
+ twSum: row.twSum === null ? null : row.twSum * factor
1745
+ };
1746
+ }
1565
1747
  //#endregion
1566
1748
  //#region src/builtins/device-history/rollup/rollup-math.ts
1567
1749
  /**
@@ -2131,7 +2313,8 @@ var HistoryRuntime = class {
2131
2313
  retention: deps.retention,
2132
2314
  catalog,
2133
2315
  itemCatalog,
2134
- readCapSlice: deps.readCapSlice
2316
+ readCapSlice: deps.readCapSlice,
2317
+ readDeviceQuantity: deps.readDeviceQuantity
2135
2318
  });
2136
2319
  this.departures = new require_retention.DepartureTracker(this.store, this.registry, logger);
2137
2320
  this.registry.onRemove((series) => {
@@ -2167,10 +2350,53 @@ var HistoryRuntime = class {
2167
2350
  await this.capture.seed();
2168
2351
  }
2169
2352
  async finishBoot() {
2353
+ await this.applyLegacyScales();
2170
2354
  const series = await this.rollup.bootRecompute(this.deps.clock());
2171
2355
  this.deps.logger.info("device-history: recent rollup window rebuilt", { meta: { series } });
2172
2356
  await this.reconcile();
2173
2357
  }
2358
+ /**
2359
+ * PetKit desiccant was stored in days; the quantity base is seconds.
2360
+ * Unraid array counters were stored in GiB; the base is bytes. Both
2361
+ * factors are honest because the unit never varied inside the series.
2362
+ * Weather and numeric-sensor are not rescaled: the unit at sample time
2363
+ * was not stored, so a factor would invent a number (D393).
2364
+ */
2365
+ async applyLegacyScales() {
2366
+ await this.scaleMatching(DESICCANT_SERIES_KEY, void 0, DAY_TO_SECONDS, DESICCANT_LEGACY_MAX_DAYS, DESICCANT_SCALE_ID);
2367
+ const list = this.deps.listLegacyScales;
2368
+ if (list === void 0) return;
2369
+ let subjects;
2370
+ try {
2371
+ subjects = await list();
2372
+ } catch (err) {
2373
+ this.deps.logger.warn("device-history: legacy scale listing failed — stored points unchanged", { meta: { error: err instanceof Error ? err.message : String(err) } });
2374
+ return;
2375
+ }
2376
+ for (const subject of subjects) await this.scaleMatching(subject.seriesKey, subject.deviceId, subject.factor, subject.legacyMax, subject.migrationId);
2377
+ }
2378
+ async scaleMatching(key, deviceId, factor, legacyMax, migrationId) {
2379
+ if (await this.store.scaleApplied(migrationId)) return;
2380
+ const subject = deviceId === void 0 ? void 0 : require_retention.subjectIdOf({
2381
+ kind: "device",
2382
+ deviceId
2383
+ });
2384
+ const series = this.registry.all().filter((row) => {
2385
+ if (row.key !== key) return false;
2386
+ return subject === void 0 || row.subject === subject;
2387
+ });
2388
+ let changed = 0;
2389
+ for (const row of series) changed += await this.store.scaleSeries(row.id, factor, legacyMax);
2390
+ await this.store.markScale(migrationId, this.deps.clock());
2391
+ if (changed > 0) this.deps.logger.info("device-history: rescaled a series into its base unit", {
2392
+ ...deviceId === void 0 ? {} : { tags: { deviceId } },
2393
+ meta: {
2394
+ migrationId,
2395
+ key,
2396
+ changed
2397
+ }
2398
+ });
2399
+ }
2174
2400
  startTimers() {
2175
2401
  const every = (ms, run) => {
2176
2402
  const timer = setInterval(() => {
@@ -2448,6 +2674,19 @@ var DeviceHistoryAddon = class extends require_dist.BaseAddon {
2448
2674
  dayDays: positiveOr(c.dayDays, 0) > 0 ? c.dayDays : null
2449
2675
  };
2450
2676
  }
2677
+ /** Unraid array counters still stored in GiB. A failed listing throws to the caller. */
2678
+ async legacyScales() {
2679
+ const devices = await this.ctx.api.deviceManager.listAll.query({});
2680
+ const scales = [];
2681
+ for (const device of devices) {
2682
+ if (device.addonId !== "provider-unraid") continue;
2683
+ const entityKey = device.config["entityKey"];
2684
+ if (typeof entityKey !== "string") continue;
2685
+ const scale = unraidInformationScale(device.id, entityKey);
2686
+ if (scale !== null) scales.push(scale);
2687
+ }
2688
+ return scales;
2689
+ }
2451
2690
  requireRuntime() {
2452
2691
  if (this.runtime === null) throw new Error("device-history is starting — the history store is not open yet");
2453
2692
  return this.runtime;
@@ -2541,6 +2780,11 @@ var DeviceHistoryAddon = class extends require_dist.BaseAddon {
2541
2780
  deviceId,
2542
2781
  capName
2543
2782
  }),
2783
+ readDeviceQuantity: async (deviceId) => {
2784
+ const quantity = (await api.deviceManager.getDevice.query({ deviceId }))?.sourceInfo?.quantity;
2785
+ return require_dist.isReadingQuantity(quantity) ? quantity : null;
2786
+ },
2787
+ listLegacyScales: () => this.legacyScales(),
2544
2788
  retention: () => this.retention(),
2545
2789
  logger: this.ctx.logger,
2546
2790
  clock: () => Date.now()