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.
- package/README.md +6 -6
- package/build/lib/parcel-client.js +175 -166
- package/build/lib/parcel-client.js.map +7 -0
- package/build/lib/state-manager.js +360 -251
- package/build/lib/state-manager.js.map +7 -0
- package/build/lib/types.js +49 -24
- package/build/lib/types.js.map +7 -0
- package/build/main.js +236 -242
- package/build/main.js.map +7 -0
- package/io-package.json +27 -27
- package/package.json +10 -9
- package/build/lib/parcel-client.d.ts +0 -43
- package/build/lib/parcel-client.d.ts.map +0 -1
- package/build/lib/state-manager.d.ts +0 -82
- package/build/lib/state-manager.d.ts.map +0 -1
- package/build/lib/types.d.ts +0 -86
- package/build/lib/types.d.ts.map +0 -1
- package/build/main.d.ts +0 -2
- package/build/main.d.ts.map +0 -1
|
@@ -1,268 +1,377 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|