iobroker.parcelapp 0.2.2 → 0.2.4

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.
@@ -1,268 +1,377 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StateManager = void 0;
4
- const types_1 = require("./types");
5
- /** Status codes that have expected delivery date/time */
6
- const TRACKABLE_STATUSES = new Set([2, 4, 8]);
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var state_manager_exports = {};
20
+ __export(state_manager_exports, {
21
+ StateManager: () => StateManager
22
+ });
23
+ module.exports = __toCommonJS(state_manager_exports);
24
+ var import_types = require("./types");
25
+ const TRACKABLE_STATUSES = /* @__PURE__ */ new Set([2, 4, 8]);
7
26
  const ESTIMATE_LABELS = {
8
- de: {
9
- overdue: "überfällig",
10
- today: "heute",
11
- tomorrow: "morgen",
12
- days: "in %d Tagen",
13
- },
14
- en: {
15
- overdue: "overdue",
16
- today: "today",
17
- tomorrow: "tomorrow",
18
- days: "in %d days",
19
- },
27
+ de: {
28
+ overdue: "\xFCberf\xE4llig",
29
+ today: "heute",
30
+ tomorrow: "morgen",
31
+ days: "in %d Tagen"
32
+ },
33
+ en: {
34
+ overdue: "overdue",
35
+ today: "today",
36
+ tomorrow: "tomorrow",
37
+ days: "in %d days"
38
+ }
20
39
  };
21
- /** Manages ioBroker states for parcel deliveries */
22
40
  class StateManager {
23
- adapter;
24
- /** @param adapter The ioBroker adapter instance */
25
- constructor(adapter) {
26
- this.adapter = adapter;
41
+ adapter;
42
+ /** @param adapter The ioBroker adapter instance */
43
+ constructor(adapter) {
44
+ this.adapter = adapter;
45
+ }
46
+ /**
47
+ * Sanitize a string for use as ioBroker object ID.
48
+ *
49
+ * @param name Raw string to sanitize
50
+ */
51
+ sanitize(name) {
52
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 50) || "unknown";
53
+ }
54
+ /**
55
+ * Build a unique package ID from a delivery.
56
+ *
57
+ * @param delivery The delivery to build an ID for
58
+ */
59
+ packageId(delivery) {
60
+ let id = this.sanitize(delivery.tracking_number);
61
+ if (delivery.extra_information) {
62
+ id += `_${this.sanitize(delivery.extra_information)}`;
63
+ }
64
+ return id;
65
+ }
66
+ /**
67
+ * Update or create all states for a delivery.
68
+ *
69
+ * @param delivery The delivery data from API
70
+ * @param carrierName Resolved carrier display name
71
+ */
72
+ async updateDelivery(delivery, carrierName) {
73
+ const pkgId = this.packageId(delivery);
74
+ const devicePath = `deliveries.${pkgId}`;
75
+ await this.adapter.extendObjectAsync(devicePath, {
76
+ type: "device",
77
+ common: {
78
+ name: delivery.description || `Package ${delivery.tracking_number}`
79
+ },
80
+ native: {}
81
+ });
82
+ const statusCode = parseInt(delivery.status_code, 10) || 0;
83
+ const lang = this.adapter.config.language || "de";
84
+ const labels = lang === "de" ? import_types.STATUS_LABELS_DE : import_types.STATUS_LABELS_EN;
85
+ await Promise.all([
86
+ this.createAndSet(
87
+ `${devicePath}.carrier`,
88
+ "Carrier",
89
+ "string",
90
+ "text",
91
+ carrierName
92
+ ),
93
+ this.createAndSet(
94
+ `${devicePath}.status`,
95
+ "Status",
96
+ "string",
97
+ "text",
98
+ labels[statusCode] || `Unknown (${statusCode})`
99
+ ),
100
+ this.createAndSet(
101
+ `${devicePath}.statusCode`,
102
+ "Status Code",
103
+ "number",
104
+ "value",
105
+ statusCode
106
+ ),
107
+ this.createAndSet(
108
+ `${devicePath}.description`,
109
+ "Description",
110
+ "string",
111
+ "text",
112
+ delivery.description || ""
113
+ ),
114
+ this.createAndSet(
115
+ `${devicePath}.trackingNumber`,
116
+ "Tracking Number",
117
+ "string",
118
+ "text",
119
+ delivery.tracking_number
120
+ ),
121
+ this.createAndSet(
122
+ `${devicePath}.extraInfo`,
123
+ "Extra Information",
124
+ "string",
125
+ "text",
126
+ delivery.extra_information || ""
127
+ ),
128
+ this.createAndSet(
129
+ `${devicePath}.deliveryWindow`,
130
+ "Delivery Window",
131
+ "string",
132
+ "text",
133
+ this.calculateDeliveryWindow(delivery, statusCode)
134
+ ),
135
+ this.createAndSet(
136
+ `${devicePath}.deliveryEstimate`,
137
+ "Delivery Estimate",
138
+ "string",
139
+ "text",
140
+ this.calculateDeliveryEstimate(delivery, statusCode)
141
+ ),
142
+ this.createAndSet(
143
+ `${devicePath}.lastEvent`,
144
+ "Last Event",
145
+ "string",
146
+ "text",
147
+ this.formatLastEvent(delivery)
148
+ ),
149
+ this.createAndSet(
150
+ `${devicePath}.lastLocation`,
151
+ "Last Location",
152
+ "string",
153
+ "text",
154
+ this.extractLastLocation(delivery)
155
+ ),
156
+ this.createAndSet(
157
+ `${devicePath}.lastUpdated`,
158
+ "Last Updated",
159
+ "string",
160
+ "date",
161
+ (/* @__PURE__ */ new Date()).toISOString()
162
+ )
163
+ ]);
164
+ }
165
+ /**
166
+ * Update summary states. Expects already-filtered active deliveries.
167
+ *
168
+ * @param activeDeliveries Only active (non-delivered) deliveries
169
+ */
170
+ async updateSummary(activeDeliveries) {
171
+ await this.adapter.extendObjectAsync("summary", {
172
+ type: "channel",
173
+ common: { name: "Summary" },
174
+ native: {}
175
+ });
176
+ const todayDeliveries = activeDeliveries.filter((d) => {
177
+ const statusCode = parseInt(d.status_code, 10) || 0;
178
+ const estimate = this.calculateDeliveryEstimate(d, statusCode);
179
+ return estimate === "heute" || estimate === "today";
180
+ });
181
+ await Promise.all([
182
+ this.createAndSet(
183
+ "summary.activeCount",
184
+ "Active Deliveries",
185
+ "number",
186
+ "value",
187
+ activeDeliveries.length
188
+ ),
189
+ this.createAndSet(
190
+ "summary.todayCount",
191
+ "Deliveries Today",
192
+ "number",
193
+ "value",
194
+ todayDeliveries.length
195
+ ),
196
+ this.createAndSet(
197
+ "summary.deliveryWindow",
198
+ "Combined Delivery Window",
199
+ "string",
200
+ "text",
201
+ this.calculateCombinedWindow(todayDeliveries)
202
+ )
203
+ ]);
204
+ }
205
+ /**
206
+ * Remove deliveries that are no longer active.
207
+ *
208
+ * @param activeIds List of currently active package IDs
209
+ */
210
+ async cleanupDeliveries(activeIds) {
211
+ const activeSet = new Set(activeIds.map((id) => `deliveries.${id}`));
212
+ const objects = await this.adapter.getObjectViewAsync("system", "device", {
213
+ startkey: `${this.adapter.namespace}.deliveries.`,
214
+ endkey: `${this.adapter.namespace}.deliveries.\u9999`
215
+ });
216
+ for (const row of objects.rows) {
217
+ const relativeId = row.id.replace(`${this.adapter.namespace}.`, "");
218
+ if (relativeId.startsWith("deliveries.") && !activeSet.has(relativeId)) {
219
+ await this.adapter.delObjectAsync(relativeId, { recursive: true });
220
+ this.adapter.log.debug(`Removed stale delivery: ${relativeId}`);
221
+ }
222
+ }
223
+ }
224
+ /**
225
+ * Calculate delivery time window — only from Unix timestamps.
226
+ *
227
+ * @param delivery The delivery data
228
+ * @param statusCode Pre-parsed status code
229
+ */
230
+ calculateDeliveryWindow(delivery, statusCode) {
231
+ if (!TRACKABLE_STATUSES.has(statusCode)) {
232
+ return "";
233
+ }
234
+ const formatTime = (timestamp) => {
235
+ if (!timestamp) {
236
+ return null;
237
+ }
238
+ return new Date(timestamp * 1e3).toLocaleTimeString("de-DE", {
239
+ hour: "2-digit",
240
+ minute: "2-digit"
241
+ });
242
+ };
243
+ const start = formatTime(delivery.timestamp_expected);
244
+ const end = formatTime(delivery.timestamp_expected_end);
245
+ if (!start) {
246
+ return "";
247
+ }
248
+ return end ? `${start} - ${end}` : start;
249
+ }
250
+ /**
251
+ * Calculate human-readable delivery estimate.
252
+ *
253
+ * @param delivery The delivery data
254
+ * @param statusCode Pre-parsed status code
255
+ */
256
+ calculateDeliveryEstimate(delivery, statusCode) {
257
+ if (!TRACKABLE_STATUSES.has(statusCode)) {
258
+ return "";
259
+ }
260
+ let expectedDate = null;
261
+ if (delivery.timestamp_expected) {
262
+ expectedDate = new Date(delivery.timestamp_expected * 1e3);
263
+ } else if (delivery.date_expected) {
264
+ expectedDate = new Date(delivery.date_expected);
265
+ }
266
+ if (!expectedDate || isNaN(expectedDate.getTime())) {
267
+ return "";
27
268
  }
28
- /**
29
- * Sanitize a string for use as ioBroker object ID.
30
- *
31
- * @param name Raw string to sanitize
32
- */
33
- sanitize(name) {
34
- return (name
35
- .toLowerCase()
36
- .replace(/[^a-z0-9]+/g, "_")
37
- .replace(/^_+|_+$/g, "")
38
- .slice(0, 50) || "unknown");
269
+ const now = /* @__PURE__ */ new Date();
270
+ const todayStart = new Date(
271
+ now.getFullYear(),
272
+ now.getMonth(),
273
+ now.getDate()
274
+ );
275
+ const expectedStart = new Date(
276
+ expectedDate.getFullYear(),
277
+ expectedDate.getMonth(),
278
+ expectedDate.getDate()
279
+ );
280
+ const diffDays = Math.round(
281
+ (expectedStart.getTime() - todayStart.getTime()) / (1e3 * 60 * 60 * 24)
282
+ );
283
+ const lang = this.adapter.config.language || "de";
284
+ const l = ESTIMATE_LABELS[lang] || ESTIMATE_LABELS.en;
285
+ if (diffDays < 0) {
286
+ return l.overdue;
39
287
  }
40
- /**
41
- * Build a unique package ID from a delivery.
42
- *
43
- * @param delivery The delivery to build an ID for
44
- */
45
- packageId(delivery) {
46
- let id = this.sanitize(delivery.tracking_number);
47
- if (delivery.extra_information) {
48
- id += `_${this.sanitize(delivery.extra_information)}`;
49
- }
50
- return id;
288
+ if (diffDays === 0) {
289
+ return l.today;
51
290
  }
52
- /**
53
- * Update or create all states for a delivery.
54
- *
55
- * @param delivery The delivery data from API
56
- * @param carrierName Resolved carrier display name
57
- */
58
- async updateDelivery(delivery, carrierName) {
59
- const pkgId = this.packageId(delivery);
60
- const devicePath = `deliveries.${pkgId}`;
61
- await this.adapter.extendObjectAsync(devicePath, {
62
- type: "device",
63
- common: {
64
- name: delivery.description || `Package ${delivery.tracking_number}`,
65
- },
66
- native: {},
67
- });
68
- const statusCode = parseInt(delivery.status_code, 10) || 0;
69
- const lang = this.adapter.config.language || "de";
70
- const labels = lang === "de" ? types_1.STATUS_LABELS_DE : types_1.STATUS_LABELS_EN;
71
- await Promise.all([
72
- this.createAndSet(`${devicePath}.carrier`, "Carrier", "string", "text", carrierName),
73
- this.createAndSet(`${devicePath}.status`, "Status", "string", "text", labels[statusCode] || `Unknown (${statusCode})`),
74
- this.createAndSet(`${devicePath}.statusCode`, "Status Code", "number", "value", statusCode),
75
- this.createAndSet(`${devicePath}.description`, "Description", "string", "text", delivery.description || ""),
76
- this.createAndSet(`${devicePath}.trackingNumber`, "Tracking Number", "string", "text", delivery.tracking_number),
77
- this.createAndSet(`${devicePath}.extraInfo`, "Extra Information", "string", "text", delivery.extra_information || ""),
78
- this.createAndSet(`${devicePath}.deliveryWindow`, "Delivery Window", "string", "text", this.calculateDeliveryWindow(delivery, statusCode)),
79
- this.createAndSet(`${devicePath}.deliveryEstimate`, "Delivery Estimate", "string", "text", this.calculateDeliveryEstimate(delivery, statusCode)),
80
- this.createAndSet(`${devicePath}.lastEvent`, "Last Event", "string", "text", this.formatLastEvent(delivery)),
81
- this.createAndSet(`${devicePath}.lastLocation`, "Last Location", "string", "text", this.extractLastLocation(delivery)),
82
- this.createAndSet(`${devicePath}.lastUpdated`, "Last Updated", "string", "date", new Date().toISOString()),
83
- ]);
291
+ if (diffDays === 1) {
292
+ return l.tomorrow;
84
293
  }
85
- /**
86
- * Update summary states. Expects already-filtered active deliveries.
87
- *
88
- * @param activeDeliveries Only active (non-delivered) deliveries
89
- */
90
- async updateSummary(activeDeliveries) {
91
- await this.adapter.extendObjectAsync("summary", {
92
- type: "channel",
93
- common: { name: "Summary" },
94
- native: {},
95
- });
96
- const todayDeliveries = activeDeliveries.filter((d) => {
97
- const statusCode = parseInt(d.status_code, 10) || 0;
98
- const estimate = this.calculateDeliveryEstimate(d, statusCode);
99
- return estimate === "heute" || estimate === "today";
100
- });
101
- await Promise.all([
102
- this.createAndSet("summary.activeCount", "Active Deliveries", "number", "value", activeDeliveries.length),
103
- this.createAndSet("summary.todayCount", "Deliveries Today", "number", "value", todayDeliveries.length),
104
- this.createAndSet("summary.deliveryWindow", "Combined Delivery Window", "string", "text", this.calculateCombinedWindow(todayDeliveries)),
105
- ]);
294
+ return l.days.replace("%d", String(diffDays));
295
+ }
296
+ /**
297
+ * Format the latest tracking event.
298
+ *
299
+ * @param delivery The delivery data
300
+ */
301
+ formatLastEvent(delivery) {
302
+ if (!delivery.events || delivery.events.length === 0) {
303
+ return "";
106
304
  }
107
- /**
108
- * Remove deliveries that are no longer active.
109
- *
110
- * @param activeIds List of currently active package IDs
111
- */
112
- async cleanupDeliveries(activeIds) {
113
- const activeSet = new Set(activeIds.map((id) => `deliveries.${id}`));
114
- const objects = await this.adapter.getObjectViewAsync("system", "device", {
115
- startkey: `${this.adapter.namespace}.deliveries.`,
116
- endkey: `${this.adapter.namespace}.deliveries.\u9999`,
117
- });
118
- for (const row of objects.rows) {
119
- const relativeId = row.id.replace(`${this.adapter.namespace}.`, "");
120
- if (relativeId.startsWith("deliveries.") && !activeSet.has(relativeId)) {
121
- await this.adapter.delObjectAsync(relativeId, { recursive: true });
122
- this.adapter.log.debug(`Removed stale delivery: ${relativeId}`);
123
- }
124
- }
305
+ const latest = delivery.events[0];
306
+ const parts = [];
307
+ if (latest.event) {
308
+ parts.push(latest.event);
125
309
  }
126
- /**
127
- * Calculate delivery time window — only from Unix timestamps.
128
- *
129
- * @param delivery The delivery data
130
- * @param statusCode Pre-parsed status code
131
- */
132
- calculateDeliveryWindow(delivery, statusCode) {
133
- if (!TRACKABLE_STATUSES.has(statusCode)) {
134
- return "";
135
- }
136
- const formatTime = (timestamp) => {
137
- if (!timestamp) {
138
- return null;
139
- }
140
- return new Date(timestamp * 1000).toLocaleTimeString("de-DE", {
141
- hour: "2-digit",
142
- minute: "2-digit",
143
- });
144
- };
145
- const start = formatTime(delivery.timestamp_expected);
146
- const end = formatTime(delivery.timestamp_expected_end);
147
- if (!start) {
148
- return "";
149
- }
150
- return end ? `${start} - ${end}` : start;
310
+ if (latest.date) {
311
+ parts.push(latest.date);
151
312
  }
152
- /**
153
- * Calculate human-readable delivery estimate.
154
- *
155
- * @param delivery The delivery data
156
- * @param statusCode Pre-parsed status code
157
- */
158
- calculateDeliveryEstimate(delivery, statusCode) {
159
- if (!TRACKABLE_STATUSES.has(statusCode)) {
160
- return "";
161
- }
162
- let expectedDate = null;
163
- if (delivery.timestamp_expected) {
164
- expectedDate = new Date(delivery.timestamp_expected * 1000);
165
- }
166
- else if (delivery.date_expected) {
167
- expectedDate = new Date(delivery.date_expected);
168
- }
169
- if (!expectedDate || isNaN(expectedDate.getTime())) {
170
- return "";
171
- }
172
- const now = new Date();
173
- const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
174
- const expectedStart = new Date(expectedDate.getFullYear(), expectedDate.getMonth(), expectedDate.getDate());
175
- const diffDays = Math.round((expectedStart.getTime() - todayStart.getTime()) / (1000 * 60 * 60 * 24));
176
- const lang = this.adapter.config.language || "de";
177
- const l = ESTIMATE_LABELS[lang] || ESTIMATE_LABELS.en;
178
- if (diffDays < 0) {
179
- return l.overdue;
180
- }
181
- if (diffDays === 0) {
182
- return l.today;
183
- }
184
- if (diffDays === 1) {
185
- return l.tomorrow;
186
- }
187
- return l.days.replace("%d", String(diffDays));
313
+ return parts.join(" - ");
314
+ }
315
+ /**
316
+ * Extract location from latest event.
317
+ *
318
+ * @param delivery The delivery data
319
+ */
320
+ extractLastLocation(delivery) {
321
+ if (!delivery.events || delivery.events.length === 0) {
322
+ return "";
188
323
  }
189
- /**
190
- * Format the latest tracking event.
191
- *
192
- * @param delivery The delivery data
193
- */
194
- formatLastEvent(delivery) {
195
- if (!delivery.events || delivery.events.length === 0) {
196
- return "";
197
- }
198
- const latest = delivery.events[0];
199
- const parts = [];
200
- if (latest.event) {
201
- parts.push(latest.event);
202
- }
203
- if (latest.date) {
204
- parts.push(latest.date);
205
- }
206
- return parts.join(" - ");
324
+ return delivery.events[0].location || "";
325
+ }
326
+ /**
327
+ * Calculate combined delivery window for today's packages.
328
+ *
329
+ * @param todayDeliveries Deliveries expected today
330
+ */
331
+ calculateCombinedWindow(todayDeliveries) {
332
+ const windows = todayDeliveries.map((d) => {
333
+ const sc = parseInt(d.status_code, 10) || 0;
334
+ return this.calculateDeliveryWindow(d, sc);
335
+ }).filter((w) => w.length > 0);
336
+ if (windows.length === 0) {
337
+ return "";
207
338
  }
208
- /**
209
- * Extract location from latest event.
210
- *
211
- * @param delivery The delivery data
212
- */
213
- extractLastLocation(delivery) {
214
- if (!delivery.events || delivery.events.length === 0) {
215
- return "";
216
- }
217
- return delivery.events[0].location || "";
339
+ if (windows.length === 1) {
340
+ return windows[0];
218
341
  }
219
- /**
220
- * Calculate combined delivery window for today's packages.
221
- *
222
- * @param todayDeliveries Deliveries expected today
223
- */
224
- calculateCombinedWindow(todayDeliveries) {
225
- const windows = todayDeliveries
226
- .map((d) => {
227
- const sc = parseInt(d.status_code, 10) || 0;
228
- return this.calculateDeliveryWindow(d, sc);
229
- })
230
- .filter((w) => w.length > 0);
231
- if (windows.length === 0) {
232
- return "";
233
- }
234
- if (windows.length === 1) {
235
- return windows[0];
236
- }
237
- const times = [];
238
- for (const w of windows) {
239
- const match = w.match(/(\d{2}:\d{2})(?:\s*-\s*(\d{2}:\d{2}))?/);
240
- if (match) {
241
- times.push({ start: match[1], end: match[2] || match[1] });
242
- }
243
- }
244
- if (times.length === 0) {
245
- return "";
246
- }
247
- times.sort((a, b) => a.start.localeCompare(b.start));
248
- return `${times[0].start} - ${times[times.length - 1].end}`;
342
+ const times = [];
343
+ for (const w of windows) {
344
+ const match = w.match(/(\d{2}:\d{2})(?:\s*-\s*(\d{2}:\d{2}))?/);
345
+ if (match) {
346
+ times.push({ start: match[1], end: match[2] || match[1] });
347
+ }
249
348
  }
250
- /**
251
- * Create/extend a read-only state and set its value.
252
- *
253
- * @param id State ID relative to adapter namespace
254
- * @param name Display name
255
- * @param type Value type
256
- * @param role ioBroker role
257
- * @param val Value to set
258
- */
259
- async createAndSet(id, name, type, role, val) {
260
- await this.adapter.extendObjectAsync(id, {
261
- type: "state",
262
- common: { name, type, role, read: true, write: false },
263
- native: {},
264
- });
265
- await this.adapter.setStateAsync(id, { val, ack: true });
349
+ if (times.length === 0) {
350
+ return "";
266
351
  }
352
+ times.sort((a, b) => a.start.localeCompare(b.start));
353
+ return `${times[0].start} - ${times[times.length - 1].end}`;
354
+ }
355
+ /**
356
+ * Create/extend a read-only state and set its value.
357
+ *
358
+ * @param id State ID relative to adapter namespace
359
+ * @param name Display name
360
+ * @param type Value type
361
+ * @param role ioBroker role
362
+ * @param val Value to set
363
+ */
364
+ async createAndSet(id, name, type, role, val) {
365
+ await this.adapter.extendObjectAsync(id, {
366
+ type: "state",
367
+ common: { name, type, role, read: true, write: false },
368
+ native: {}
369
+ });
370
+ await this.adapter.setStateAsync(id, { val, ack: true });
371
+ }
267
372
  }
268
- exports.StateManager = StateManager;
373
+ // Annotate the CommonJS export names for ESM import in node:
374
+ 0 && (module.exports = {
375
+ StateManager
376
+ });
377
+ //# sourceMappingURL=state-manager.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/state-manager.ts"],
4
+ "sourcesContent": ["import type { AdapterInstance } from \"@iobroker/adapter-core\";\nimport type { ParcelDelivery } from \"./types\";\nimport { STATUS_LABELS_DE, STATUS_LABELS_EN } from \"./types\";\n\n/** Status codes that have expected delivery date/time */\nconst TRACKABLE_STATUSES = new Set([2, 4, 8]);\n\nconst ESTIMATE_LABELS: Record<string, Record<string, string>> = {\n de: {\n overdue: \"\u00FCberf\u00E4llig\",\n today: \"heute\",\n tomorrow: \"morgen\",\n days: \"in %d Tagen\",\n },\n en: {\n overdue: \"overdue\",\n today: \"today\",\n tomorrow: \"tomorrow\",\n days: \"in %d days\",\n },\n};\n\n/** Manages ioBroker states for parcel deliveries */\nexport class StateManager {\n private adapter: AdapterInstance;\n\n /** @param adapter The ioBroker adapter instance */\n constructor(adapter: AdapterInstance) {\n this.adapter = adapter;\n }\n\n /**\n * Sanitize a string for use as ioBroker object ID.\n *\n * @param name Raw string to sanitize\n */\n sanitize(name: string): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 50) || \"unknown\"\n );\n }\n\n /**\n * Build a unique package ID from a delivery.\n *\n * @param delivery The delivery to build an ID for\n */\n packageId(delivery: ParcelDelivery): string {\n let id = this.sanitize(delivery.tracking_number);\n if (delivery.extra_information) {\n id += `_${this.sanitize(delivery.extra_information)}`;\n }\n return id;\n }\n\n /**\n * Update or create all states for a delivery.\n *\n * @param delivery The delivery data from API\n * @param carrierName Resolved carrier display name\n */\n async updateDelivery(\n delivery: ParcelDelivery,\n carrierName: string,\n ): Promise<void> {\n const pkgId = this.packageId(delivery);\n const devicePath = `deliveries.${pkgId}`;\n\n await this.adapter.extendObjectAsync(devicePath, {\n type: \"device\",\n common: {\n name: delivery.description || `Package ${delivery.tracking_number}`,\n },\n native: {},\n });\n\n const statusCode = parseInt(delivery.status_code, 10) || 0;\n const lang = this.adapter.config.language || \"de\";\n const labels = lang === \"de\" ? STATUS_LABELS_DE : STATUS_LABELS_EN;\n\n await Promise.all([\n this.createAndSet(\n `${devicePath}.carrier`,\n \"Carrier\",\n \"string\",\n \"text\",\n carrierName,\n ),\n this.createAndSet(\n `${devicePath}.status`,\n \"Status\",\n \"string\",\n \"text\",\n labels[statusCode] || `Unknown (${statusCode})`,\n ),\n this.createAndSet(\n `${devicePath}.statusCode`,\n \"Status Code\",\n \"number\",\n \"value\",\n statusCode,\n ),\n this.createAndSet(\n `${devicePath}.description`,\n \"Description\",\n \"string\",\n \"text\",\n delivery.description || \"\",\n ),\n this.createAndSet(\n `${devicePath}.trackingNumber`,\n \"Tracking Number\",\n \"string\",\n \"text\",\n delivery.tracking_number,\n ),\n this.createAndSet(\n `${devicePath}.extraInfo`,\n \"Extra Information\",\n \"string\",\n \"text\",\n delivery.extra_information || \"\",\n ),\n this.createAndSet(\n `${devicePath}.deliveryWindow`,\n \"Delivery Window\",\n \"string\",\n \"text\",\n this.calculateDeliveryWindow(delivery, statusCode),\n ),\n this.createAndSet(\n `${devicePath}.deliveryEstimate`,\n \"Delivery Estimate\",\n \"string\",\n \"text\",\n this.calculateDeliveryEstimate(delivery, statusCode),\n ),\n this.createAndSet(\n `${devicePath}.lastEvent`,\n \"Last Event\",\n \"string\",\n \"text\",\n this.formatLastEvent(delivery),\n ),\n this.createAndSet(\n `${devicePath}.lastLocation`,\n \"Last Location\",\n \"string\",\n \"text\",\n this.extractLastLocation(delivery),\n ),\n this.createAndSet(\n `${devicePath}.lastUpdated`,\n \"Last Updated\",\n \"string\",\n \"date\",\n new Date().toISOString(),\n ),\n ]);\n }\n\n /**\n * Update summary states. Expects already-filtered active deliveries.\n *\n * @param activeDeliveries Only active (non-delivered) deliveries\n */\n async updateSummary(activeDeliveries: ParcelDelivery[]): Promise<void> {\n await this.adapter.extendObjectAsync(\"summary\", {\n type: \"channel\",\n common: { name: \"Summary\" },\n native: {},\n });\n\n const todayDeliveries = activeDeliveries.filter((d) => {\n const statusCode = parseInt(d.status_code, 10) || 0;\n const estimate = this.calculateDeliveryEstimate(d, statusCode);\n return estimate === \"heute\" || estimate === \"today\";\n });\n\n await Promise.all([\n this.createAndSet(\n \"summary.activeCount\",\n \"Active Deliveries\",\n \"number\",\n \"value\",\n activeDeliveries.length,\n ),\n this.createAndSet(\n \"summary.todayCount\",\n \"Deliveries Today\",\n \"number\",\n \"value\",\n todayDeliveries.length,\n ),\n this.createAndSet(\n \"summary.deliveryWindow\",\n \"Combined Delivery Window\",\n \"string\",\n \"text\",\n this.calculateCombinedWindow(todayDeliveries),\n ),\n ]);\n }\n\n /**\n * Remove deliveries that are no longer active.\n *\n * @param activeIds List of currently active package IDs\n */\n async cleanupDeliveries(activeIds: string[]): Promise<void> {\n const activeSet = new Set(activeIds.map((id) => `deliveries.${id}`));\n\n const objects = await this.adapter.getObjectViewAsync(\"system\", \"device\", {\n startkey: `${this.adapter.namespace}.deliveries.`,\n endkey: `${this.adapter.namespace}.deliveries.\\u9999`,\n });\n\n for (const row of objects.rows) {\n const relativeId = row.id.replace(`${this.adapter.namespace}.`, \"\");\n if (relativeId.startsWith(\"deliveries.\") && !activeSet.has(relativeId)) {\n await this.adapter.delObjectAsync(relativeId, { recursive: true });\n this.adapter.log.debug(`Removed stale delivery: ${relativeId}`);\n }\n }\n }\n\n /**\n * Calculate delivery time window \u2014 only from Unix timestamps.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n */\n private calculateDeliveryWindow(\n delivery: ParcelDelivery,\n statusCode: number,\n ): string {\n if (!TRACKABLE_STATUSES.has(statusCode)) {\n return \"\";\n }\n\n const formatTime = (timestamp?: number): string | null => {\n if (!timestamp) {\n return null;\n }\n return new Date(timestamp * 1000).toLocaleTimeString(\"de-DE\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n };\n\n const start = formatTime(delivery.timestamp_expected);\n const end = formatTime(delivery.timestamp_expected_end);\n\n if (!start) {\n return \"\";\n }\n return end ? `${start} - ${end}` : start;\n }\n\n /**\n * Calculate human-readable delivery estimate.\n *\n * @param delivery The delivery data\n * @param statusCode Pre-parsed status code\n */\n private calculateDeliveryEstimate(\n delivery: ParcelDelivery,\n statusCode: number,\n ): string {\n if (!TRACKABLE_STATUSES.has(statusCode)) {\n return \"\";\n }\n\n let expectedDate: Date | null = null;\n if (delivery.timestamp_expected) {\n expectedDate = new Date(delivery.timestamp_expected * 1000);\n } else if (delivery.date_expected) {\n expectedDate = new Date(delivery.date_expected);\n }\n\n if (!expectedDate || isNaN(expectedDate.getTime())) {\n return \"\";\n }\n\n const now = new Date();\n const todayStart = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n );\n const expectedStart = new Date(\n expectedDate.getFullYear(),\n expectedDate.getMonth(),\n expectedDate.getDate(),\n );\n const diffDays = Math.round(\n (expectedStart.getTime() - todayStart.getTime()) / (1000 * 60 * 60 * 24),\n );\n\n const lang = this.adapter.config.language || \"de\";\n const l = ESTIMATE_LABELS[lang] || ESTIMATE_LABELS.en;\n\n if (diffDays < 0) {\n return l.overdue;\n }\n if (diffDays === 0) {\n return l.today;\n }\n if (diffDays === 1) {\n return l.tomorrow;\n }\n return l.days.replace(\"%d\", String(diffDays));\n }\n\n /**\n * Format the latest tracking event.\n *\n * @param delivery The delivery data\n */\n private formatLastEvent(delivery: ParcelDelivery): string {\n if (!delivery.events || delivery.events.length === 0) {\n return \"\";\n }\n const latest = delivery.events[0];\n const parts: string[] = [];\n if (latest.event) {\n parts.push(latest.event);\n }\n if (latest.date) {\n parts.push(latest.date);\n }\n return parts.join(\" - \");\n }\n\n /**\n * Extract location from latest event.\n *\n * @param delivery The delivery data\n */\n private extractLastLocation(delivery: ParcelDelivery): string {\n if (!delivery.events || delivery.events.length === 0) {\n return \"\";\n }\n return delivery.events[0].location || \"\";\n }\n\n /**\n * Calculate combined delivery window for today's packages.\n *\n * @param todayDeliveries Deliveries expected today\n */\n private calculateCombinedWindow(todayDeliveries: ParcelDelivery[]): string {\n const windows = todayDeliveries\n .map((d) => {\n const sc = parseInt(d.status_code, 10) || 0;\n return this.calculateDeliveryWindow(d, sc);\n })\n .filter((w) => w.length > 0);\n\n if (windows.length === 0) {\n return \"\";\n }\n if (windows.length === 1) {\n return windows[0];\n }\n\n const times: {\n /** Window start */ start: string;\n /** Window end */ end: string;\n }[] = [];\n for (const w of windows) {\n const match = w.match(/(\\d{2}:\\d{2})(?:\\s*-\\s*(\\d{2}:\\d{2}))?/);\n if (match) {\n times.push({ start: match[1], end: match[2] || match[1] });\n }\n }\n\n if (times.length === 0) {\n return \"\";\n }\n\n times.sort((a, b) => a.start.localeCompare(b.start));\n return `${times[0].start} - ${times[times.length - 1].end}`;\n }\n\n /**\n * Create/extend a read-only state and set its value.\n *\n * @param id State ID relative to adapter namespace\n * @param name Display name\n * @param type Value type\n * @param role ioBroker role\n * @param val Value to set\n */\n private async createAndSet(\n id: string,\n name: string,\n type: ioBroker.CommonType,\n role: string,\n val: ioBroker.StateValue,\n ): Promise<void> {\n await this.adapter.extendObjectAsync(id, {\n type: \"state\",\n common: { name, type, role, read: true, write: false },\n native: {},\n });\n await this.adapter.setStateAsync(id, { val, ack: true });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }