@camstack/addon-post-analysis 1.2.629 → 1.2.630
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/dist/{clip-model-registry-M_QM7l6j.js → clip-model-registry-DB0TSd14.js} +141 -86
- package/dist/{clip-model-registry-SrfKJ0e8.mjs → clip-model-registry-wZ4Nt5jo.mjs} +132 -83
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CUsaUp1g.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BjktQPk_.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BxPKSyZo.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DdbV-R1z.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-q1u97PmF.mjs → hostInit-BMCiXaWO.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +340 -266
- package/dist/pipeline-analytics/index.mjs +339 -265
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DxapkUgD.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-D7KeW_uB.mjs +0 -26
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as MediaFileKindEnum, $i as
|
|
1
|
+
import { $ as MediaFileKindEnum, $i as RECORD_QUERY_MAX, $n as consumableFormOf, $t as OpsLogEntrySchema, A as DeclaredDevices, Ai as summarizeReview, An as TrackSideSchema, Ar as isDefaultAlarmProfileId, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as viewerLinkBaseOf, Bn as applyModeDelaysEdits, Br as isScheduleActive, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as tuple, Ci as sensorRaisedOf, Cn as TimelapseRuleInputSchema, Cr as gifDeriveSpeed, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Di as subKindsOf, Dn as TrackPuritySchema, Dr as isAlertEpisodeSeverity, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as EventCategory, Ei as storageOccupancyCapability, En as TrackPlacementSchema, Er as isAlertEpisodeOpen, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as systemEventSummaryScheduleOf, Fn as alarmPanelCapability, Fr as isKnownTemplateVar, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as evaluateSensorEdge, Gn as audioMetricsCapability, Gr as linkedStepOf, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as zoneAnalyticsCapability, Hn as applySecureOnArmEdit, Hr as isSystemEventSummaryRule, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventSummarySplitOf, In as alertEpisodeId, Ir as isLabelSupplyStepId, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as errMsg, Jn as buildSubjectPath, Jr as ncMediaPolicyError, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as isSourceCap, Kn as audioModeOf, Kr as mayWriteToLocation, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as templateCarriesSummary, Ln as alertEpisodeKind, Lr as isMediaOwnerType, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as systemEventDeliveryProblems, Mn as activateAlarmProfile, Mr as isDrawableSubjectPath, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as systemEventFamilyProblems, Nn as addonWidgetsSourceCapability, Nr as isEventKindLabelKey, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oi as subjectPathSampleFromBox, On as TrackRelationKindSchema, Or as isAppLinkBase, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as systemEventFilterApplies, Pn as alarmDeviceSecureOnArm, Pr as isEventOwnerType, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as RECORD_MAX_DATA_BYTES, Qn as cloneAlarmSettings, Qr as parseAlertEpisodePayload, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as templateVarsFor, Rn as alertEpisodePayload, Rr as isRetiredClearKey, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as templateLiteral, Si as secureActuationFor, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as failureContributionCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as unknown, Ti as startMoverUnderClearance, Tn as TimelapseRuleSchema, Tr as isAlarmControllableCap, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, Un as armRemovalReconcile, Ur as isVectorIndexDimConflict, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as withSystemEventClasses, Vn as applyNonBlockingEdit, Vr as isSystemDelivery, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as canonicalHash, Wn as assertTimelapseCadences, Wr as kebabToCamel, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as CamProfileSchema, Xn as calendarEventLocalSpan, Xr as notificationRulesCapability, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as BaseAddon, Yn as buildViewerLink, Yr as normalizeNcMediaPolicy, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as InvalidRecordError, Zn as classifyRetrainReview, Zr as occupancyScope, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as object, _i as ruleBelongsToAlarm$1, _n as SceneMonitorSchema, _r as encodeVectorBase64, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as isDeviceScopedCap, ai as readMotionTriggeredBy, an as RetrainDismissedSchema, ar as deriveDetailCropRect, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as strictObject, bi as ruleKindOf, bn as SetTrackRelationInputSchema, br as evictionPolicyOfLocation, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as sleep$1, ci as resolveClipSearchMinScore, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as describeSystemEventDeliveryProblem, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, da as array, di as resolveModeNotifyPolicy, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceRemovalHookCapability, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as createEvent, ei as pickFaceRescoreIntervalMinutes, en as PackageLlmVerdictSchema, er as consumableLowPercentOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fa as boolean, fi as resolveNcMediaImage, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as number, gi as ruleAutoTriggersAlarm, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as looseObject, hi as rootSupplyServes, hn as SCENE_DIVERGED, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as hydrateSchema, ii as readDeviceStateFrom, in as RebuildObjectEmbeddingsRefusalSchema, ir as deleteAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ji as syncActiveAlarmProfileSettings, jn as TrackSourceSchema, jr as isDetectionMacroClass, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ki as subjectPathSvgPoints, kn as TrackRelationSchema, kr as isAudioClassName, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as ZodObject, li as resolveLocationMode, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventFamilyProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as literal, mi as retrainRevisionConflictMessage, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mr as embeddingRefusalOf, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as defineCustomActions, ni as plateGalleryCapability, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as cosineSimilarity$1, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as nodePin, oi as readTimelapseGeneratedAt, on as RetrainOriginSchema, or as derivePostTriggeredAction, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as discriminatedUnion, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as DeviceType, qn as buildEventKindDescriptor, qr as memoizePinnedFfmpeg, qt as NcSystemEventKindSchema, ra as defineRecordKinds, ri as projectSubjectPath, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as createAlarmProfile, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, si as renameAlarmProfile, sn as RetrainStatusSchema, sr as deriveRecordingMode, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as customAction, ti as pipelineAnalyticsCapability, tr as coreBlockIdFromAddonId, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as _enum, ui as resolveModeDelays, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as deviceAttentionKind, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as partialRecord, vi as ruleDeviceCompatCapQueries, vn as SetTrackPurityInputSchema, vr as ensureAlarmProfiles, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as union, wi as servedTemplateVars, wn as TimelapseRulePatchSchema, wr as gifFromMp4Args, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as string, xi as sceneMonitorCapability, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as faceGalleryCapability, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as record, yi as ruleGroupingApplies, yn as SetTrackPurityResultSchema, yr as eventKindLabel, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as vectorDimFromBase64, zn as applyBlockingEdit, zr as isRootSuppliableStepId, zt as NcRuleTargetSchema } from "../clip-model-registry-wZ4Nt5jo.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -9018,27 +9018,13 @@ var en_default = {
|
|
|
9018
9018
|
"system.device-consumable-low.title": "Consumable low — {{deviceName}}",
|
|
9019
9019
|
"system.device-consumable-low.body": "{{item}} on {{deviceName}} is at {{percentage}}%",
|
|
9020
9020
|
"system.device-consumable-low.body.unmeasured": "{{item}} on {{deviceName}} needs replacing",
|
|
9021
|
+
"system.device-consumable-low.body.tank": "{{item}} on {{deviceName}} is nearly empty",
|
|
9022
|
+
"system.device-consumable-low.body.tank-full": "{{item}} on {{deviceName}} is full",
|
|
9023
|
+
"system.device-consumable-low.body.product": "{{item}} on {{deviceName}} is running out",
|
|
9024
|
+
"system.device-consumable-low.body.bin": "{{item}} on {{deviceName}} is full",
|
|
9021
9025
|
"system.device-consumable-normal.title": "Consumable replaced — {{deviceName}}",
|
|
9022
9026
|
"system.device-consumable-normal.body": "{{item}} on {{deviceName}} is back to {{percentage}}%",
|
|
9023
9027
|
"system.device-consumable-normal.body.unmeasured": "{{item}} on {{deviceName}} is OK again",
|
|
9024
|
-
"system.device-tank-low.title": "Tank low — {{deviceName}}",
|
|
9025
|
-
"system.device-tank-full.title": "Tank full — {{deviceName}}",
|
|
9026
|
-
"system.device-tank-normal.title": "Tank OK — {{deviceName}}",
|
|
9027
|
-
"system.device-tank-low.body.cleanWater": "The clean water tank on {{deviceName}} is running low",
|
|
9028
|
-
"system.device-tank-full.body.cleanWater": "The clean water tank on {{deviceName}} is full",
|
|
9029
|
-
"system.device-tank-normal.body.cleanWater": "The clean water tank on {{deviceName}} is OK again",
|
|
9030
|
-
"system.device-tank-low.body.dirtyWater": "The dirty water tank on {{deviceName}} is running low",
|
|
9031
|
-
"system.device-tank-full.body.dirtyWater": "The dirty water tank on {{deviceName}} is full",
|
|
9032
|
-
"system.device-tank-normal.body.dirtyWater": "The dirty water tank on {{deviceName}} is OK again",
|
|
9033
|
-
"system.device-tank-low.body.detergent": "The detergent tank on {{deviceName}} is running low",
|
|
9034
|
-
"system.device-tank-full.body.detergent": "The detergent tank on {{deviceName}} is full",
|
|
9035
|
-
"system.device-tank-normal.body.detergent": "The detergent tank on {{deviceName}} is OK again",
|
|
9036
|
-
"system.device-tank-low.body.dustBin": "The dust bin on {{deviceName}} is running low",
|
|
9037
|
-
"system.device-tank-full.body.dustBin": "The dust bin on {{deviceName}} is full",
|
|
9038
|
-
"system.device-tank-normal.body.dustBin": "The dust bin on {{deviceName}} is OK again",
|
|
9039
|
-
"system.device-tank-low.body.item": "{{item}} on {{deviceName}} is running low",
|
|
9040
|
-
"system.device-tank-full.body.item": "{{item}} on {{deviceName}} is full",
|
|
9041
|
-
"system.device-tank-normal.body.item": "{{item}} on {{deviceName}} is OK again",
|
|
9042
9028
|
"system.camera-online.title": "Device online",
|
|
9043
9029
|
"system.camera-online.body": "Device {{deviceName}} is online",
|
|
9044
9030
|
"system.camera-offline.title": "Device offline",
|
|
@@ -9193,8 +9179,7 @@ var en_default = {
|
|
|
9193
9179
|
"summary.section.device-state": "Offline or off",
|
|
9194
9180
|
"summary.section.streams": "Streams offline",
|
|
9195
9181
|
"summary.section.battery": "Low batteries",
|
|
9196
|
-
"summary.section.consumables": "Consumables
|
|
9197
|
-
"summary.section.tanks": "Tanks low",
|
|
9182
|
+
"summary.section.consumables": "Consumables",
|
|
9198
9183
|
"summary.section.detection-blind": "Detecting nothing",
|
|
9199
9184
|
"summary.section.nodes": "Nodes with problems",
|
|
9200
9185
|
"summary.section.addons": "Addons stopped",
|
|
@@ -9212,7 +9197,12 @@ var en_default = {
|
|
|
9212
9197
|
"summary.line.addon-crash-loop": "{{nodeId}} — {{addons}} stopped after repeated crashes (since {{since}})",
|
|
9213
9198
|
"summary.line.update": "{{packageName}} {{currentVersion}} → {{latestVersion}}",
|
|
9214
9199
|
"summary.line.wrapper-update": "Container {{currentVersion}} → {{latestVersion}}",
|
|
9215
|
-
"summary.line.
|
|
9200
|
+
"summary.line.consumable.part": "{{item}} {{percentage}}%",
|
|
9201
|
+
"summary.line.consumable.part.unmeasured": "{{item}} needs replacing",
|
|
9202
|
+
"summary.line.consumable.tank": "{{item}} nearly empty",
|
|
9203
|
+
"summary.line.consumable.tank-full": "{{item}} full",
|
|
9204
|
+
"summary.line.consumable.product": "{{item}} running out",
|
|
9205
|
+
"summary.line.consumable.bin": "{{item}} full",
|
|
9216
9206
|
"calendar-events.title": "{{rule}}: {{count}} upcoming",
|
|
9217
9207
|
"calendar-events.empty": "No events in the next {{horizonDays}} day(s).",
|
|
9218
9208
|
"calendar-events.day.today": "Today — {{date}}",
|
|
@@ -9424,27 +9414,13 @@ var it_default = {
|
|
|
9424
9414
|
"system.device-consumable-low.title": "Consumabile basso — {{deviceName}}",
|
|
9425
9415
|
"system.device-consumable-low.body": "{{item}} su {{deviceName}} è al {{percentage}}%",
|
|
9426
9416
|
"system.device-consumable-low.body.unmeasured": "{{item}} su {{deviceName}} va sostituito",
|
|
9417
|
+
"system.device-consumable-low.body.tank": "{{item}} di {{deviceName}} è quasi vuota",
|
|
9418
|
+
"system.device-consumable-low.body.tank-full": "{{item}} di {{deviceName}} è piena",
|
|
9419
|
+
"system.device-consumable-low.body.product": "{{item}} di {{deviceName}} è in esaurimento",
|
|
9420
|
+
"system.device-consumable-low.body.bin": "{{item}} di {{deviceName}} è pieno",
|
|
9427
9421
|
"system.device-consumable-normal.title": "Consumabile sostituito — {{deviceName}}",
|
|
9428
9422
|
"system.device-consumable-normal.body": "{{item}} su {{deviceName}} è tornato al {{percentage}}%",
|
|
9429
9423
|
"system.device-consumable-normal.body.unmeasured": "{{item}} su {{deviceName}} è di nuovo a posto",
|
|
9430
|
-
"system.device-tank-low.title": "Serbatoio quasi vuoto — {{deviceName}}",
|
|
9431
|
-
"system.device-tank-full.title": "Serbatoio pieno — {{deviceName}}",
|
|
9432
|
-
"system.device-tank-normal.title": "Serbatoio a posto — {{deviceName}}",
|
|
9433
|
-
"system.device-tank-low.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è quasi vuoto",
|
|
9434
|
-
"system.device-tank-full.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è pieno",
|
|
9435
|
-
"system.device-tank-normal.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è di nuovo a posto",
|
|
9436
|
-
"system.device-tank-low.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è quasi vuoto",
|
|
9437
|
-
"system.device-tank-full.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è pieno",
|
|
9438
|
-
"system.device-tank-normal.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è di nuovo a posto",
|
|
9439
|
-
"system.device-tank-low.body.detergent": "Il serbatoio del detergente di {{deviceName}} è quasi vuoto",
|
|
9440
|
-
"system.device-tank-full.body.detergent": "Il serbatoio del detergente di {{deviceName}} è pieno",
|
|
9441
|
-
"system.device-tank-normal.body.detergent": "Il serbatoio del detergente di {{deviceName}} è di nuovo a posto",
|
|
9442
|
-
"system.device-tank-low.body.dustBin": "Il contenitore della polvere di {{deviceName}} è quasi vuoto",
|
|
9443
|
-
"system.device-tank-full.body.dustBin": "Il contenitore della polvere di {{deviceName}} è pieno",
|
|
9444
|
-
"system.device-tank-normal.body.dustBin": "Il contenitore della polvere di {{deviceName}} è di nuovo a posto",
|
|
9445
|
-
"system.device-tank-low.body.item": "{{item}} di {{deviceName}} sta finendo",
|
|
9446
|
-
"system.device-tank-full.body.item": "{{item}} di {{deviceName}} è pieno",
|
|
9447
|
-
"system.device-tank-normal.body.item": "{{item}} di {{deviceName}} è di nuovo a posto",
|
|
9448
9424
|
"system.camera-online.title": "Dispositivo online",
|
|
9449
9425
|
"system.camera-online.body": "Il dispositivo {{deviceName}} è online",
|
|
9450
9426
|
"system.camera-offline.title": "Dispositivo offline",
|
|
@@ -9599,8 +9575,7 @@ var it_default = {
|
|
|
9599
9575
|
"summary.section.device-state": "Offline o spenti",
|
|
9600
9576
|
"summary.section.streams": "Stream offline",
|
|
9601
9577
|
"summary.section.battery": "Batterie scariche",
|
|
9602
|
-
"summary.section.consumables": "Consumabili
|
|
9603
|
-
"summary.section.tanks": "Serbatoi bassi",
|
|
9578
|
+
"summary.section.consumables": "Consumabili",
|
|
9604
9579
|
"summary.section.detection-blind": "Senza detection",
|
|
9605
9580
|
"summary.section.nodes": "Nodi con problemi",
|
|
9606
9581
|
"summary.section.addons": "Addon fermati",
|
|
@@ -9618,7 +9593,12 @@ var it_default = {
|
|
|
9618
9593
|
"summary.line.addon-crash-loop": "{{nodeId}} — {{addons}} fermati dopo crash ripetuti (da {{since}})",
|
|
9619
9594
|
"summary.line.update": "{{packageName}} {{currentVersion}} → {{latestVersion}}",
|
|
9620
9595
|
"summary.line.wrapper-update": "Container {{currentVersion}} → {{latestVersion}}",
|
|
9621
|
-
"summary.line.
|
|
9596
|
+
"summary.line.consumable.part": "{{item}} {{percentage}}%",
|
|
9597
|
+
"summary.line.consumable.part.unmeasured": "{{item}} da sostituire",
|
|
9598
|
+
"summary.line.consumable.tank": "{{item}} quasi vuota",
|
|
9599
|
+
"summary.line.consumable.tank-full": "{{item}} piena",
|
|
9600
|
+
"summary.line.consumable.product": "{{item}} in esaurimento",
|
|
9601
|
+
"summary.line.consumable.bin": "{{item}} pieno",
|
|
9622
9602
|
"calendar-events.title": "{{rule}}: {{count}} in arrivo",
|
|
9623
9603
|
"calendar-events.empty": "Nessun evento nei prossimi {{horizonDays}} giorno/i.",
|
|
9624
9604
|
"calendar-events.day.today": "Oggi — {{date}}",
|
|
@@ -11900,9 +11880,9 @@ function systemEventKeys(kind) {
|
|
|
11900
11880
|
titleKey: `system.${kind}.title`,
|
|
11901
11881
|
bodyKey: `system.${kind}.body.ignored`
|
|
11902
11882
|
};
|
|
11903
|
-
if (
|
|
11883
|
+
if (kind === "device-consumable-low") return {
|
|
11904
11884
|
titleKey: `system.${kind}.title`,
|
|
11905
|
-
bodyKey:
|
|
11885
|
+
bodyKey: "system.device-consumable-low.body.tank"
|
|
11906
11886
|
};
|
|
11907
11887
|
if (kind === "rule-broken") return {
|
|
11908
11888
|
titleKey: `system.${kind}.title`,
|
|
@@ -71475,6 +71455,25 @@ Object.freeze({
|
|
|
71475
71455
|
"path": "reason",
|
|
71476
71456
|
"valueType": "string"
|
|
71477
71457
|
}],
|
|
71458
|
+
"running-state": [
|
|
71459
|
+
{
|
|
71460
|
+
"path": "running",
|
|
71461
|
+
"valueType": "boolean"
|
|
71462
|
+
},
|
|
71463
|
+
{
|
|
71464
|
+
"path": "since",
|
|
71465
|
+
"valueType": "number"
|
|
71466
|
+
},
|
|
71467
|
+
{
|
|
71468
|
+
"path": "source",
|
|
71469
|
+
"valueType": "enum",
|
|
71470
|
+
"enumValues": ["native", "assigned"]
|
|
71471
|
+
},
|
|
71472
|
+
{
|
|
71473
|
+
"path": "phase",
|
|
71474
|
+
"valueType": "string"
|
|
71475
|
+
}
|
|
71476
|
+
],
|
|
71478
71477
|
"script-runner": [
|
|
71479
71478
|
{
|
|
71480
71479
|
"path": "isRunning",
|
|
@@ -80982,83 +80981,6 @@ function deriveConsumableBand(input) {
|
|
|
80982
80981
|
return value <= input.thresholdPercent ? "low" : "normal";
|
|
80983
80982
|
}
|
|
80984
80983
|
//#endregion
|
|
80985
|
-
//#region src/notification-center/alerts/tank-band.ts
|
|
80986
|
-
/**
|
|
80987
|
-
* A TANK reduced to the only thing worth storing: its band (D642).
|
|
80988
|
-
*
|
|
80989
|
-
* A tank is a `supply` or `waste` item of a device's `consumables` slice
|
|
80990
|
-
* (D915 — before it, the four vacuum tanks lived on `vacuum-control`): a
|
|
80991
|
-
* vacuum's clean water, dirty water, detergent and dust bin, a dishwasher's
|
|
80992
|
-
* salt and rinse aid. `consumable-routing.ts` narrows the item's status to the
|
|
80993
|
-
* band vocabulary below. The operator asked for them to notify like
|
|
80994
|
-
* consumables: a supply running low, a waste tank full.
|
|
80995
|
-
*
|
|
80996
|
-
* ## The verdict is the provider's
|
|
80997
|
-
*
|
|
80998
|
-
* A tank's `status` is the device's own sensor (`ok` | `low` | `full`),
|
|
80999
|
-
* and it is the ONLY thing judged. The hub has no threshold for a tank level:
|
|
81000
|
-
* the rule for thresholds is that they belong to whoever generates the event,
|
|
81001
|
-
* and a tank that says `low` has already decided. A tank that reports only a
|
|
81002
|
-
* `level` is therefore not judged at all (`null`) rather than judged against a
|
|
81003
|
-
* number the hub would have to invent.
|
|
81004
|
-
*
|
|
81005
|
-
* ## `null` is the third answer, and it is not `normal`
|
|
81006
|
-
*
|
|
81007
|
-
* An unknown status says nothing — it neither opens nor closes an episode, for
|
|
81008
|
-
* the reason `battery-band.ts` gives: a reading nobody took is not evidence
|
|
81009
|
-
* that anything changed.
|
|
81010
|
-
*
|
|
81011
|
-
* Pure. No clock, no I/O.
|
|
81012
|
-
*/
|
|
81013
|
-
/**
|
|
81014
|
-
* The tanks with a sentence of their own (`system.device-tank-*.body.<key>`),
|
|
81015
|
-
* by their item key — the four a vacuum reports. Any other tank (salt, rinse
|
|
81016
|
-
* aid, …) is worded by its label (`body.item`).
|
|
81017
|
-
*/
|
|
81018
|
-
var NC_TANK_KEYS = [
|
|
81019
|
-
"cleanWater",
|
|
81020
|
-
"dirtyWater",
|
|
81021
|
-
"detergent",
|
|
81022
|
-
"dustBin"
|
|
81023
|
-
];
|
|
81024
|
-
function isNamedTankKey(key) {
|
|
81025
|
-
return NC_TANK_KEYS.some((k) => k === key);
|
|
81026
|
-
}
|
|
81027
|
-
/**
|
|
81028
|
-
* How long a tank change must HOLD before it counts (D642 review M2).
|
|
81029
|
-
*
|
|
81030
|
-
* A float switch sloshing at the edge of `low` / `full` flips its status back
|
|
81031
|
-
* and forth while the robot moves; each flip is a durable transition, and
|
|
81032
|
-
* without a hold each one is a raise and a recovery on the operator's phone.
|
|
81033
|
-
* One minute is two reload ticks: long enough to outlast a slosh, short enough
|
|
81034
|
-
* that a genuinely full dust bin is said within a minute and a half.
|
|
81035
|
-
*/
|
|
81036
|
-
var NC_TANK_HOLD_MS = 6e4;
|
|
81037
|
-
function deriveTankBand(reading) {
|
|
81038
|
-
switch (reading.status) {
|
|
81039
|
-
case "low": return "low";
|
|
81040
|
-
case "full": return "full";
|
|
81041
|
-
case "ok": return "normal";
|
|
81042
|
-
case null: return null;
|
|
81043
|
-
}
|
|
81044
|
-
}
|
|
81045
|
-
/** `tank:<deviceId>:<key>` — the subject of one tank's episode. */
|
|
81046
|
-
function tankSubject(deviceId, key) {
|
|
81047
|
-
return `tank:${String(deviceId)}:${key}`;
|
|
81048
|
-
}
|
|
81049
|
-
/** The parts of a tank subject, or `null` when it is not one. */
|
|
81050
|
-
function parseTankSubject(subject) {
|
|
81051
|
-
const match = /^tank:(\d+):(.+)$/.exec(subject);
|
|
81052
|
-
if (match === null) return null;
|
|
81053
|
-
const deviceId = Number(match[1]);
|
|
81054
|
-
const key = match[2];
|
|
81055
|
-
if (!Number.isSafeInteger(deviceId) || key === void 0) return null;
|
|
81056
|
-
return {
|
|
81057
|
-
deviceId,
|
|
81058
|
-
key
|
|
81059
|
-
};
|
|
81060
|
-
}
|
|
81061
|
-
//#endregion
|
|
81062
80984
|
//#region src/notification-center/device-directory.ts
|
|
81063
80985
|
/**
|
|
81064
80986
|
* WHO a device id is — the mirrored directory the system-event intake reads.
|
|
@@ -81820,6 +81742,7 @@ function incomingFromConsumableBand(input) {
|
|
|
81820
81742
|
textParams: {
|
|
81821
81743
|
...deviceTextParams(input.deviceId, input.identity),
|
|
81822
81744
|
item: input.item.label,
|
|
81745
|
+
form: "part",
|
|
81823
81746
|
...percentage !== null ? { percentage: String(percentage) } : {}
|
|
81824
81747
|
},
|
|
81825
81748
|
deviceId: input.deviceId,
|
|
@@ -81827,29 +81750,33 @@ function incomingFromConsumableBand(input) {
|
|
|
81827
81750
|
...input.identity !== void 0 ? { deviceType: input.identity.type } : {}
|
|
81828
81751
|
});
|
|
81829
81752
|
}
|
|
81753
|
+
/** Instant body keys for a consumable form (D934). */
|
|
81754
|
+
function consumableLowBodyKey(form, attention, measured) {
|
|
81755
|
+
if (form === "part") return measured ? "system.device-consumable-low.body" : "system.device-consumable-low.body.unmeasured";
|
|
81756
|
+
if (form === "tank") return attention === "full" ? "system.device-consumable-low.body.tank-full" : "system.device-consumable-low.body.tank";
|
|
81757
|
+
if (form === "product") return "system.device-consumable-low.body.product";
|
|
81758
|
+
return "system.device-consumable-low.body.bin";
|
|
81759
|
+
}
|
|
81830
81760
|
/**
|
|
81831
|
-
* A
|
|
81761
|
+
* A supply / waste item judged on the provider's verdict (D934).
|
|
81832
81762
|
*
|
|
81833
|
-
*
|
|
81834
|
-
*
|
|
81835
|
-
* and the recovery keeps it. For the four named vacuum tanks the BODY VARIANT
|
|
81836
|
-
* names the tank in the reader's language — the params are frozen before the
|
|
81837
|
-
* language is known, so the tank's name cannot be a param; `tank` carries the
|
|
81838
|
-
* stable token for a custom template. Any other tank (a dishwasher's salt, …)
|
|
81839
|
-
* is worded by the provider's label through `body.item`.
|
|
81763
|
+
* Subject `consumable:<deviceId>:<key>` — no `consumableLowPercent` so rules
|
|
81764
|
+
* at 15 % still hear salt and water.
|
|
81840
81765
|
*/
|
|
81841
|
-
function
|
|
81842
|
-
const kind = input.band === "low" ? "device-
|
|
81766
|
+
function incomingFromProviderConsumableBand(input) {
|
|
81767
|
+
const kind = input.band === "low" ? "device-consumable-low" : "device-consumable-normal";
|
|
81768
|
+
const bodyKey = input.band === "low" ? consumableLowBodyKey(input.item.form, input.attention, false) : "system.device-consumable-normal.body.unmeasured";
|
|
81843
81769
|
return systemSubject({
|
|
81844
81770
|
facts: input.facts,
|
|
81845
81771
|
kind,
|
|
81846
|
-
subject:
|
|
81772
|
+
subject: legacyConsumableSubject(input.deviceId, input.item.key),
|
|
81847
81773
|
titleKey: `system.${kind}.title`,
|
|
81848
|
-
bodyKey
|
|
81774
|
+
bodyKey,
|
|
81849
81775
|
textParams: {
|
|
81850
81776
|
...deviceTextParams(input.deviceId, input.identity),
|
|
81851
|
-
|
|
81852
|
-
|
|
81777
|
+
item: input.item.label,
|
|
81778
|
+
form: input.item.form,
|
|
81779
|
+
attention: input.attention
|
|
81853
81780
|
},
|
|
81854
81781
|
deviceId: input.deviceId,
|
|
81855
81782
|
...input.identity !== void 0 ? { deviceType: input.identity.type } : {}
|
|
@@ -82734,13 +82661,43 @@ function deliveryForKind(kind) {
|
|
|
82734
82661
|
return kind;
|
|
82735
82662
|
}
|
|
82736
82663
|
//#endregion
|
|
82737
|
-
//#region src/notification-center/alerts/
|
|
82664
|
+
//#region src/notification-center/alerts/tank-band.ts
|
|
82738
82665
|
/**
|
|
82739
|
-
*
|
|
82740
|
-
*
|
|
82741
|
-
*
|
|
82742
|
-
*
|
|
82666
|
+
* How long a tank change must HOLD before it counts (D642 review M2).
|
|
82667
|
+
*
|
|
82668
|
+
* A float switch sloshing at the edge of `low` / `full` flips its status back
|
|
82669
|
+
* and forth while the robot moves; each flip is a durable transition, and
|
|
82670
|
+
* without a hold each one is a raise and a recovery on the operator's phone.
|
|
82671
|
+
* One minute is two reload ticks: long enough to outlast a slosh, short enough
|
|
82672
|
+
* that a genuinely full dust bin is said within a minute and a half.
|
|
82743
82673
|
*/
|
|
82674
|
+
var NC_TANK_HOLD_MS = 6e4;
|
|
82675
|
+
function deriveTankBand(reading) {
|
|
82676
|
+
switch (reading.status) {
|
|
82677
|
+
case "low": return "low";
|
|
82678
|
+
case "full": return "full";
|
|
82679
|
+
case "ok": return "normal";
|
|
82680
|
+
case null: return null;
|
|
82681
|
+
}
|
|
82682
|
+
}
|
|
82683
|
+
/** `tank:<deviceId>:<key>` — the subject of one tank's episode. */
|
|
82684
|
+
function tankSubject(deviceId, key) {
|
|
82685
|
+
return `tank:${String(deviceId)}:${key}`;
|
|
82686
|
+
}
|
|
82687
|
+
/** The parts of a tank subject, or `null` when it is not one. */
|
|
82688
|
+
function parseTankSubject(subject) {
|
|
82689
|
+
const match = /^tank:(\d+):(.+)$/.exec(subject);
|
|
82690
|
+
if (match === null) return null;
|
|
82691
|
+
const deviceId = Number(match[1]);
|
|
82692
|
+
const key = match[2];
|
|
82693
|
+
if (!Number.isSafeInteger(deviceId) || key === void 0) return null;
|
|
82694
|
+
return {
|
|
82695
|
+
deviceId,
|
|
82696
|
+
key
|
|
82697
|
+
};
|
|
82698
|
+
}
|
|
82699
|
+
//#endregion
|
|
82700
|
+
//#region src/notification-center/alerts/open-alert.ts
|
|
82744
82701
|
function unmeasuredEpisode(input) {
|
|
82745
82702
|
const deviceId = alertSubjectDeviceId(input.subject);
|
|
82746
82703
|
if (deviceId === void 0) return null;
|
|
@@ -82758,7 +82715,23 @@ function unmeasuredEpisode(input) {
|
|
|
82758
82715
|
identity
|
|
82759
82716
|
});
|
|
82760
82717
|
const parts = parseConsumableSubject(input.subject);
|
|
82761
|
-
if (
|
|
82718
|
+
if (parts !== null && parts.legacy) {
|
|
82719
|
+
const attention = input.detail === "full" ? "full" : "low";
|
|
82720
|
+
const form = providerFormFromDetail(input.detail);
|
|
82721
|
+
return incomingFromProviderConsumableBand({
|
|
82722
|
+
facts,
|
|
82723
|
+
deviceId,
|
|
82724
|
+
band: "low",
|
|
82725
|
+
item: {
|
|
82726
|
+
key: parts.key,
|
|
82727
|
+
label: parts.key,
|
|
82728
|
+
form
|
|
82729
|
+
},
|
|
82730
|
+
attention,
|
|
82731
|
+
identity
|
|
82732
|
+
});
|
|
82733
|
+
}
|
|
82734
|
+
if (parts !== null) return incomingFromConsumableBand({
|
|
82762
82735
|
facts,
|
|
82763
82736
|
deviceId,
|
|
82764
82737
|
band: "low",
|
|
@@ -82772,25 +82745,30 @@ function unmeasuredEpisode(input) {
|
|
|
82772
82745
|
identity
|
|
82773
82746
|
});
|
|
82774
82747
|
const tank = parseTankSubject(input.subject);
|
|
82775
|
-
if (tank
|
|
82776
|
-
|
|
82777
|
-
|
|
82778
|
-
|
|
82779
|
-
|
|
82780
|
-
|
|
82781
|
-
|
|
82782
|
-
|
|
82748
|
+
if (tank !== null && tank.deviceId === deviceId) {
|
|
82749
|
+
const attention = input.detail === "full" ? "full" : "low";
|
|
82750
|
+
return incomingFromProviderConsumableBand({
|
|
82751
|
+
facts,
|
|
82752
|
+
deviceId,
|
|
82753
|
+
band: "low",
|
|
82754
|
+
item: {
|
|
82755
|
+
key: tank.key,
|
|
82756
|
+
label: tank.key,
|
|
82757
|
+
form: "tank"
|
|
82758
|
+
},
|
|
82759
|
+
attention,
|
|
82760
|
+
identity
|
|
82761
|
+
});
|
|
82762
|
+
}
|
|
82763
|
+
return null;
|
|
82764
|
+
}
|
|
82765
|
+
function providerFormFromDetail(detail) {
|
|
82766
|
+
if (detail === "full") return "bin";
|
|
82767
|
+
return "tank";
|
|
82783
82768
|
}
|
|
82784
|
-
/**
|
|
82785
|
-
* One alerting row as an open episode, or `null` when it cannot be worded (the
|
|
82786
|
-
* caller logs it). A legacy consumable row is skipped: it is adopted or
|
|
82787
|
-
* retired first. A tank whose band the row remembers is worded by it
|
|
82788
|
-
* (review M6), even without a reading.
|
|
82789
|
-
*/
|
|
82790
82769
|
function openAlertOf(row, input) {
|
|
82791
82770
|
const parts = parseConsumableSubject(row.subject);
|
|
82792
|
-
|
|
82793
|
-
const family = parts !== null ? "consumable" : row.subject.startsWith("tank:") ? "tank" : "battery";
|
|
82771
|
+
const family = parts !== null || row.subject.startsWith("tank:") ? "consumable" : "battery";
|
|
82794
82772
|
const subject = (input.restated ?? unmeasuredEpisode({
|
|
82795
82773
|
subject: row.subject,
|
|
82796
82774
|
family,
|
|
@@ -82799,7 +82777,7 @@ function openAlertOf(row, input) {
|
|
|
82799
82777
|
...row.detail !== void 0 ? { detail: row.detail } : {}
|
|
82800
82778
|
}))?.subject.systemEvent;
|
|
82801
82779
|
if (subject === void 0) return null;
|
|
82802
|
-
const measured = input.restated !== null ||
|
|
82780
|
+
const measured = input.restated !== null || row.detail !== void 0 || parts !== null && !parts.legacy && subject.textParams?.["percentage"] !== void 0;
|
|
82803
82781
|
return {
|
|
82804
82782
|
family,
|
|
82805
82783
|
since: row.since,
|
|
@@ -82915,8 +82893,8 @@ var NcDeviceAlerts = class {
|
|
|
82915
82893
|
* reminder sweep and for judging a threshold a rule gains later (D642).
|
|
82916
82894
|
*/
|
|
82917
82895
|
lastConsumable = /* @__PURE__ */ new Map();
|
|
82918
|
-
/**
|
|
82919
|
-
|
|
82896
|
+
/** Provider-judged items, keyed by episode subject (`consumable:<id>:<key>`). */
|
|
82897
|
+
lastProvider = /* @__PURE__ */ new Map();
|
|
82920
82898
|
/**
|
|
82921
82899
|
* Tank changes on HOLD (review M2): a band that would move the episode,
|
|
82922
82900
|
* first seen at `since`. It counts once it has held for `NC_TANK_HOLD_MS`,
|
|
@@ -82988,6 +82966,15 @@ var NcDeviceAlerts = class {
|
|
|
82988
82966
|
* genuine `replace` would then read as a transition out of a state nobody
|
|
82989
82967
|
* observed.
|
|
82990
82968
|
*/
|
|
82969
|
+
/** One item from the consumables slice — every form (D934). */
|
|
82970
|
+
observeConsumableItem(deviceId, sliceItem) {
|
|
82971
|
+
this.closeLegacyTankEpisode(deviceId, sliceItem.key);
|
|
82972
|
+
if (sliceItem.wear !== null) {
|
|
82973
|
+
this.observeConsumable(deviceId, sliceItem.wear);
|
|
82974
|
+
return;
|
|
82975
|
+
}
|
|
82976
|
+
this.observeProviderConsumable(deviceId, sliceItem);
|
|
82977
|
+
}
|
|
82991
82978
|
observeConsumable(deviceId, item) {
|
|
82992
82979
|
this.lastConsumable.set(itemKeyOf(deviceId, item.key), {
|
|
82993
82980
|
deviceId,
|
|
@@ -83004,31 +82991,51 @@ var NcDeviceAlerts = class {
|
|
|
83004
82991
|
const settings = this.deps.settings();
|
|
83005
82992
|
for (const thresholdPercent of thresholds) this.judgeConsumable(deviceId, item, thresholdPercent, settings);
|
|
83006
82993
|
}
|
|
83007
|
-
/**
|
|
83008
|
-
|
|
83009
|
-
|
|
83010
|
-
*
|
|
83011
|
-
* The provider's verdict is the band (`tank-band.ts`); an unknown status
|
|
83012
|
-
* leaves everything untouched, an open episode included.
|
|
83013
|
-
*/
|
|
83014
|
-
observeTank(deviceId, tank) {
|
|
83015
|
-
const band = deriveTankBand(tank);
|
|
82994
|
+
/** A supply / waste / tank-form item — provider verdict only (D934). */
|
|
82995
|
+
observeProviderConsumable(deviceId, sliceItem) {
|
|
82996
|
+
const band = deriveTankBand(providerTankReading(sliceItem));
|
|
83016
82997
|
if (band === null) return;
|
|
83017
|
-
const subject =
|
|
83018
|
-
|
|
82998
|
+
const subject = legacyConsumableSubject(deviceId, sliceItem.key);
|
|
82999
|
+
const attention = band === "full" ? "full" : "low";
|
|
83000
|
+
this.lastProvider.set(subject, {
|
|
83019
83001
|
deviceId,
|
|
83020
|
-
|
|
83002
|
+
item: sliceItem,
|
|
83003
|
+
attention
|
|
83021
83004
|
});
|
|
83022
83005
|
if (!this.tankChangeHeld(subject, deviceId, band)) return;
|
|
83023
|
-
this.apply(subject, band !== "normal", this.deps.settings(), (facts, alerting) =>
|
|
83006
|
+
this.apply(subject, band !== "normal", this.deps.settings(), (facts, alerting) => incomingFromProviderConsumableBand({
|
|
83024
83007
|
facts,
|
|
83025
83008
|
deviceId,
|
|
83026
|
-
band: alerting ?
|
|
83027
|
-
|
|
83028
|
-
|
|
83009
|
+
band: alerting ? "low" : "normal",
|
|
83010
|
+
item: {
|
|
83011
|
+
key: sliceItem.key,
|
|
83012
|
+
label: sliceItem.label,
|
|
83013
|
+
form: sliceItem.form
|
|
83014
|
+
},
|
|
83015
|
+
attention,
|
|
83029
83016
|
identity: this.deps.lookup(deviceId)
|
|
83030
83017
|
}), band === "normal" ? void 0 : band);
|
|
83031
83018
|
}
|
|
83019
|
+
/** A leftover `tank:<deviceId>:<key>` row is closed silently when the item is seen as a consumable. */
|
|
83020
|
+
closeLegacyTankEpisode(deviceId, key) {
|
|
83021
|
+
const subject = tankSubject(deviceId, key);
|
|
83022
|
+
const row = this.deps.rows.get(subject);
|
|
83023
|
+
if (row === void 0 || !row.alerting) return;
|
|
83024
|
+
this.logger()?.info("legacy tank episode closed — the consumables cap owns this item now (D934)", {
|
|
83025
|
+
tags: { deviceId },
|
|
83026
|
+
meta: {
|
|
83027
|
+
subject,
|
|
83028
|
+
key
|
|
83029
|
+
}
|
|
83030
|
+
});
|
|
83031
|
+
this.deps.rows.put({
|
|
83032
|
+
...row,
|
|
83033
|
+
alerting: false
|
|
83034
|
+
});
|
|
83035
|
+
this.lastProvider.delete(legacyConsumableSubject(deviceId, key));
|
|
83036
|
+
this.tankHold.delete(subject);
|
|
83037
|
+
this.tankHold.delete(legacyConsumableSubject(deviceId, key));
|
|
83038
|
+
}
|
|
83032
83039
|
/**
|
|
83033
83040
|
* Has this tank band held long enough to move its episode? A band that
|
|
83034
83041
|
* would not move it (a repeat, or low ⇄ full inside one open episode) passes
|
|
@@ -83069,12 +83076,12 @@ var NcDeviceAlerts = class {
|
|
|
83069
83076
|
const nowMs = this.now();
|
|
83070
83077
|
for (const [subject, held] of [...this.tankHold]) {
|
|
83071
83078
|
if (nowMs - held.since < NC_TANK_HOLD_MS) continue;
|
|
83072
|
-
const reading = this.
|
|
83079
|
+
const reading = this.lastProvider.get(subject);
|
|
83073
83080
|
if (reading === void 0) {
|
|
83074
83081
|
this.tankHold.delete(subject);
|
|
83075
83082
|
continue;
|
|
83076
83083
|
}
|
|
83077
|
-
this.
|
|
83084
|
+
this.observeProviderConsumable(reading.deviceId, reading.item);
|
|
83078
83085
|
}
|
|
83079
83086
|
}
|
|
83080
83087
|
/** One item against ONE threshold: its own episode, its own hysteresis. */
|
|
@@ -83198,14 +83205,14 @@ var NcDeviceAlerts = class {
|
|
|
83198
83205
|
const subjects = this.deps.rows.all().map((row) => row.subject).filter((subject) => alertSubjectDeviceId(subject) === deviceId);
|
|
83199
83206
|
for (const subject of subjects) {
|
|
83200
83207
|
this.lastReading.delete(subject);
|
|
83201
|
-
this.
|
|
83208
|
+
this.lastProvider.delete(subject);
|
|
83202
83209
|
this.offeredAt.delete(subject);
|
|
83203
83210
|
await this.deps.rows.remove(subject);
|
|
83204
83211
|
}
|
|
83205
83212
|
this.lastReading.delete(`battery:${deviceId}`);
|
|
83206
83213
|
for (const [key, reading] of this.lastConsumable) if (reading.deviceId === deviceId) this.lastConsumable.delete(key);
|
|
83207
83214
|
for (const subject of this.tankHold.keys()) if (alertSubjectDeviceId(subject) === deviceId) this.tankHold.delete(subject);
|
|
83208
|
-
for (const subject of this.
|
|
83215
|
+
for (const subject of this.lastProvider.keys()) if (alertSubjectDeviceId(subject) === deviceId) this.lastProvider.delete(subject);
|
|
83209
83216
|
return subjects.length;
|
|
83210
83217
|
}
|
|
83211
83218
|
/**
|
|
@@ -83282,8 +83289,23 @@ var NcDeviceAlerts = class {
|
|
|
83282
83289
|
/** How to re-word a subject's alert from the last reading, or `null`. */
|
|
83283
83290
|
restatementOf(subject) {
|
|
83284
83291
|
const parts = parseConsumableSubject(subject);
|
|
83292
|
+
if (parts !== null && parts.legacy) {
|
|
83293
|
+
const provider = this.lastProvider.get(subject);
|
|
83294
|
+
if (provider !== void 0) return (facts) => incomingFromProviderConsumableBand({
|
|
83295
|
+
facts,
|
|
83296
|
+
deviceId: provider.deviceId,
|
|
83297
|
+
band: "low",
|
|
83298
|
+
item: {
|
|
83299
|
+
key: provider.item.key,
|
|
83300
|
+
label: provider.item.label,
|
|
83301
|
+
form: provider.item.form
|
|
83302
|
+
},
|
|
83303
|
+
attention: provider.attention,
|
|
83304
|
+
identity: this.deps.lookup(provider.deviceId)
|
|
83305
|
+
});
|
|
83306
|
+
}
|
|
83285
83307
|
const consumable = parts === null || parts.legacy ? void 0 : this.lastConsumable.get(itemKeyOf(parts.deviceId, parts.key));
|
|
83286
|
-
if (parts !== null && consumable !== void 0) return (facts) => incomingFromConsumableBand({
|
|
83308
|
+
if (parts !== null && !parts.legacy && consumable !== void 0) return (facts) => incomingFromConsumableBand({
|
|
83287
83309
|
facts,
|
|
83288
83310
|
deviceId: consumable.deviceId,
|
|
83289
83311
|
band: "low",
|
|
@@ -83291,16 +83313,6 @@ var NcDeviceAlerts = class {
|
|
|
83291
83313
|
thresholdPercent: parts.thresholdPercent,
|
|
83292
83314
|
identity: this.deps.lookup(consumable.deviceId)
|
|
83293
83315
|
});
|
|
83294
|
-
const tank = this.lastTank.get(subject);
|
|
83295
|
-
const tankBand = tank === void 0 ? null : deriveTankBand(tank.tank);
|
|
83296
|
-
if (tank !== void 0 && (tankBand === "low" || tankBand === "full")) return (facts) => incomingFromTankBand({
|
|
83297
|
-
facts,
|
|
83298
|
-
deviceId: tank.deviceId,
|
|
83299
|
-
band: tankBand,
|
|
83300
|
-
tank: tank.tank.key,
|
|
83301
|
-
...tank.tank.label !== void 0 ? { label: tank.tank.label } : {},
|
|
83302
|
-
identity: this.deps.lookup(tank.deviceId)
|
|
83303
|
-
});
|
|
83304
83316
|
const battery = this.lastReading.get(subject);
|
|
83305
83317
|
if (battery !== void 0) return (facts) => incomingFromBatteryBand({
|
|
83306
83318
|
facts,
|
|
@@ -83433,8 +83445,52 @@ function tagsOf(subject) {
|
|
|
83433
83445
|
const deviceId = alertSubjectDeviceId(subject);
|
|
83434
83446
|
return deviceId !== void 0 ? { tags: { deviceId } } : {};
|
|
83435
83447
|
}
|
|
83448
|
+
function providerTankReading(item) {
|
|
83449
|
+
const base = {
|
|
83450
|
+
key: item.key,
|
|
83451
|
+
label: item.label,
|
|
83452
|
+
level: item.level,
|
|
83453
|
+
status: null
|
|
83454
|
+
};
|
|
83455
|
+
const status = item.status;
|
|
83456
|
+
if (item.kind === "supply") switch (status) {
|
|
83457
|
+
case "low":
|
|
83458
|
+
case "empty":
|
|
83459
|
+
case "replace": return {
|
|
83460
|
+
...base,
|
|
83461
|
+
status: "low"
|
|
83462
|
+
};
|
|
83463
|
+
case "ok":
|
|
83464
|
+
case "full": return {
|
|
83465
|
+
...base,
|
|
83466
|
+
status: "ok"
|
|
83467
|
+
};
|
|
83468
|
+
default: return base;
|
|
83469
|
+
}
|
|
83470
|
+
if (item.kind === "waste") switch (status) {
|
|
83471
|
+
case "full":
|
|
83472
|
+
case "replace": return {
|
|
83473
|
+
...base,
|
|
83474
|
+
status: "full"
|
|
83475
|
+
};
|
|
83476
|
+
case "ok":
|
|
83477
|
+
case "low":
|
|
83478
|
+
case "empty": return {
|
|
83479
|
+
...base,
|
|
83480
|
+
status: "ok"
|
|
83481
|
+
};
|
|
83482
|
+
default: return base;
|
|
83483
|
+
}
|
|
83484
|
+
return base;
|
|
83485
|
+
}
|
|
83436
83486
|
//#endregion
|
|
83437
83487
|
//#region src/notification-center/alerts/consumable-routing.ts
|
|
83488
|
+
/**
|
|
83489
|
+
* The `consumables` slice — one vocabulary for every form (D934).
|
|
83490
|
+
*
|
|
83491
|
+
* Every item is a consumable reading: wear parts judged at the rule's threshold,
|
|
83492
|
+
* supply and waste judged on the provider's verdict (held like a float switch).
|
|
83493
|
+
*/
|
|
83438
83494
|
function wearStatus(status) {
|
|
83439
83495
|
switch (status) {
|
|
83440
83496
|
case "replace":
|
|
@@ -83444,59 +83500,44 @@ function wearStatus(status) {
|
|
|
83444
83500
|
default: return null;
|
|
83445
83501
|
}
|
|
83446
83502
|
}
|
|
83447
|
-
function
|
|
83448
|
-
|
|
83449
|
-
case "low":
|
|
83450
|
-
case "empty":
|
|
83451
|
-
case "replace": return "low";
|
|
83452
|
-
case "ok":
|
|
83453
|
-
case "full": return "ok";
|
|
83454
|
-
default: return null;
|
|
83455
|
-
}
|
|
83503
|
+
function consumableKindOf(raw) {
|
|
83504
|
+
return typeof raw.kind === "string" ? raw.kind : void 0;
|
|
83456
83505
|
}
|
|
83457
|
-
function
|
|
83458
|
-
|
|
83459
|
-
|
|
83460
|
-
|
|
83461
|
-
|
|
83462
|
-
|
|
83463
|
-
|
|
83464
|
-
default: return null;
|
|
83465
|
-
}
|
|
83506
|
+
function consumableFormOfRaw(raw) {
|
|
83507
|
+
const kind = consumableKindOf(raw);
|
|
83508
|
+
const formField = typeof raw.form === "string" ? raw.form : void 0;
|
|
83509
|
+
return consumableFormOf({
|
|
83510
|
+
...kind === "wear" || kind === "supply" || kind === "waste" ? { kind } : {},
|
|
83511
|
+
...formField === "part" || formField === "tank" || formField === "product" || formField === "bin" ? { form: formField } : {}
|
|
83512
|
+
});
|
|
83466
83513
|
}
|
|
83467
|
-
/**
|
|
83468
|
-
function
|
|
83469
|
-
const
|
|
83470
|
-
|
|
83471
|
-
|
|
83472
|
-
|
|
83473
|
-
|
|
83474
|
-
|
|
83475
|
-
|
|
83476
|
-
|
|
83477
|
-
|
|
83478
|
-
};
|
|
83514
|
+
/** Parse one slice entry into the unified consumable item shape. */
|
|
83515
|
+
function parseConsumableSliceItem(raw) {
|
|
83516
|
+
const kind = consumableKindOf(raw);
|
|
83517
|
+
const form = consumableFormOfRaw(raw);
|
|
83518
|
+
const status = typeof raw.status === "string" ? raw.status : null;
|
|
83519
|
+
const wear = kind === void 0 || kind === "wear" ? {
|
|
83520
|
+
key: raw.key,
|
|
83521
|
+
label: raw.label,
|
|
83522
|
+
level: raw.level,
|
|
83523
|
+
status: wearStatus(status)
|
|
83524
|
+
} : null;
|
|
83479
83525
|
return {
|
|
83480
|
-
|
|
83481
|
-
|
|
83482
|
-
|
|
83483
|
-
|
|
83484
|
-
|
|
83485
|
-
|
|
83486
|
-
|
|
83526
|
+
key: raw.key,
|
|
83527
|
+
label: raw.label,
|
|
83528
|
+
level: raw.level,
|
|
83529
|
+
status,
|
|
83530
|
+
kind,
|
|
83531
|
+
form,
|
|
83532
|
+
wear
|
|
83487
83533
|
};
|
|
83488
83534
|
}
|
|
83489
83535
|
function isRecord$1(value) {
|
|
83490
83536
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83491
83537
|
}
|
|
83492
83538
|
/**
|
|
83493
|
-
* A `device.state-changed` carrying a `consumables` slice
|
|
83494
|
-
*
|
|
83495
|
-
*
|
|
83496
|
-
* An item is kept only when it carries a usable `key` — the subject is built
|
|
83497
|
-
* from it. `level` and `status` are each optional; an item with NEITHER is
|
|
83498
|
-
* still returned, because deciding that it has told us nothing belongs to the
|
|
83499
|
-
* band derivation and not to a parser (D393: absent is not zero).
|
|
83539
|
+
* A `device.state-changed` carrying a `consumables` slice; `null` for any other
|
|
83540
|
+
* event, a malformed slice or an empty list.
|
|
83500
83541
|
*/
|
|
83501
83542
|
function consumablesSliceOf(event) {
|
|
83502
83543
|
if (event.category !== "device.state-changed") return null;
|
|
@@ -83507,8 +83548,7 @@ function consumablesSliceOf(event) {
|
|
|
83507
83548
|
if (typeof deviceId !== "number" || !isRecord$1(slice)) return null;
|
|
83508
83549
|
const rawItems = slice["items"];
|
|
83509
83550
|
if (!Array.isArray(rawItems)) return null;
|
|
83510
|
-
const
|
|
83511
|
-
const tanks = [];
|
|
83551
|
+
const items = [];
|
|
83512
83552
|
for (const entry of rawItems) {
|
|
83513
83553
|
if (!isRecord$1(entry)) continue;
|
|
83514
83554
|
const key = entry["key"];
|
|
@@ -83517,21 +83557,20 @@ function consumablesSliceOf(event) {
|
|
|
83517
83557
|
const level = entry["level"];
|
|
83518
83558
|
const status = entry["status"];
|
|
83519
83559
|
const kind = entry["kind"];
|
|
83520
|
-
const
|
|
83560
|
+
const form = entry["form"];
|
|
83561
|
+
items.push(parseConsumableSliceItem({
|
|
83521
83562
|
key,
|
|
83522
83563
|
label: typeof label === "string" && label.length > 0 ? label : key,
|
|
83523
83564
|
level: typeof level === "number" && Number.isFinite(level) ? level : null,
|
|
83524
83565
|
status: typeof status === "string" ? status : null,
|
|
83525
|
-
kind: typeof kind === "string" ? kind : void 0
|
|
83526
|
-
|
|
83527
|
-
|
|
83528
|
-
else consumables.push(routed.item);
|
|
83566
|
+
kind: typeof kind === "string" ? kind : void 0,
|
|
83567
|
+
form: typeof form === "string" ? form : void 0
|
|
83568
|
+
}));
|
|
83529
83569
|
}
|
|
83530
|
-
if (
|
|
83570
|
+
if (items.length === 0) return null;
|
|
83531
83571
|
return {
|
|
83532
83572
|
deviceId,
|
|
83533
|
-
|
|
83534
|
-
tanks
|
|
83573
|
+
items
|
|
83535
83574
|
};
|
|
83536
83575
|
}
|
|
83537
83576
|
//#endregion
|
|
@@ -85179,8 +85218,6 @@ var NcDeviceStateCache = class {
|
|
|
85179
85218
|
* a job that COMPLETED, and the state it leaves behind is not a condition that
|
|
85180
85219
|
* is still true. The ADR records why it is excluded rather than approximated.
|
|
85181
85220
|
*/
|
|
85182
|
-
/** One source: the cap's Zod enum (D698 LOW-6). */
|
|
85183
|
-
var NC_STATE_SUMMARY_SECTION_IDS = NcStateSummarySectionIdSchema.options;
|
|
85184
85221
|
/** The kind groups that have no section, and why — said on the preview. */
|
|
85185
85222
|
var NC_STATE_SUMMARY_EXCLUDED_GROUPS = { "backups-exports": "a backup or an export is a completed job, not a state that is still true" };
|
|
85186
85223
|
function isStateSectionId(value) {
|
|
@@ -85192,7 +85229,6 @@ var NC_STATE_SECTION_VAR = {
|
|
|
85192
85229
|
streams: "summaryStreams",
|
|
85193
85230
|
battery: "summaryBattery",
|
|
85194
85231
|
consumables: "summaryConsumables",
|
|
85195
|
-
tanks: "summaryTanks",
|
|
85196
85232
|
"detection-blind": "summaryDetection",
|
|
85197
85233
|
nodes: "summaryNodes",
|
|
85198
85234
|
addons: "summaryAddons",
|
|
@@ -85210,11 +85246,13 @@ function sectionKinds(section) {
|
|
|
85210
85246
|
* direction, only the event did.
|
|
85211
85247
|
*/
|
|
85212
85248
|
function sectionsOfRule(condition) {
|
|
85213
|
-
|
|
85249
|
+
const kinds = condition.kinds;
|
|
85250
|
+
return NC_STATE_SUMMARY_SECTION_IDS.filter((section) => sectionKinds(section).some((kind) => kinds.includes(kind)));
|
|
85214
85251
|
}
|
|
85215
85252
|
/** The kind groups the rule selected that have no section. */
|
|
85216
85253
|
function excludedGroupsOfRule(condition) {
|
|
85217
|
-
|
|
85254
|
+
const kinds = condition.kinds;
|
|
85255
|
+
return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((group) => NC_STATE_SUMMARY_EXCLUDED_GROUPS[group.id] !== void 0 && group.kinds.some((kind) => kinds.includes(kind))).map((group) => group.id);
|
|
85218
85256
|
}
|
|
85219
85257
|
/**
|
|
85220
85258
|
* The condition the summary judges its items with: the rule's own, with its
|
|
@@ -89961,6 +89999,12 @@ function classCounts(members) {
|
|
|
89961
89999
|
* params go last because they are the row's own truth about a system event and
|
|
89962
90000
|
* outrank the live vocabulary for exactly those names.
|
|
89963
90001
|
*/
|
|
90002
|
+
/** Intake-only keys for body variant selection — not operator template vars (D934). */
|
|
90003
|
+
function systemEventParamsForTemplate(params) {
|
|
90004
|
+
if (params === void 0) return {};
|
|
90005
|
+
const { form: _form, attention: _attention, ...rest } = params;
|
|
90006
|
+
return rest;
|
|
90007
|
+
}
|
|
89964
90008
|
function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
|
|
89965
90009
|
const subject = entry.payload.subject;
|
|
89966
90010
|
const occupancy = subject.occupancy;
|
|
@@ -90008,7 +90052,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
|
|
|
90008
90052
|
latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : "",
|
|
90009
90053
|
...systemEvent?.deviceId !== void 0 ? { deviceName: String(systemEvent.deviceId) } : {},
|
|
90010
90054
|
...systemEvent?.nodeId !== void 0 && (systemEvent.kind === "node-online" || systemEvent.kind === "node-offline") ? { nodeName: systemEvent.nodeId } : {},
|
|
90011
|
-
...systemEvent?.textParams
|
|
90055
|
+
...systemEventParamsForTemplate(systemEvent?.textParams)
|
|
90012
90056
|
};
|
|
90013
90057
|
}
|
|
90014
90058
|
/**
|
|
@@ -99471,15 +99515,30 @@ function deviceParams(deviceId, identity) {
|
|
|
99471
99515
|
deviceName: name.length > 0 ? name : String(deviceId)
|
|
99472
99516
|
};
|
|
99473
99517
|
}
|
|
99474
|
-
function
|
|
99518
|
+
function consumableSummaryLineKey(params, measured) {
|
|
99519
|
+
const form = params["form"] ?? "part";
|
|
99520
|
+
if (form === "part") return measured && params["percentage"] !== void 0 ? "summary.line.consumable.part" : "summary.line.consumable.part.unmeasured";
|
|
99521
|
+
if (form === "tank") return params["attention"] === "full" ? "summary.line.consumable.tank-full" : "summary.line.consumable.tank";
|
|
99522
|
+
if (form === "product") return "summary.line.consumable.product";
|
|
99523
|
+
return "summary.line.consumable.bin";
|
|
99524
|
+
}
|
|
99525
|
+
function alertItem(section, open, lookup) {
|
|
99475
99526
|
const params = open.subject.textParams ?? {};
|
|
99476
|
-
const
|
|
99527
|
+
const lineKey = section === "consumables" ? consumableSummaryLineKey(params, open.measured) : open.subject.bodyKey ?? "";
|
|
99528
|
+
const lineParams = section === "consumables" ? (({ deviceName: _deviceName, ...rest }) => rest)(params) : params;
|
|
99529
|
+
const deviceId = open.subject.deviceId;
|
|
99530
|
+
const identity = deviceId !== void 0 ? lookup(deviceId) : void 0;
|
|
99531
|
+
const deviceName = params["deviceName"] ?? (identity !== void 0 && identity.name.length > 0 ? identity.name : deviceId !== void 0 ? String(deviceId) : void 0);
|
|
99477
99532
|
return {
|
|
99478
99533
|
section,
|
|
99479
99534
|
subject: open.subject,
|
|
99480
|
-
lineKey
|
|
99481
|
-
params,
|
|
99482
|
-
since: open.since
|
|
99535
|
+
lineKey,
|
|
99536
|
+
params: lineParams,
|
|
99537
|
+
since: open.since,
|
|
99538
|
+
...section === "consumables" && deviceId !== void 0 && deviceName !== void 0 ? { device: {
|
|
99539
|
+
id: deviceId,
|
|
99540
|
+
name: deviceName
|
|
99541
|
+
} } : {}
|
|
99483
99542
|
};
|
|
99484
99543
|
}
|
|
99485
99544
|
function livenessItem(row, lookup) {
|
|
@@ -99608,7 +99667,7 @@ function stateItem(row, lookup, nodes) {
|
|
|
99608
99667
|
* (`parentDeviceId` set) still owns those alerts — filtering every accessory
|
|
99609
99668
|
* here dropped every tank line of the 2026-10-05 evening summary for
|
|
99610
99669
|
* deviceId 4385 while the episode was open and the instant path had already
|
|
99611
|
-
* held
|
|
99670
|
+
* held a legacy tank kind for that rule.
|
|
99612
99671
|
*/
|
|
99613
99672
|
function stillTrue(item, ports) {
|
|
99614
99673
|
const deviceId = item.subject.deviceId;
|
|
@@ -99648,16 +99707,16 @@ function readSection(section, ports, nowMs) {
|
|
|
99648
99707
|
}
|
|
99649
99708
|
/** Which ports a section needs, and why each one is unavailable. */
|
|
99650
99709
|
function readRaw(section, ports, nowMs) {
|
|
99651
|
-
if (section === "battery" || section === "consumables"
|
|
99710
|
+
if (section === "battery" || section === "consumables") {
|
|
99652
99711
|
const alerts = ports.alerts();
|
|
99653
99712
|
if (alerts === null) return {
|
|
99654
99713
|
answered: false,
|
|
99655
99714
|
reason: UNREAD_ALERTS
|
|
99656
99715
|
};
|
|
99657
|
-
const family = section === "battery" ? "battery" :
|
|
99716
|
+
const family = section === "battery" ? "battery" : "consumable";
|
|
99658
99717
|
return {
|
|
99659
99718
|
answered: true,
|
|
99660
|
-
items: alerts.filter((open) => open.family === family).map((open) => alertItem(section, open))
|
|
99719
|
+
items: alerts.filter((open) => open.family === family).map((open) => alertItem(section, open, ports.lookup))
|
|
99661
99720
|
};
|
|
99662
99721
|
}
|
|
99663
99722
|
const state = ports.state();
|
|
@@ -99817,11 +99876,12 @@ function wordsOf(input, section) {
|
|
|
99817
99876
|
/** One bullet per item; an item with a `node` is listed under its node's heading (D708). */
|
|
99818
99877
|
function linesOf(input, items) {
|
|
99819
99878
|
const bullet = (item) => `• ${lineOf(input, item)}`;
|
|
99820
|
-
if (
|
|
99821
|
-
return groupByNode(items, input.texts.language).flatMap((group) => [input.texts.text({
|
|
99879
|
+
if (items.some((item) => item.node !== void 0)) return groupByNode(items, input.texts.language).flatMap((group) => [input.texts.text({
|
|
99822
99880
|
key: "summary.node.heading",
|
|
99823
99881
|
vars: { node: group.node.name }
|
|
99824
99882
|
}), ...group.items.map(bullet)]).join("\n");
|
|
99883
|
+
if (items.some((item) => item.device !== void 0)) return groupByDevice(items, input.texts.language).flatMap((group) => [`${group.device.name}:`, ...group.items.map(bullet)]).join("\n");
|
|
99884
|
+
return items.map(bullet).join("\n");
|
|
99825
99885
|
}
|
|
99826
99886
|
/**
|
|
99827
99887
|
* The items cut per node, in a STABLE order: the hub first, then by name, then
|
|
@@ -99846,6 +99906,21 @@ var HUB_HEADING = {
|
|
|
99846
99906
|
name: "hub",
|
|
99847
99907
|
isHub: true
|
|
99848
99908
|
};
|
|
99909
|
+
/** Consumables items under each device, devices ordered by name (D934). */
|
|
99910
|
+
function groupByDevice(items, language) {
|
|
99911
|
+
const groups = /* @__PURE__ */ new Map();
|
|
99912
|
+
for (const item of items) {
|
|
99913
|
+
const device = item.device;
|
|
99914
|
+
if (device === void 0) continue;
|
|
99915
|
+
const group = groups.get(device.id);
|
|
99916
|
+
if (group === void 0) groups.set(device.id, {
|
|
99917
|
+
device,
|
|
99918
|
+
items: [item]
|
|
99919
|
+
});
|
|
99920
|
+
else group.items.push(item);
|
|
99921
|
+
}
|
|
99922
|
+
return [...groups.values()].toSorted((a, b) => a.device.name.localeCompare(b.device.name, language));
|
|
99923
|
+
}
|
|
99849
99924
|
function stateSummaryText(input) {
|
|
99850
99925
|
const sectionVars = {};
|
|
99851
99926
|
const sections = [];
|
|
@@ -103912,8 +103987,7 @@ var NotificationCenter = class {
|
|
|
103912
103987
|
}
|
|
103913
103988
|
const consumables = consumablesSliceOf(event);
|
|
103914
103989
|
if (consumables !== null) {
|
|
103915
|
-
for (const item of consumables.
|
|
103916
|
-
for (const tank of consumables.tanks) this.deviceAlerts.observeTank(consumables.deviceId, tank);
|
|
103990
|
+
for (const item of consumables.items) this.deviceAlerts.observeConsumableItem(consumables.deviceId, item);
|
|
103917
103991
|
return;
|
|
103918
103992
|
}
|
|
103919
103993
|
const lookup = this.deviceDirectory.lookup();
|