@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
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-DB0TSd14.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -9026,27 +9026,13 @@ var en_default = {
|
|
|
9026
9026
|
"system.device-consumable-low.title": "Consumable low — {{deviceName}}",
|
|
9027
9027
|
"system.device-consumable-low.body": "{{item}} on {{deviceName}} is at {{percentage}}%",
|
|
9028
9028
|
"system.device-consumable-low.body.unmeasured": "{{item}} on {{deviceName}} needs replacing",
|
|
9029
|
+
"system.device-consumable-low.body.tank": "{{item}} on {{deviceName}} is nearly empty",
|
|
9030
|
+
"system.device-consumable-low.body.tank-full": "{{item}} on {{deviceName}} is full",
|
|
9031
|
+
"system.device-consumable-low.body.product": "{{item}} on {{deviceName}} is running out",
|
|
9032
|
+
"system.device-consumable-low.body.bin": "{{item}} on {{deviceName}} is full",
|
|
9029
9033
|
"system.device-consumable-normal.title": "Consumable replaced — {{deviceName}}",
|
|
9030
9034
|
"system.device-consumable-normal.body": "{{item}} on {{deviceName}} is back to {{percentage}}%",
|
|
9031
9035
|
"system.device-consumable-normal.body.unmeasured": "{{item}} on {{deviceName}} is OK again",
|
|
9032
|
-
"system.device-tank-low.title": "Tank low — {{deviceName}}",
|
|
9033
|
-
"system.device-tank-full.title": "Tank full — {{deviceName}}",
|
|
9034
|
-
"system.device-tank-normal.title": "Tank OK — {{deviceName}}",
|
|
9035
|
-
"system.device-tank-low.body.cleanWater": "The clean water tank on {{deviceName}} is running low",
|
|
9036
|
-
"system.device-tank-full.body.cleanWater": "The clean water tank on {{deviceName}} is full",
|
|
9037
|
-
"system.device-tank-normal.body.cleanWater": "The clean water tank on {{deviceName}} is OK again",
|
|
9038
|
-
"system.device-tank-low.body.dirtyWater": "The dirty water tank on {{deviceName}} is running low",
|
|
9039
|
-
"system.device-tank-full.body.dirtyWater": "The dirty water tank on {{deviceName}} is full",
|
|
9040
|
-
"system.device-tank-normal.body.dirtyWater": "The dirty water tank on {{deviceName}} is OK again",
|
|
9041
|
-
"system.device-tank-low.body.detergent": "The detergent tank on {{deviceName}} is running low",
|
|
9042
|
-
"system.device-tank-full.body.detergent": "The detergent tank on {{deviceName}} is full",
|
|
9043
|
-
"system.device-tank-normal.body.detergent": "The detergent tank on {{deviceName}} is OK again",
|
|
9044
|
-
"system.device-tank-low.body.dustBin": "The dust bin on {{deviceName}} is running low",
|
|
9045
|
-
"system.device-tank-full.body.dustBin": "The dust bin on {{deviceName}} is full",
|
|
9046
|
-
"system.device-tank-normal.body.dustBin": "The dust bin on {{deviceName}} is OK again",
|
|
9047
|
-
"system.device-tank-low.body.item": "{{item}} on {{deviceName}} is running low",
|
|
9048
|
-
"system.device-tank-full.body.item": "{{item}} on {{deviceName}} is full",
|
|
9049
|
-
"system.device-tank-normal.body.item": "{{item}} on {{deviceName}} is OK again",
|
|
9050
9036
|
"system.camera-online.title": "Device online",
|
|
9051
9037
|
"system.camera-online.body": "Device {{deviceName}} is online",
|
|
9052
9038
|
"system.camera-offline.title": "Device offline",
|
|
@@ -9201,8 +9187,7 @@ var en_default = {
|
|
|
9201
9187
|
"summary.section.device-state": "Offline or off",
|
|
9202
9188
|
"summary.section.streams": "Streams offline",
|
|
9203
9189
|
"summary.section.battery": "Low batteries",
|
|
9204
|
-
"summary.section.consumables": "Consumables
|
|
9205
|
-
"summary.section.tanks": "Tanks low",
|
|
9190
|
+
"summary.section.consumables": "Consumables",
|
|
9206
9191
|
"summary.section.detection-blind": "Detecting nothing",
|
|
9207
9192
|
"summary.section.nodes": "Nodes with problems",
|
|
9208
9193
|
"summary.section.addons": "Addons stopped",
|
|
@@ -9220,7 +9205,12 @@ var en_default = {
|
|
|
9220
9205
|
"summary.line.addon-crash-loop": "{{nodeId}} — {{addons}} stopped after repeated crashes (since {{since}})",
|
|
9221
9206
|
"summary.line.update": "{{packageName}} {{currentVersion}} → {{latestVersion}}",
|
|
9222
9207
|
"summary.line.wrapper-update": "Container {{currentVersion}} → {{latestVersion}}",
|
|
9223
|
-
"summary.line.
|
|
9208
|
+
"summary.line.consumable.part": "{{item}} {{percentage}}%",
|
|
9209
|
+
"summary.line.consumable.part.unmeasured": "{{item}} needs replacing",
|
|
9210
|
+
"summary.line.consumable.tank": "{{item}} nearly empty",
|
|
9211
|
+
"summary.line.consumable.tank-full": "{{item}} full",
|
|
9212
|
+
"summary.line.consumable.product": "{{item}} running out",
|
|
9213
|
+
"summary.line.consumable.bin": "{{item}} full",
|
|
9224
9214
|
"calendar-events.title": "{{rule}}: {{count}} upcoming",
|
|
9225
9215
|
"calendar-events.empty": "No events in the next {{horizonDays}} day(s).",
|
|
9226
9216
|
"calendar-events.day.today": "Today — {{date}}",
|
|
@@ -9432,27 +9422,13 @@ var it_default = {
|
|
|
9432
9422
|
"system.device-consumable-low.title": "Consumabile basso — {{deviceName}}",
|
|
9433
9423
|
"system.device-consumable-low.body": "{{item}} su {{deviceName}} è al {{percentage}}%",
|
|
9434
9424
|
"system.device-consumable-low.body.unmeasured": "{{item}} su {{deviceName}} va sostituito",
|
|
9425
|
+
"system.device-consumable-low.body.tank": "{{item}} di {{deviceName}} è quasi vuota",
|
|
9426
|
+
"system.device-consumable-low.body.tank-full": "{{item}} di {{deviceName}} è piena",
|
|
9427
|
+
"system.device-consumable-low.body.product": "{{item}} di {{deviceName}} è in esaurimento",
|
|
9428
|
+
"system.device-consumable-low.body.bin": "{{item}} di {{deviceName}} è pieno",
|
|
9435
9429
|
"system.device-consumable-normal.title": "Consumabile sostituito — {{deviceName}}",
|
|
9436
9430
|
"system.device-consumable-normal.body": "{{item}} su {{deviceName}} è tornato al {{percentage}}%",
|
|
9437
9431
|
"system.device-consumable-normal.body.unmeasured": "{{item}} su {{deviceName}} è di nuovo a posto",
|
|
9438
|
-
"system.device-tank-low.title": "Serbatoio quasi vuoto — {{deviceName}}",
|
|
9439
|
-
"system.device-tank-full.title": "Serbatoio pieno — {{deviceName}}",
|
|
9440
|
-
"system.device-tank-normal.title": "Serbatoio a posto — {{deviceName}}",
|
|
9441
|
-
"system.device-tank-low.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è quasi vuoto",
|
|
9442
|
-
"system.device-tank-full.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è pieno",
|
|
9443
|
-
"system.device-tank-normal.body.cleanWater": "Il serbatoio dell'acqua pulita di {{deviceName}} è di nuovo a posto",
|
|
9444
|
-
"system.device-tank-low.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è quasi vuoto",
|
|
9445
|
-
"system.device-tank-full.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è pieno",
|
|
9446
|
-
"system.device-tank-normal.body.dirtyWater": "Il serbatoio dell'acqua sporca di {{deviceName}} è di nuovo a posto",
|
|
9447
|
-
"system.device-tank-low.body.detergent": "Il serbatoio del detergente di {{deviceName}} è quasi vuoto",
|
|
9448
|
-
"system.device-tank-full.body.detergent": "Il serbatoio del detergente di {{deviceName}} è pieno",
|
|
9449
|
-
"system.device-tank-normal.body.detergent": "Il serbatoio del detergente di {{deviceName}} è di nuovo a posto",
|
|
9450
|
-
"system.device-tank-low.body.dustBin": "Il contenitore della polvere di {{deviceName}} è quasi vuoto",
|
|
9451
|
-
"system.device-tank-full.body.dustBin": "Il contenitore della polvere di {{deviceName}} è pieno",
|
|
9452
|
-
"system.device-tank-normal.body.dustBin": "Il contenitore della polvere di {{deviceName}} è di nuovo a posto",
|
|
9453
|
-
"system.device-tank-low.body.item": "{{item}} di {{deviceName}} sta finendo",
|
|
9454
|
-
"system.device-tank-full.body.item": "{{item}} di {{deviceName}} è pieno",
|
|
9455
|
-
"system.device-tank-normal.body.item": "{{item}} di {{deviceName}} è di nuovo a posto",
|
|
9456
9432
|
"system.camera-online.title": "Dispositivo online",
|
|
9457
9433
|
"system.camera-online.body": "Il dispositivo {{deviceName}} è online",
|
|
9458
9434
|
"system.camera-offline.title": "Dispositivo offline",
|
|
@@ -9607,8 +9583,7 @@ var it_default = {
|
|
|
9607
9583
|
"summary.section.device-state": "Offline o spenti",
|
|
9608
9584
|
"summary.section.streams": "Stream offline",
|
|
9609
9585
|
"summary.section.battery": "Batterie scariche",
|
|
9610
|
-
"summary.section.consumables": "Consumabili
|
|
9611
|
-
"summary.section.tanks": "Serbatoi bassi",
|
|
9586
|
+
"summary.section.consumables": "Consumabili",
|
|
9612
9587
|
"summary.section.detection-blind": "Senza detection",
|
|
9613
9588
|
"summary.section.nodes": "Nodi con problemi",
|
|
9614
9589
|
"summary.section.addons": "Addon fermati",
|
|
@@ -9626,7 +9601,12 @@ var it_default = {
|
|
|
9626
9601
|
"summary.line.addon-crash-loop": "{{nodeId}} — {{addons}} fermati dopo crash ripetuti (da {{since}})",
|
|
9627
9602
|
"summary.line.update": "{{packageName}} {{currentVersion}} → {{latestVersion}}",
|
|
9628
9603
|
"summary.line.wrapper-update": "Container {{currentVersion}} → {{latestVersion}}",
|
|
9629
|
-
"summary.line.
|
|
9604
|
+
"summary.line.consumable.part": "{{item}} {{percentage}}%",
|
|
9605
|
+
"summary.line.consumable.part.unmeasured": "{{item}} da sostituire",
|
|
9606
|
+
"summary.line.consumable.tank": "{{item}} quasi vuota",
|
|
9607
|
+
"summary.line.consumable.tank-full": "{{item}} piena",
|
|
9608
|
+
"summary.line.consumable.product": "{{item}} in esaurimento",
|
|
9609
|
+
"summary.line.consumable.bin": "{{item}} pieno",
|
|
9630
9610
|
"calendar-events.title": "{{rule}}: {{count}} in arrivo",
|
|
9631
9611
|
"calendar-events.empty": "Nessun evento nei prossimi {{horizonDays}} giorno/i.",
|
|
9632
9612
|
"calendar-events.day.today": "Oggi — {{date}}",
|
|
@@ -11908,9 +11888,9 @@ function systemEventKeys(kind) {
|
|
|
11908
11888
|
titleKey: `system.${kind}.title`,
|
|
11909
11889
|
bodyKey: `system.${kind}.body.ignored`
|
|
11910
11890
|
};
|
|
11911
|
-
if (
|
|
11891
|
+
if (kind === "device-consumable-low") return {
|
|
11912
11892
|
titleKey: `system.${kind}.title`,
|
|
11913
|
-
bodyKey:
|
|
11893
|
+
bodyKey: "system.device-consumable-low.body.tank"
|
|
11914
11894
|
};
|
|
11915
11895
|
if (kind === "rule-broken") return {
|
|
11916
11896
|
titleKey: `system.${kind}.title`,
|
|
@@ -71570,6 +71550,25 @@ Object.freeze({
|
|
|
71570
71550
|
"path": "reason",
|
|
71571
71551
|
"valueType": "string"
|
|
71572
71552
|
}],
|
|
71553
|
+
"running-state": [
|
|
71554
|
+
{
|
|
71555
|
+
"path": "running",
|
|
71556
|
+
"valueType": "boolean"
|
|
71557
|
+
},
|
|
71558
|
+
{
|
|
71559
|
+
"path": "since",
|
|
71560
|
+
"valueType": "number"
|
|
71561
|
+
},
|
|
71562
|
+
{
|
|
71563
|
+
"path": "source",
|
|
71564
|
+
"valueType": "enum",
|
|
71565
|
+
"enumValues": ["native", "assigned"]
|
|
71566
|
+
},
|
|
71567
|
+
{
|
|
71568
|
+
"path": "phase",
|
|
71569
|
+
"valueType": "string"
|
|
71570
|
+
}
|
|
71571
|
+
],
|
|
71573
71572
|
"script-runner": [
|
|
71574
71573
|
{
|
|
71575
71574
|
"path": "isRunning",
|
|
@@ -81077,83 +81076,6 @@ function deriveConsumableBand(input) {
|
|
|
81077
81076
|
return value <= input.thresholdPercent ? "low" : "normal";
|
|
81078
81077
|
}
|
|
81079
81078
|
//#endregion
|
|
81080
|
-
//#region src/notification-center/alerts/tank-band.ts
|
|
81081
|
-
/**
|
|
81082
|
-
* A TANK reduced to the only thing worth storing: its band (D642).
|
|
81083
|
-
*
|
|
81084
|
-
* A tank is a `supply` or `waste` item of a device's `consumables` slice
|
|
81085
|
-
* (D915 — before it, the four vacuum tanks lived on `vacuum-control`): a
|
|
81086
|
-
* vacuum's clean water, dirty water, detergent and dust bin, a dishwasher's
|
|
81087
|
-
* salt and rinse aid. `consumable-routing.ts` narrows the item's status to the
|
|
81088
|
-
* band vocabulary below. The operator asked for them to notify like
|
|
81089
|
-
* consumables: a supply running low, a waste tank full.
|
|
81090
|
-
*
|
|
81091
|
-
* ## The verdict is the provider's
|
|
81092
|
-
*
|
|
81093
|
-
* A tank's `status` is the device's own sensor (`ok` | `low` | `full`),
|
|
81094
|
-
* and it is the ONLY thing judged. The hub has no threshold for a tank level:
|
|
81095
|
-
* the rule for thresholds is that they belong to whoever generates the event,
|
|
81096
|
-
* and a tank that says `low` has already decided. A tank that reports only a
|
|
81097
|
-
* `level` is therefore not judged at all (`null`) rather than judged against a
|
|
81098
|
-
* number the hub would have to invent.
|
|
81099
|
-
*
|
|
81100
|
-
* ## `null` is the third answer, and it is not `normal`
|
|
81101
|
-
*
|
|
81102
|
-
* An unknown status says nothing — it neither opens nor closes an episode, for
|
|
81103
|
-
* the reason `battery-band.ts` gives: a reading nobody took is not evidence
|
|
81104
|
-
* that anything changed.
|
|
81105
|
-
*
|
|
81106
|
-
* Pure. No clock, no I/O.
|
|
81107
|
-
*/
|
|
81108
|
-
/**
|
|
81109
|
-
* The tanks with a sentence of their own (`system.device-tank-*.body.<key>`),
|
|
81110
|
-
* by their item key — the four a vacuum reports. Any other tank (salt, rinse
|
|
81111
|
-
* aid, …) is worded by its label (`body.item`).
|
|
81112
|
-
*/
|
|
81113
|
-
var NC_TANK_KEYS = [
|
|
81114
|
-
"cleanWater",
|
|
81115
|
-
"dirtyWater",
|
|
81116
|
-
"detergent",
|
|
81117
|
-
"dustBin"
|
|
81118
|
-
];
|
|
81119
|
-
function isNamedTankKey(key) {
|
|
81120
|
-
return NC_TANK_KEYS.some((k) => k === key);
|
|
81121
|
-
}
|
|
81122
|
-
/**
|
|
81123
|
-
* How long a tank change must HOLD before it counts (D642 review M2).
|
|
81124
|
-
*
|
|
81125
|
-
* A float switch sloshing at the edge of `low` / `full` flips its status back
|
|
81126
|
-
* and forth while the robot moves; each flip is a durable transition, and
|
|
81127
|
-
* without a hold each one is a raise and a recovery on the operator's phone.
|
|
81128
|
-
* One minute is two reload ticks: long enough to outlast a slosh, short enough
|
|
81129
|
-
* that a genuinely full dust bin is said within a minute and a half.
|
|
81130
|
-
*/
|
|
81131
|
-
var NC_TANK_HOLD_MS = 6e4;
|
|
81132
|
-
function deriveTankBand(reading) {
|
|
81133
|
-
switch (reading.status) {
|
|
81134
|
-
case "low": return "low";
|
|
81135
|
-
case "full": return "full";
|
|
81136
|
-
case "ok": return "normal";
|
|
81137
|
-
case null: return null;
|
|
81138
|
-
}
|
|
81139
|
-
}
|
|
81140
|
-
/** `tank:<deviceId>:<key>` — the subject of one tank's episode. */
|
|
81141
|
-
function tankSubject(deviceId, key) {
|
|
81142
|
-
return `tank:${String(deviceId)}:${key}`;
|
|
81143
|
-
}
|
|
81144
|
-
/** The parts of a tank subject, or `null` when it is not one. */
|
|
81145
|
-
function parseTankSubject(subject) {
|
|
81146
|
-
const match = /^tank:(\d+):(.+)$/.exec(subject);
|
|
81147
|
-
if (match === null) return null;
|
|
81148
|
-
const deviceId = Number(match[1]);
|
|
81149
|
-
const key = match[2];
|
|
81150
|
-
if (!Number.isSafeInteger(deviceId) || key === void 0) return null;
|
|
81151
|
-
return {
|
|
81152
|
-
deviceId,
|
|
81153
|
-
key
|
|
81154
|
-
};
|
|
81155
|
-
}
|
|
81156
|
-
//#endregion
|
|
81157
81079
|
//#region src/notification-center/device-directory.ts
|
|
81158
81080
|
/**
|
|
81159
81081
|
* WHO a device id is — the mirrored directory the system-event intake reads.
|
|
@@ -81915,6 +81837,7 @@ function incomingFromConsumableBand(input) {
|
|
|
81915
81837
|
textParams: {
|
|
81916
81838
|
...deviceTextParams(input.deviceId, input.identity),
|
|
81917
81839
|
item: input.item.label,
|
|
81840
|
+
form: "part",
|
|
81918
81841
|
...percentage !== null ? { percentage: String(percentage) } : {}
|
|
81919
81842
|
},
|
|
81920
81843
|
deviceId: input.deviceId,
|
|
@@ -81922,29 +81845,33 @@ function incomingFromConsumableBand(input) {
|
|
|
81922
81845
|
...input.identity !== void 0 ? { deviceType: input.identity.type } : {}
|
|
81923
81846
|
});
|
|
81924
81847
|
}
|
|
81848
|
+
/** Instant body keys for a consumable form (D934). */
|
|
81849
|
+
function consumableLowBodyKey(form, attention, measured) {
|
|
81850
|
+
if (form === "part") return measured ? "system.device-consumable-low.body" : "system.device-consumable-low.body.unmeasured";
|
|
81851
|
+
if (form === "tank") return attention === "full" ? "system.device-consumable-low.body.tank-full" : "system.device-consumable-low.body.tank";
|
|
81852
|
+
if (form === "product") return "system.device-consumable-low.body.product";
|
|
81853
|
+
return "system.device-consumable-low.body.bin";
|
|
81854
|
+
}
|
|
81925
81855
|
/**
|
|
81926
|
-
* A
|
|
81856
|
+
* A supply / waste item judged on the provider's verdict (D934).
|
|
81927
81857
|
*
|
|
81928
|
-
*
|
|
81929
|
-
*
|
|
81930
|
-
* and the recovery keeps it. For the four named vacuum tanks the BODY VARIANT
|
|
81931
|
-
* names the tank in the reader's language — the params are frozen before the
|
|
81932
|
-
* language is known, so the tank's name cannot be a param; `tank` carries the
|
|
81933
|
-
* stable token for a custom template. Any other tank (a dishwasher's salt, …)
|
|
81934
|
-
* is worded by the provider's label through `body.item`.
|
|
81858
|
+
* Subject `consumable:<deviceId>:<key>` — no `consumableLowPercent` so rules
|
|
81859
|
+
* at 15 % still hear salt and water.
|
|
81935
81860
|
*/
|
|
81936
|
-
function
|
|
81937
|
-
const kind = input.band === "low" ? "device-
|
|
81861
|
+
function incomingFromProviderConsumableBand(input) {
|
|
81862
|
+
const kind = input.band === "low" ? "device-consumable-low" : "device-consumable-normal";
|
|
81863
|
+
const bodyKey = input.band === "low" ? consumableLowBodyKey(input.item.form, input.attention, false) : "system.device-consumable-normal.body.unmeasured";
|
|
81938
81864
|
return systemSubject({
|
|
81939
81865
|
facts: input.facts,
|
|
81940
81866
|
kind,
|
|
81941
|
-
subject:
|
|
81867
|
+
subject: legacyConsumableSubject(input.deviceId, input.item.key),
|
|
81942
81868
|
titleKey: `system.${kind}.title`,
|
|
81943
|
-
bodyKey
|
|
81869
|
+
bodyKey,
|
|
81944
81870
|
textParams: {
|
|
81945
81871
|
...deviceTextParams(input.deviceId, input.identity),
|
|
81946
|
-
|
|
81947
|
-
|
|
81872
|
+
item: input.item.label,
|
|
81873
|
+
form: input.item.form,
|
|
81874
|
+
attention: input.attention
|
|
81948
81875
|
},
|
|
81949
81876
|
deviceId: input.deviceId,
|
|
81950
81877
|
...input.identity !== void 0 ? { deviceType: input.identity.type } : {}
|
|
@@ -82829,13 +82756,43 @@ function deliveryForKind(kind) {
|
|
|
82829
82756
|
return kind;
|
|
82830
82757
|
}
|
|
82831
82758
|
//#endregion
|
|
82832
|
-
//#region src/notification-center/alerts/
|
|
82759
|
+
//#region src/notification-center/alerts/tank-band.ts
|
|
82833
82760
|
/**
|
|
82834
|
-
*
|
|
82835
|
-
*
|
|
82836
|
-
*
|
|
82837
|
-
*
|
|
82761
|
+
* How long a tank change must HOLD before it counts (D642 review M2).
|
|
82762
|
+
*
|
|
82763
|
+
* A float switch sloshing at the edge of `low` / `full` flips its status back
|
|
82764
|
+
* and forth while the robot moves; each flip is a durable transition, and
|
|
82765
|
+
* without a hold each one is a raise and a recovery on the operator's phone.
|
|
82766
|
+
* One minute is two reload ticks: long enough to outlast a slosh, short enough
|
|
82767
|
+
* that a genuinely full dust bin is said within a minute and a half.
|
|
82838
82768
|
*/
|
|
82769
|
+
var NC_TANK_HOLD_MS = 6e4;
|
|
82770
|
+
function deriveTankBand(reading) {
|
|
82771
|
+
switch (reading.status) {
|
|
82772
|
+
case "low": return "low";
|
|
82773
|
+
case "full": return "full";
|
|
82774
|
+
case "ok": return "normal";
|
|
82775
|
+
case null: return null;
|
|
82776
|
+
}
|
|
82777
|
+
}
|
|
82778
|
+
/** `tank:<deviceId>:<key>` — the subject of one tank's episode. */
|
|
82779
|
+
function tankSubject(deviceId, key) {
|
|
82780
|
+
return `tank:${String(deviceId)}:${key}`;
|
|
82781
|
+
}
|
|
82782
|
+
/** The parts of a tank subject, or `null` when it is not one. */
|
|
82783
|
+
function parseTankSubject(subject) {
|
|
82784
|
+
const match = /^tank:(\d+):(.+)$/.exec(subject);
|
|
82785
|
+
if (match === null) return null;
|
|
82786
|
+
const deviceId = Number(match[1]);
|
|
82787
|
+
const key = match[2];
|
|
82788
|
+
if (!Number.isSafeInteger(deviceId) || key === void 0) return null;
|
|
82789
|
+
return {
|
|
82790
|
+
deviceId,
|
|
82791
|
+
key
|
|
82792
|
+
};
|
|
82793
|
+
}
|
|
82794
|
+
//#endregion
|
|
82795
|
+
//#region src/notification-center/alerts/open-alert.ts
|
|
82839
82796
|
function unmeasuredEpisode(input) {
|
|
82840
82797
|
const deviceId = alertSubjectDeviceId(input.subject);
|
|
82841
82798
|
if (deviceId === void 0) return null;
|
|
@@ -82853,7 +82810,23 @@ function unmeasuredEpisode(input) {
|
|
|
82853
82810
|
identity
|
|
82854
82811
|
});
|
|
82855
82812
|
const parts = parseConsumableSubject(input.subject);
|
|
82856
|
-
if (
|
|
82813
|
+
if (parts !== null && parts.legacy) {
|
|
82814
|
+
const attention = input.detail === "full" ? "full" : "low";
|
|
82815
|
+
const form = providerFormFromDetail(input.detail);
|
|
82816
|
+
return incomingFromProviderConsumableBand({
|
|
82817
|
+
facts,
|
|
82818
|
+
deviceId,
|
|
82819
|
+
band: "low",
|
|
82820
|
+
item: {
|
|
82821
|
+
key: parts.key,
|
|
82822
|
+
label: parts.key,
|
|
82823
|
+
form
|
|
82824
|
+
},
|
|
82825
|
+
attention,
|
|
82826
|
+
identity
|
|
82827
|
+
});
|
|
82828
|
+
}
|
|
82829
|
+
if (parts !== null) return incomingFromConsumableBand({
|
|
82857
82830
|
facts,
|
|
82858
82831
|
deviceId,
|
|
82859
82832
|
band: "low",
|
|
@@ -82867,25 +82840,30 @@ function unmeasuredEpisode(input) {
|
|
|
82867
82840
|
identity
|
|
82868
82841
|
});
|
|
82869
82842
|
const tank = parseTankSubject(input.subject);
|
|
82870
|
-
if (tank
|
|
82871
|
-
|
|
82872
|
-
|
|
82873
|
-
|
|
82874
|
-
|
|
82875
|
-
|
|
82876
|
-
|
|
82877
|
-
|
|
82843
|
+
if (tank !== null && tank.deviceId === deviceId) {
|
|
82844
|
+
const attention = input.detail === "full" ? "full" : "low";
|
|
82845
|
+
return incomingFromProviderConsumableBand({
|
|
82846
|
+
facts,
|
|
82847
|
+
deviceId,
|
|
82848
|
+
band: "low",
|
|
82849
|
+
item: {
|
|
82850
|
+
key: tank.key,
|
|
82851
|
+
label: tank.key,
|
|
82852
|
+
form: "tank"
|
|
82853
|
+
},
|
|
82854
|
+
attention,
|
|
82855
|
+
identity
|
|
82856
|
+
});
|
|
82857
|
+
}
|
|
82858
|
+
return null;
|
|
82859
|
+
}
|
|
82860
|
+
function providerFormFromDetail(detail) {
|
|
82861
|
+
if (detail === "full") return "bin";
|
|
82862
|
+
return "tank";
|
|
82878
82863
|
}
|
|
82879
|
-
/**
|
|
82880
|
-
* One alerting row as an open episode, or `null` when it cannot be worded (the
|
|
82881
|
-
* caller logs it). A legacy consumable row is skipped: it is adopted or
|
|
82882
|
-
* retired first. A tank whose band the row remembers is worded by it
|
|
82883
|
-
* (review M6), even without a reading.
|
|
82884
|
-
*/
|
|
82885
82864
|
function openAlertOf(row, input) {
|
|
82886
82865
|
const parts = parseConsumableSubject(row.subject);
|
|
82887
|
-
|
|
82888
|
-
const family = parts !== null ? "consumable" : row.subject.startsWith("tank:") ? "tank" : "battery";
|
|
82866
|
+
const family = parts !== null || row.subject.startsWith("tank:") ? "consumable" : "battery";
|
|
82889
82867
|
const subject = (input.restated ?? unmeasuredEpisode({
|
|
82890
82868
|
subject: row.subject,
|
|
82891
82869
|
family,
|
|
@@ -82894,7 +82872,7 @@ function openAlertOf(row, input) {
|
|
|
82894
82872
|
...row.detail !== void 0 ? { detail: row.detail } : {}
|
|
82895
82873
|
}))?.subject.systemEvent;
|
|
82896
82874
|
if (subject === void 0) return null;
|
|
82897
|
-
const measured = input.restated !== null ||
|
|
82875
|
+
const measured = input.restated !== null || row.detail !== void 0 || parts !== null && !parts.legacy && subject.textParams?.["percentage"] !== void 0;
|
|
82898
82876
|
return {
|
|
82899
82877
|
family,
|
|
82900
82878
|
since: row.since,
|
|
@@ -83010,8 +82988,8 @@ var NcDeviceAlerts = class {
|
|
|
83010
82988
|
* reminder sweep and for judging a threshold a rule gains later (D642).
|
|
83011
82989
|
*/
|
|
83012
82990
|
lastConsumable = /* @__PURE__ */ new Map();
|
|
83013
|
-
/**
|
|
83014
|
-
|
|
82991
|
+
/** Provider-judged items, keyed by episode subject (`consumable:<id>:<key>`). */
|
|
82992
|
+
lastProvider = /* @__PURE__ */ new Map();
|
|
83015
82993
|
/**
|
|
83016
82994
|
* Tank changes on HOLD (review M2): a band that would move the episode,
|
|
83017
82995
|
* first seen at `since`. It counts once it has held for `NC_TANK_HOLD_MS`,
|
|
@@ -83083,6 +83061,15 @@ var NcDeviceAlerts = class {
|
|
|
83083
83061
|
* genuine `replace` would then read as a transition out of a state nobody
|
|
83084
83062
|
* observed.
|
|
83085
83063
|
*/
|
|
83064
|
+
/** One item from the consumables slice — every form (D934). */
|
|
83065
|
+
observeConsumableItem(deviceId, sliceItem) {
|
|
83066
|
+
this.closeLegacyTankEpisode(deviceId, sliceItem.key);
|
|
83067
|
+
if (sliceItem.wear !== null) {
|
|
83068
|
+
this.observeConsumable(deviceId, sliceItem.wear);
|
|
83069
|
+
return;
|
|
83070
|
+
}
|
|
83071
|
+
this.observeProviderConsumable(deviceId, sliceItem);
|
|
83072
|
+
}
|
|
83086
83073
|
observeConsumable(deviceId, item) {
|
|
83087
83074
|
this.lastConsumable.set(itemKeyOf(deviceId, item.key), {
|
|
83088
83075
|
deviceId,
|
|
@@ -83099,31 +83086,51 @@ var NcDeviceAlerts = class {
|
|
|
83099
83086
|
const settings = this.deps.settings();
|
|
83100
83087
|
for (const thresholdPercent of thresholds) this.judgeConsumable(deviceId, item, thresholdPercent, settings);
|
|
83101
83088
|
}
|
|
83102
|
-
/**
|
|
83103
|
-
|
|
83104
|
-
|
|
83105
|
-
*
|
|
83106
|
-
* The provider's verdict is the band (`tank-band.ts`); an unknown status
|
|
83107
|
-
* leaves everything untouched, an open episode included.
|
|
83108
|
-
*/
|
|
83109
|
-
observeTank(deviceId, tank) {
|
|
83110
|
-
const band = deriveTankBand(tank);
|
|
83089
|
+
/** A supply / waste / tank-form item — provider verdict only (D934). */
|
|
83090
|
+
observeProviderConsumable(deviceId, sliceItem) {
|
|
83091
|
+
const band = deriveTankBand(providerTankReading(sliceItem));
|
|
83111
83092
|
if (band === null) return;
|
|
83112
|
-
const subject =
|
|
83113
|
-
|
|
83093
|
+
const subject = legacyConsumableSubject(deviceId, sliceItem.key);
|
|
83094
|
+
const attention = band === "full" ? "full" : "low";
|
|
83095
|
+
this.lastProvider.set(subject, {
|
|
83114
83096
|
deviceId,
|
|
83115
|
-
|
|
83097
|
+
item: sliceItem,
|
|
83098
|
+
attention
|
|
83116
83099
|
});
|
|
83117
83100
|
if (!this.tankChangeHeld(subject, deviceId, band)) return;
|
|
83118
|
-
this.apply(subject, band !== "normal", this.deps.settings(), (facts, alerting) =>
|
|
83101
|
+
this.apply(subject, band !== "normal", this.deps.settings(), (facts, alerting) => incomingFromProviderConsumableBand({
|
|
83119
83102
|
facts,
|
|
83120
83103
|
deviceId,
|
|
83121
|
-
band: alerting ?
|
|
83122
|
-
|
|
83123
|
-
|
|
83104
|
+
band: alerting ? "low" : "normal",
|
|
83105
|
+
item: {
|
|
83106
|
+
key: sliceItem.key,
|
|
83107
|
+
label: sliceItem.label,
|
|
83108
|
+
form: sliceItem.form
|
|
83109
|
+
},
|
|
83110
|
+
attention,
|
|
83124
83111
|
identity: this.deps.lookup(deviceId)
|
|
83125
83112
|
}), band === "normal" ? void 0 : band);
|
|
83126
83113
|
}
|
|
83114
|
+
/** A leftover `tank:<deviceId>:<key>` row is closed silently when the item is seen as a consumable. */
|
|
83115
|
+
closeLegacyTankEpisode(deviceId, key) {
|
|
83116
|
+
const subject = tankSubject(deviceId, key);
|
|
83117
|
+
const row = this.deps.rows.get(subject);
|
|
83118
|
+
if (row === void 0 || !row.alerting) return;
|
|
83119
|
+
this.logger()?.info("legacy tank episode closed — the consumables cap owns this item now (D934)", {
|
|
83120
|
+
tags: { deviceId },
|
|
83121
|
+
meta: {
|
|
83122
|
+
subject,
|
|
83123
|
+
key
|
|
83124
|
+
}
|
|
83125
|
+
});
|
|
83126
|
+
this.deps.rows.put({
|
|
83127
|
+
...row,
|
|
83128
|
+
alerting: false
|
|
83129
|
+
});
|
|
83130
|
+
this.lastProvider.delete(legacyConsumableSubject(deviceId, key));
|
|
83131
|
+
this.tankHold.delete(subject);
|
|
83132
|
+
this.tankHold.delete(legacyConsumableSubject(deviceId, key));
|
|
83133
|
+
}
|
|
83127
83134
|
/**
|
|
83128
83135
|
* Has this tank band held long enough to move its episode? A band that
|
|
83129
83136
|
* would not move it (a repeat, or low ⇄ full inside one open episode) passes
|
|
@@ -83164,12 +83171,12 @@ var NcDeviceAlerts = class {
|
|
|
83164
83171
|
const nowMs = this.now();
|
|
83165
83172
|
for (const [subject, held] of [...this.tankHold]) {
|
|
83166
83173
|
if (nowMs - held.since < NC_TANK_HOLD_MS) continue;
|
|
83167
|
-
const reading = this.
|
|
83174
|
+
const reading = this.lastProvider.get(subject);
|
|
83168
83175
|
if (reading === void 0) {
|
|
83169
83176
|
this.tankHold.delete(subject);
|
|
83170
83177
|
continue;
|
|
83171
83178
|
}
|
|
83172
|
-
this.
|
|
83179
|
+
this.observeProviderConsumable(reading.deviceId, reading.item);
|
|
83173
83180
|
}
|
|
83174
83181
|
}
|
|
83175
83182
|
/** One item against ONE threshold: its own episode, its own hysteresis. */
|
|
@@ -83293,14 +83300,14 @@ var NcDeviceAlerts = class {
|
|
|
83293
83300
|
const subjects = this.deps.rows.all().map((row) => row.subject).filter((subject) => alertSubjectDeviceId(subject) === deviceId);
|
|
83294
83301
|
for (const subject of subjects) {
|
|
83295
83302
|
this.lastReading.delete(subject);
|
|
83296
|
-
this.
|
|
83303
|
+
this.lastProvider.delete(subject);
|
|
83297
83304
|
this.offeredAt.delete(subject);
|
|
83298
83305
|
await this.deps.rows.remove(subject);
|
|
83299
83306
|
}
|
|
83300
83307
|
this.lastReading.delete(`battery:${deviceId}`);
|
|
83301
83308
|
for (const [key, reading] of this.lastConsumable) if (reading.deviceId === deviceId) this.lastConsumable.delete(key);
|
|
83302
83309
|
for (const subject of this.tankHold.keys()) if (alertSubjectDeviceId(subject) === deviceId) this.tankHold.delete(subject);
|
|
83303
|
-
for (const subject of this.
|
|
83310
|
+
for (const subject of this.lastProvider.keys()) if (alertSubjectDeviceId(subject) === deviceId) this.lastProvider.delete(subject);
|
|
83304
83311
|
return subjects.length;
|
|
83305
83312
|
}
|
|
83306
83313
|
/**
|
|
@@ -83377,8 +83384,23 @@ var NcDeviceAlerts = class {
|
|
|
83377
83384
|
/** How to re-word a subject's alert from the last reading, or `null`. */
|
|
83378
83385
|
restatementOf(subject) {
|
|
83379
83386
|
const parts = parseConsumableSubject(subject);
|
|
83387
|
+
if (parts !== null && parts.legacy) {
|
|
83388
|
+
const provider = this.lastProvider.get(subject);
|
|
83389
|
+
if (provider !== void 0) return (facts) => incomingFromProviderConsumableBand({
|
|
83390
|
+
facts,
|
|
83391
|
+
deviceId: provider.deviceId,
|
|
83392
|
+
band: "low",
|
|
83393
|
+
item: {
|
|
83394
|
+
key: provider.item.key,
|
|
83395
|
+
label: provider.item.label,
|
|
83396
|
+
form: provider.item.form
|
|
83397
|
+
},
|
|
83398
|
+
attention: provider.attention,
|
|
83399
|
+
identity: this.deps.lookup(provider.deviceId)
|
|
83400
|
+
});
|
|
83401
|
+
}
|
|
83380
83402
|
const consumable = parts === null || parts.legacy ? void 0 : this.lastConsumable.get(itemKeyOf(parts.deviceId, parts.key));
|
|
83381
|
-
if (parts !== null && consumable !== void 0) return (facts) => incomingFromConsumableBand({
|
|
83403
|
+
if (parts !== null && !parts.legacy && consumable !== void 0) return (facts) => incomingFromConsumableBand({
|
|
83382
83404
|
facts,
|
|
83383
83405
|
deviceId: consumable.deviceId,
|
|
83384
83406
|
band: "low",
|
|
@@ -83386,16 +83408,6 @@ var NcDeviceAlerts = class {
|
|
|
83386
83408
|
thresholdPercent: parts.thresholdPercent,
|
|
83387
83409
|
identity: this.deps.lookup(consumable.deviceId)
|
|
83388
83410
|
});
|
|
83389
|
-
const tank = this.lastTank.get(subject);
|
|
83390
|
-
const tankBand = tank === void 0 ? null : deriveTankBand(tank.tank);
|
|
83391
|
-
if (tank !== void 0 && (tankBand === "low" || tankBand === "full")) return (facts) => incomingFromTankBand({
|
|
83392
|
-
facts,
|
|
83393
|
-
deviceId: tank.deviceId,
|
|
83394
|
-
band: tankBand,
|
|
83395
|
-
tank: tank.tank.key,
|
|
83396
|
-
...tank.tank.label !== void 0 ? { label: tank.tank.label } : {},
|
|
83397
|
-
identity: this.deps.lookup(tank.deviceId)
|
|
83398
|
-
});
|
|
83399
83411
|
const battery = this.lastReading.get(subject);
|
|
83400
83412
|
if (battery !== void 0) return (facts) => incomingFromBatteryBand({
|
|
83401
83413
|
facts,
|
|
@@ -83528,8 +83540,52 @@ function tagsOf(subject) {
|
|
|
83528
83540
|
const deviceId = alertSubjectDeviceId(subject);
|
|
83529
83541
|
return deviceId !== void 0 ? { tags: { deviceId } } : {};
|
|
83530
83542
|
}
|
|
83543
|
+
function providerTankReading(item) {
|
|
83544
|
+
const base = {
|
|
83545
|
+
key: item.key,
|
|
83546
|
+
label: item.label,
|
|
83547
|
+
level: item.level,
|
|
83548
|
+
status: null
|
|
83549
|
+
};
|
|
83550
|
+
const status = item.status;
|
|
83551
|
+
if (item.kind === "supply") switch (status) {
|
|
83552
|
+
case "low":
|
|
83553
|
+
case "empty":
|
|
83554
|
+
case "replace": return {
|
|
83555
|
+
...base,
|
|
83556
|
+
status: "low"
|
|
83557
|
+
};
|
|
83558
|
+
case "ok":
|
|
83559
|
+
case "full": return {
|
|
83560
|
+
...base,
|
|
83561
|
+
status: "ok"
|
|
83562
|
+
};
|
|
83563
|
+
default: return base;
|
|
83564
|
+
}
|
|
83565
|
+
if (item.kind === "waste") switch (status) {
|
|
83566
|
+
case "full":
|
|
83567
|
+
case "replace": return {
|
|
83568
|
+
...base,
|
|
83569
|
+
status: "full"
|
|
83570
|
+
};
|
|
83571
|
+
case "ok":
|
|
83572
|
+
case "low":
|
|
83573
|
+
case "empty": return {
|
|
83574
|
+
...base,
|
|
83575
|
+
status: "ok"
|
|
83576
|
+
};
|
|
83577
|
+
default: return base;
|
|
83578
|
+
}
|
|
83579
|
+
return base;
|
|
83580
|
+
}
|
|
83531
83581
|
//#endregion
|
|
83532
83582
|
//#region src/notification-center/alerts/consumable-routing.ts
|
|
83583
|
+
/**
|
|
83584
|
+
* The `consumables` slice — one vocabulary for every form (D934).
|
|
83585
|
+
*
|
|
83586
|
+
* Every item is a consumable reading: wear parts judged at the rule's threshold,
|
|
83587
|
+
* supply and waste judged on the provider's verdict (held like a float switch).
|
|
83588
|
+
*/
|
|
83533
83589
|
function wearStatus(status) {
|
|
83534
83590
|
switch (status) {
|
|
83535
83591
|
case "replace":
|
|
@@ -83539,59 +83595,44 @@ function wearStatus(status) {
|
|
|
83539
83595
|
default: return null;
|
|
83540
83596
|
}
|
|
83541
83597
|
}
|
|
83542
|
-
function
|
|
83543
|
-
|
|
83544
|
-
case "low":
|
|
83545
|
-
case "empty":
|
|
83546
|
-
case "replace": return "low";
|
|
83547
|
-
case "ok":
|
|
83548
|
-
case "full": return "ok";
|
|
83549
|
-
default: return null;
|
|
83550
|
-
}
|
|
83598
|
+
function consumableKindOf(raw) {
|
|
83599
|
+
return typeof raw.kind === "string" ? raw.kind : void 0;
|
|
83551
83600
|
}
|
|
83552
|
-
function
|
|
83553
|
-
|
|
83554
|
-
|
|
83555
|
-
|
|
83556
|
-
|
|
83557
|
-
|
|
83558
|
-
|
|
83559
|
-
default: return null;
|
|
83560
|
-
}
|
|
83601
|
+
function consumableFormOfRaw(raw) {
|
|
83602
|
+
const kind = consumableKindOf(raw);
|
|
83603
|
+
const formField = typeof raw.form === "string" ? raw.form : void 0;
|
|
83604
|
+
return require_clip_model_registry.consumableFormOf({
|
|
83605
|
+
...kind === "wear" || kind === "supply" || kind === "waste" ? { kind } : {},
|
|
83606
|
+
...formField === "part" || formField === "tank" || formField === "product" || formField === "bin" ? { form: formField } : {}
|
|
83607
|
+
});
|
|
83561
83608
|
}
|
|
83562
|
-
/**
|
|
83563
|
-
function
|
|
83564
|
-
const
|
|
83565
|
-
|
|
83566
|
-
|
|
83567
|
-
|
|
83568
|
-
|
|
83569
|
-
|
|
83570
|
-
|
|
83571
|
-
|
|
83572
|
-
|
|
83573
|
-
};
|
|
83609
|
+
/** Parse one slice entry into the unified consumable item shape. */
|
|
83610
|
+
function parseConsumableSliceItem(raw) {
|
|
83611
|
+
const kind = consumableKindOf(raw);
|
|
83612
|
+
const form = consumableFormOfRaw(raw);
|
|
83613
|
+
const status = typeof raw.status === "string" ? raw.status : null;
|
|
83614
|
+
const wear = kind === void 0 || kind === "wear" ? {
|
|
83615
|
+
key: raw.key,
|
|
83616
|
+
label: raw.label,
|
|
83617
|
+
level: raw.level,
|
|
83618
|
+
status: wearStatus(status)
|
|
83619
|
+
} : null;
|
|
83574
83620
|
return {
|
|
83575
|
-
|
|
83576
|
-
|
|
83577
|
-
|
|
83578
|
-
|
|
83579
|
-
|
|
83580
|
-
|
|
83581
|
-
|
|
83621
|
+
key: raw.key,
|
|
83622
|
+
label: raw.label,
|
|
83623
|
+
level: raw.level,
|
|
83624
|
+
status,
|
|
83625
|
+
kind,
|
|
83626
|
+
form,
|
|
83627
|
+
wear
|
|
83582
83628
|
};
|
|
83583
83629
|
}
|
|
83584
83630
|
function isRecord$1(value) {
|
|
83585
83631
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83586
83632
|
}
|
|
83587
83633
|
/**
|
|
83588
|
-
* A `device.state-changed` carrying a `consumables` slice
|
|
83589
|
-
*
|
|
83590
|
-
*
|
|
83591
|
-
* An item is kept only when it carries a usable `key` — the subject is built
|
|
83592
|
-
* from it. `level` and `status` are each optional; an item with NEITHER is
|
|
83593
|
-
* still returned, because deciding that it has told us nothing belongs to the
|
|
83594
|
-
* band derivation and not to a parser (D393: absent is not zero).
|
|
83634
|
+
* A `device.state-changed` carrying a `consumables` slice; `null` for any other
|
|
83635
|
+
* event, a malformed slice or an empty list.
|
|
83595
83636
|
*/
|
|
83596
83637
|
function consumablesSliceOf(event) {
|
|
83597
83638
|
if (event.category !== "device.state-changed") return null;
|
|
@@ -83602,8 +83643,7 @@ function consumablesSliceOf(event) {
|
|
|
83602
83643
|
if (typeof deviceId !== "number" || !isRecord$1(slice)) return null;
|
|
83603
83644
|
const rawItems = slice["items"];
|
|
83604
83645
|
if (!Array.isArray(rawItems)) return null;
|
|
83605
|
-
const
|
|
83606
|
-
const tanks = [];
|
|
83646
|
+
const items = [];
|
|
83607
83647
|
for (const entry of rawItems) {
|
|
83608
83648
|
if (!isRecord$1(entry)) continue;
|
|
83609
83649
|
const key = entry["key"];
|
|
@@ -83612,21 +83652,20 @@ function consumablesSliceOf(event) {
|
|
|
83612
83652
|
const level = entry["level"];
|
|
83613
83653
|
const status = entry["status"];
|
|
83614
83654
|
const kind = entry["kind"];
|
|
83615
|
-
const
|
|
83655
|
+
const form = entry["form"];
|
|
83656
|
+
items.push(parseConsumableSliceItem({
|
|
83616
83657
|
key,
|
|
83617
83658
|
label: typeof label === "string" && label.length > 0 ? label : key,
|
|
83618
83659
|
level: typeof level === "number" && Number.isFinite(level) ? level : null,
|
|
83619
83660
|
status: typeof status === "string" ? status : null,
|
|
83620
|
-
kind: typeof kind === "string" ? kind : void 0
|
|
83621
|
-
|
|
83622
|
-
|
|
83623
|
-
else consumables.push(routed.item);
|
|
83661
|
+
kind: typeof kind === "string" ? kind : void 0,
|
|
83662
|
+
form: typeof form === "string" ? form : void 0
|
|
83663
|
+
}));
|
|
83624
83664
|
}
|
|
83625
|
-
if (
|
|
83665
|
+
if (items.length === 0) return null;
|
|
83626
83666
|
return {
|
|
83627
83667
|
deviceId,
|
|
83628
|
-
|
|
83629
|
-
tanks
|
|
83668
|
+
items
|
|
83630
83669
|
};
|
|
83631
83670
|
}
|
|
83632
83671
|
//#endregion
|
|
@@ -85274,12 +85313,10 @@ var NcDeviceStateCache = class {
|
|
|
85274
85313
|
* a job that COMPLETED, and the state it leaves behind is not a condition that
|
|
85275
85314
|
* is still true. The ADR records why it is excluded rather than approximated.
|
|
85276
85315
|
*/
|
|
85277
|
-
/** One source: the cap's Zod enum (D698 LOW-6). */
|
|
85278
|
-
var NC_STATE_SUMMARY_SECTION_IDS = require_clip_model_registry.NcStateSummarySectionIdSchema.options;
|
|
85279
85316
|
/** The kind groups that have no section, and why — said on the preview. */
|
|
85280
85317
|
var NC_STATE_SUMMARY_EXCLUDED_GROUPS = { "backups-exports": "a backup or an export is a completed job, not a state that is still true" };
|
|
85281
85318
|
function isStateSectionId(value) {
|
|
85282
|
-
return NC_STATE_SUMMARY_SECTION_IDS.some((id) => id === value);
|
|
85319
|
+
return require_clip_model_registry.NC_STATE_SUMMARY_SECTION_IDS.some((id) => id === value);
|
|
85283
85320
|
}
|
|
85284
85321
|
/** The template variable that carries one section's lines (`template-vars.ts`). */
|
|
85285
85322
|
var NC_STATE_SECTION_VAR = {
|
|
@@ -85287,7 +85324,6 @@ var NC_STATE_SECTION_VAR = {
|
|
|
85287
85324
|
streams: "summaryStreams",
|
|
85288
85325
|
battery: "summaryBattery",
|
|
85289
85326
|
consumables: "summaryConsumables",
|
|
85290
|
-
tanks: "summaryTanks",
|
|
85291
85327
|
"detection-blind": "summaryDetection",
|
|
85292
85328
|
nodes: "summaryNodes",
|
|
85293
85329
|
addons: "summaryAddons",
|
|
@@ -85305,11 +85341,13 @@ function sectionKinds(section) {
|
|
|
85305
85341
|
* direction, only the event did.
|
|
85306
85342
|
*/
|
|
85307
85343
|
function sectionsOfRule(condition) {
|
|
85308
|
-
|
|
85344
|
+
const kinds = condition.kinds;
|
|
85345
|
+
return require_clip_model_registry.NC_STATE_SUMMARY_SECTION_IDS.filter((section) => sectionKinds(section).some((kind) => kinds.includes(kind)));
|
|
85309
85346
|
}
|
|
85310
85347
|
/** The kind groups the rule selected that have no section. */
|
|
85311
85348
|
function excludedGroupsOfRule(condition) {
|
|
85312
|
-
|
|
85349
|
+
const kinds = condition.kinds;
|
|
85350
|
+
return require_clip_model_registry.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);
|
|
85313
85351
|
}
|
|
85314
85352
|
/**
|
|
85315
85353
|
* The condition the summary judges its items with: the rule's own, with its
|
|
@@ -90056,6 +90094,12 @@ function classCounts(members) {
|
|
|
90056
90094
|
* params go last because they are the row's own truth about a system event and
|
|
90057
90095
|
* outrank the live vocabulary for exactly those names.
|
|
90058
90096
|
*/
|
|
90097
|
+
/** Intake-only keys for body variant selection — not operator template vars (D934). */
|
|
90098
|
+
function systemEventParamsForTemplate(params) {
|
|
90099
|
+
if (params === void 0) return {};
|
|
90100
|
+
const { form: _form, attention: _attention, ...rest } = params;
|
|
90101
|
+
return rest;
|
|
90102
|
+
}
|
|
90059
90103
|
function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
|
|
90060
90104
|
const subject = entry.payload.subject;
|
|
90061
90105
|
const occupancy = subject.occupancy;
|
|
@@ -90103,7 +90147,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
|
|
|
90103
90147
|
latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : "",
|
|
90104
90148
|
...systemEvent?.deviceId !== void 0 ? { deviceName: String(systemEvent.deviceId) } : {},
|
|
90105
90149
|
...systemEvent?.nodeId !== void 0 && (systemEvent.kind === "node-online" || systemEvent.kind === "node-offline") ? { nodeName: systemEvent.nodeId } : {},
|
|
90106
|
-
...systemEvent?.textParams
|
|
90150
|
+
...systemEventParamsForTemplate(systemEvent?.textParams)
|
|
90107
90151
|
};
|
|
90108
90152
|
}
|
|
90109
90153
|
/**
|
|
@@ -99566,15 +99610,30 @@ function deviceParams(deviceId, identity) {
|
|
|
99566
99610
|
deviceName: name.length > 0 ? name : String(deviceId)
|
|
99567
99611
|
};
|
|
99568
99612
|
}
|
|
99569
|
-
function
|
|
99613
|
+
function consumableSummaryLineKey(params, measured) {
|
|
99614
|
+
const form = params["form"] ?? "part";
|
|
99615
|
+
if (form === "part") return measured && params["percentage"] !== void 0 ? "summary.line.consumable.part" : "summary.line.consumable.part.unmeasured";
|
|
99616
|
+
if (form === "tank") return params["attention"] === "full" ? "summary.line.consumable.tank-full" : "summary.line.consumable.tank";
|
|
99617
|
+
if (form === "product") return "summary.line.consumable.product";
|
|
99618
|
+
return "summary.line.consumable.bin";
|
|
99619
|
+
}
|
|
99620
|
+
function alertItem(section, open, lookup) {
|
|
99570
99621
|
const params = open.subject.textParams ?? {};
|
|
99571
|
-
const
|
|
99622
|
+
const lineKey = section === "consumables" ? consumableSummaryLineKey(params, open.measured) : open.subject.bodyKey ?? "";
|
|
99623
|
+
const lineParams = section === "consumables" ? (({ deviceName: _deviceName, ...rest }) => rest)(params) : params;
|
|
99624
|
+
const deviceId = open.subject.deviceId;
|
|
99625
|
+
const identity = deviceId !== void 0 ? lookup(deviceId) : void 0;
|
|
99626
|
+
const deviceName = params["deviceName"] ?? (identity !== void 0 && identity.name.length > 0 ? identity.name : deviceId !== void 0 ? String(deviceId) : void 0);
|
|
99572
99627
|
return {
|
|
99573
99628
|
section,
|
|
99574
99629
|
subject: open.subject,
|
|
99575
|
-
lineKey
|
|
99576
|
-
params,
|
|
99577
|
-
since: open.since
|
|
99630
|
+
lineKey,
|
|
99631
|
+
params: lineParams,
|
|
99632
|
+
since: open.since,
|
|
99633
|
+
...section === "consumables" && deviceId !== void 0 && deviceName !== void 0 ? { device: {
|
|
99634
|
+
id: deviceId,
|
|
99635
|
+
name: deviceName
|
|
99636
|
+
} } : {}
|
|
99578
99637
|
};
|
|
99579
99638
|
}
|
|
99580
99639
|
function livenessItem(row, lookup) {
|
|
@@ -99703,7 +99762,7 @@ function stateItem(row, lookup, nodes) {
|
|
|
99703
99762
|
* (`parentDeviceId` set) still owns those alerts — filtering every accessory
|
|
99704
99763
|
* here dropped every tank line of the 2026-10-05 evening summary for
|
|
99705
99764
|
* deviceId 4385 while the episode was open and the instant path had already
|
|
99706
|
-
* held
|
|
99765
|
+
* held a legacy tank kind for that rule.
|
|
99707
99766
|
*/
|
|
99708
99767
|
function stillTrue(item, ports) {
|
|
99709
99768
|
const deviceId = item.subject.deviceId;
|
|
@@ -99743,16 +99802,16 @@ function readSection(section, ports, nowMs) {
|
|
|
99743
99802
|
}
|
|
99744
99803
|
/** Which ports a section needs, and why each one is unavailable. */
|
|
99745
99804
|
function readRaw(section, ports, nowMs) {
|
|
99746
|
-
if (section === "battery" || section === "consumables"
|
|
99805
|
+
if (section === "battery" || section === "consumables") {
|
|
99747
99806
|
const alerts = ports.alerts();
|
|
99748
99807
|
if (alerts === null) return {
|
|
99749
99808
|
answered: false,
|
|
99750
99809
|
reason: UNREAD_ALERTS
|
|
99751
99810
|
};
|
|
99752
|
-
const family = section === "battery" ? "battery" :
|
|
99811
|
+
const family = section === "battery" ? "battery" : "consumable";
|
|
99753
99812
|
return {
|
|
99754
99813
|
answered: true,
|
|
99755
|
-
items: alerts.filter((open) => open.family === family).map((open) => alertItem(section, open))
|
|
99814
|
+
items: alerts.filter((open) => open.family === family).map((open) => alertItem(section, open, ports.lookup))
|
|
99756
99815
|
};
|
|
99757
99816
|
}
|
|
99758
99817
|
const state = ports.state();
|
|
@@ -99912,11 +99971,12 @@ function wordsOf(input, section) {
|
|
|
99912
99971
|
/** One bullet per item; an item with a `node` is listed under its node's heading (D708). */
|
|
99913
99972
|
function linesOf(input, items) {
|
|
99914
99973
|
const bullet = (item) => `• ${lineOf(input, item)}`;
|
|
99915
|
-
if (
|
|
99916
|
-
return groupByNode(items, input.texts.language).flatMap((group) => [input.texts.text({
|
|
99974
|
+
if (items.some((item) => item.node !== void 0)) return groupByNode(items, input.texts.language).flatMap((group) => [input.texts.text({
|
|
99917
99975
|
key: "summary.node.heading",
|
|
99918
99976
|
vars: { node: group.node.name }
|
|
99919
99977
|
}), ...group.items.map(bullet)]).join("\n");
|
|
99978
|
+
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");
|
|
99979
|
+
return items.map(bullet).join("\n");
|
|
99920
99980
|
}
|
|
99921
99981
|
/**
|
|
99922
99982
|
* The items cut per node, in a STABLE order: the hub first, then by name, then
|
|
@@ -99941,6 +100001,21 @@ var HUB_HEADING = {
|
|
|
99941
100001
|
name: "hub",
|
|
99942
100002
|
isHub: true
|
|
99943
100003
|
};
|
|
100004
|
+
/** Consumables items under each device, devices ordered by name (D934). */
|
|
100005
|
+
function groupByDevice(items, language) {
|
|
100006
|
+
const groups = /* @__PURE__ */ new Map();
|
|
100007
|
+
for (const item of items) {
|
|
100008
|
+
const device = item.device;
|
|
100009
|
+
if (device === void 0) continue;
|
|
100010
|
+
const group = groups.get(device.id);
|
|
100011
|
+
if (group === void 0) groups.set(device.id, {
|
|
100012
|
+
device,
|
|
100013
|
+
items: [item]
|
|
100014
|
+
});
|
|
100015
|
+
else group.items.push(item);
|
|
100016
|
+
}
|
|
100017
|
+
return [...groups.values()].toSorted((a, b) => a.device.name.localeCompare(b.device.name, language));
|
|
100018
|
+
}
|
|
99944
100019
|
function stateSummaryText(input) {
|
|
99945
100020
|
const sectionVars = {};
|
|
99946
100021
|
const sections = [];
|
|
@@ -104007,8 +104082,7 @@ var NotificationCenter = class {
|
|
|
104007
104082
|
}
|
|
104008
104083
|
const consumables = consumablesSliceOf(event);
|
|
104009
104084
|
if (consumables !== null) {
|
|
104010
|
-
for (const item of consumables.
|
|
104011
|
-
for (const tank of consumables.tanks) this.deviceAlerts.observeTank(consumables.deviceId, tank);
|
|
104085
|
+
for (const item of consumables.items) this.deviceAlerts.observeConsumableItem(consumables.deviceId, item);
|
|
104012
104086
|
return;
|
|
104013
104087
|
}
|
|
104014
104088
|
const lookup = this.deviceDirectory.lookup();
|