@camstack/addon-provider-bosch 0.1.35 → 0.1.37
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/CHANGELOG.md +12 -0
- package/dist/{account-integration-offline-De_H_EsP.js → account-integration-offline-VItxeiJg.js} +2 -2
- package/dist/{account-integration-offline-Bt3N9Zdk.mjs → account-integration-offline-nChgZUlC.mjs} +1 -1
- package/dist/{dist-CyZFf49n.js → bosch-instances-BQiyzlPt.mjs} +33936 -31385
- package/dist/{dist-CkMPcvma.mjs → bosch-instances-CO8yIFzJ.js} +34261 -31124
- package/dist/home-connect/addon.js +349 -278
- package/dist/home-connect/addon.mjs +175 -104
- package/dist/homecom/addon.js +247 -117
- package/dist/homecom/addon.mjs +165 -35
- package/dist/shc/addon.js +217 -93
- package/dist/shc/addon.mjs +130 -6
- package/package.json +14 -2
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_bosch_instances = require("../bosch-instances-CO8yIFzJ.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-VItxeiJg.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_tls = require("node:tls");
|
|
6
6
|
let node_zlib = require("node:zlib");
|
|
7
7
|
let node_dns_promises = require("node:dns/promises");
|
|
8
8
|
//#region src/home-connect/custom-actions.ts
|
|
9
|
-
var homeConnectActions =
|
|
10
|
-
beginSignIn:
|
|
11
|
-
attemptId:
|
|
12
|
-
authorizeUrl:
|
|
13
|
-
summary:
|
|
9
|
+
var homeConnectActions = require_bosch_instances.defineCustomActions({
|
|
10
|
+
beginSignIn: require_bosch_instances.customAction(require_bosch_instances.object({}), require_bosch_instances.object({
|
|
11
|
+
attemptId: require_bosch_instances.string().min(1),
|
|
12
|
+
authorizeUrl: require_bosch_instances.string().min(1),
|
|
13
|
+
summary: require_bosch_instances.string()
|
|
14
14
|
}), {
|
|
15
15
|
kind: "mutation",
|
|
16
16
|
auth: "admin"
|
|
17
17
|
}),
|
|
18
|
-
finishSignIn:
|
|
19
|
-
attemptId:
|
|
20
|
-
callback:
|
|
21
|
-
}),
|
|
22
|
-
token:
|
|
23
|
-
refresh:
|
|
24
|
-
accountLabel:
|
|
18
|
+
finishSignIn: require_bosch_instances.customAction(require_bosch_instances.object({
|
|
19
|
+
attemptId: require_bosch_instances.string().min(1),
|
|
20
|
+
callback: require_bosch_instances.string().min(1)
|
|
21
|
+
}), require_bosch_instances.object({
|
|
22
|
+
token: require_bosch_instances.string().min(1).meta({ secret: true }),
|
|
23
|
+
refresh: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
24
|
+
accountLabel: require_bosch_instances.string(),
|
|
25
25
|
/** JSON of the paired appliances — no credential on it. */
|
|
26
|
-
appliances:
|
|
26
|
+
appliances: require_bosch_instances.string().min(1),
|
|
27
27
|
/** JSON of `haId -> { psk, iv64? }`; stored in the keychain, never in `appliances`. */
|
|
28
|
-
applianceSecrets:
|
|
29
|
-
summary:
|
|
28
|
+
applianceSecrets: require_bosch_instances.string().min(1).meta({ secret: true }),
|
|
29
|
+
summary: require_bosch_instances.string()
|
|
30
30
|
}), {
|
|
31
31
|
kind: "mutation",
|
|
32
32
|
auth: "admin"
|
|
@@ -36,21 +36,21 @@ var homeConnectActions = require_dist.defineCustomActions({
|
|
|
36
36
|
* app-scheme redirect and finished the exchange, then the same fields `finishSignIn` returns.
|
|
37
37
|
* Claimed once. A failed exchange answers with its operator-facing message as an error.
|
|
38
38
|
*/
|
|
39
|
-
claimSignIn:
|
|
40
|
-
pending:
|
|
41
|
-
token:
|
|
42
|
-
refresh:
|
|
43
|
-
accountLabel:
|
|
44
|
-
appliances:
|
|
45
|
-
applianceSecrets:
|
|
46
|
-
summary:
|
|
39
|
+
claimSignIn: require_bosch_instances.customAction(require_bosch_instances.object({ attemptId: require_bosch_instances.string().min(1) }), require_bosch_instances.object({
|
|
40
|
+
pending: require_bosch_instances.boolean(),
|
|
41
|
+
token: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
42
|
+
refresh: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
43
|
+
accountLabel: require_bosch_instances.string().optional(),
|
|
44
|
+
appliances: require_bosch_instances.string().optional(),
|
|
45
|
+
applianceSecrets: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
46
|
+
summary: require_bosch_instances.string().optional()
|
|
47
47
|
}), {
|
|
48
48
|
kind: "mutation",
|
|
49
49
|
auth: "admin"
|
|
50
50
|
}),
|
|
51
|
-
signOut:
|
|
52
|
-
signedOut:
|
|
53
|
-
summary:
|
|
51
|
+
signOut: require_bosch_instances.customAction(require_bosch_instances.object({}), require_bosch_instances.object({
|
|
52
|
+
signedOut: require_bosch_instances.literal(true),
|
|
53
|
+
summary: require_bosch_instances.string()
|
|
54
54
|
}), {
|
|
55
55
|
kind: "mutation",
|
|
56
56
|
auth: "admin"
|
|
@@ -73,12 +73,12 @@ async function trackHomeConnectContainers(deps, trigger) {
|
|
|
73
73
|
} catch (err) {
|
|
74
74
|
deps.logger.warn("Home Connect could not list its devices to watch their children", { meta: {
|
|
75
75
|
trigger,
|
|
76
|
-
error:
|
|
76
|
+
error: require_bosch_instances.errMsg(err)
|
|
77
77
|
} });
|
|
78
78
|
return 0;
|
|
79
79
|
}
|
|
80
80
|
for (const d of devices) {
|
|
81
|
-
if (d.type !==
|
|
81
|
+
if (d.type !== require_bosch_instances.DeviceType.Container || deps.reconciler.tracks(d.id)) continue;
|
|
82
82
|
const cfg = await deps.loadConfig(d.id).catch(() => ({}));
|
|
83
83
|
const brokerId = cfg["brokerId"];
|
|
84
84
|
const haId = cfg["haId"];
|
|
@@ -1004,35 +1004,26 @@ async function finishHomeConnectSignIn(input, store, fetchImpl = homeConnectLogi
|
|
|
1004
1004
|
}, fetchImpl);
|
|
1005
1005
|
}
|
|
1006
1006
|
var homeConnectSignInAttempts = require_account_integration_offline.createSignInAttemptStore();
|
|
1007
|
-
var HomeConnectModeSchema =
|
|
1007
|
+
var HomeConnectModeSchema = require_bosch_instances._enum(["AES", "TLS"]);
|
|
1008
1008
|
function isPlainObject$1(value) {
|
|
1009
1009
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1010
1010
|
}
|
|
1011
|
-
var homeConnectDeviceDescriptionSchema =
|
|
1012
|
-
|
|
1013
|
-
host:
|
|
1014
|
-
psk:
|
|
1011
|
+
var homeConnectDeviceDescriptionSchema = require_bosch_instances.union([require_bosch_instances.string(), require_bosch_instances.record(require_bosch_instances.string(), require_bosch_instances.unknown())]).optional();
|
|
1012
|
+
require_bosch_instances.object({
|
|
1013
|
+
host: require_bosch_instances.string().min(1),
|
|
1014
|
+
psk: require_bosch_instances.string().min(1).meta({ secret: true }),
|
|
1015
1015
|
mode: HomeConnectModeSchema.default("AES"),
|
|
1016
|
-
iv64:
|
|
1016
|
+
iv64: require_bosch_instances.string().optional(),
|
|
1017
1017
|
deviceDescription: homeConnectDeviceDescriptionSchema
|
|
1018
1018
|
});
|
|
1019
1019
|
/** Setting key of the keychain-stored credentials of every paired appliance. */
|
|
1020
1020
|
var APPLIANCE_SECRETS_KEY = "applianceSecrets";
|
|
1021
|
-
/**
|
|
1022
|
-
* The integration-settings keys that hold a credential — what a write from this addon names
|
|
1023
|
-
* as `secretKeys` so the hub moves them into the keychain.
|
|
1024
|
-
*/
|
|
1025
|
-
var HOME_CONNECT_SECRET_KEYS = [
|
|
1026
|
-
"token",
|
|
1027
|
-
"refresh",
|
|
1028
|
-
APPLIANCE_SECRETS_KEY
|
|
1029
|
-
];
|
|
1030
1021
|
/** One appliance as persisted in the integration's `appliances` setting: no credential on it. */
|
|
1031
|
-
var homeConnectApplianceRowSchema =
|
|
1032
|
-
haId:
|
|
1033
|
-
name:
|
|
1034
|
-
type:
|
|
1035
|
-
host:
|
|
1022
|
+
var homeConnectApplianceRowSchema = require_bosch_instances.object({
|
|
1023
|
+
haId: require_bosch_instances.string().min(1),
|
|
1024
|
+
name: require_bosch_instances.string(),
|
|
1025
|
+
type: require_bosch_instances.string(),
|
|
1026
|
+
host: require_bosch_instances.string().min(1),
|
|
1036
1027
|
mode: HomeConnectModeSchema,
|
|
1037
1028
|
deviceDescription: homeConnectDeviceDescriptionSchema
|
|
1038
1029
|
});
|
|
@@ -1042,28 +1033,28 @@ var homeConnectApplianceRowSchema = require_dist.object({
|
|
|
1042
1033
|
* Read once, adopted into the keychain, then never written again.
|
|
1043
1034
|
*/
|
|
1044
1035
|
var legacyApplianceRowSchema = homeConnectApplianceRowSchema.extend({
|
|
1045
|
-
psk:
|
|
1046
|
-
iv64:
|
|
1036
|
+
psk: require_bosch_instances.string().optional(),
|
|
1037
|
+
iv64: require_bosch_instances.string().optional()
|
|
1047
1038
|
});
|
|
1048
1039
|
/** What the keychain holds per appliance. */
|
|
1049
|
-
var homeConnectApplianceSecretSchema =
|
|
1050
|
-
psk:
|
|
1051
|
-
iv64:
|
|
1040
|
+
var homeConnectApplianceSecretSchema = require_bosch_instances.object({
|
|
1041
|
+
psk: require_bosch_instances.string().min(1),
|
|
1042
|
+
iv64: require_bosch_instances.string().optional()
|
|
1052
1043
|
});
|
|
1053
1044
|
/** `applianceSecrets`: one string in the keychain, JSON of `haId -> { psk, iv64? }`. */
|
|
1054
|
-
var homeConnectApplianceSecretsSchema =
|
|
1045
|
+
var homeConnectApplianceSecretsSchema = require_bosch_instances.record(require_bosch_instances.string(), homeConnectApplianceSecretSchema);
|
|
1055
1046
|
/** One appliance in memory: its row and its credential together. */
|
|
1056
1047
|
var homeConnectStoredApplianceSchema = homeConnectApplianceRowSchema.extend({
|
|
1057
|
-
psk:
|
|
1058
|
-
iv64:
|
|
1048
|
+
psk: require_bosch_instances.string().min(1),
|
|
1049
|
+
iv64: require_bosch_instances.string().optional()
|
|
1059
1050
|
});
|
|
1060
1051
|
/** The integration is the Home Connect account. Profiles are downloaded once at sign-in. */
|
|
1061
|
-
var homeConnectConfigSchema =
|
|
1062
|
-
token:
|
|
1063
|
-
refresh:
|
|
1064
|
-
appliances:
|
|
1052
|
+
var homeConnectConfigSchema = require_bosch_instances.object({
|
|
1053
|
+
token: require_bosch_instances.string().min(1).describe("Home Connect access token").meta({ secret: true }),
|
|
1054
|
+
refresh: require_bosch_instances.string().optional().describe("Home Connect refresh token").meta({ secret: true }),
|
|
1055
|
+
appliances: require_bosch_instances.array(homeConnectStoredApplianceSchema).optional(),
|
|
1065
1056
|
/** Shown on the sign-in field. The JWT subject, not a credential. */
|
|
1066
|
-
accountLabel:
|
|
1057
|
+
accountLabel: require_bosch_instances.string().optional()
|
|
1067
1058
|
});
|
|
1068
1059
|
function parseJsonValue(raw) {
|
|
1069
1060
|
if (typeof raw !== "string") return raw;
|
|
@@ -1086,7 +1077,7 @@ function legacyRowsOf(raw) {
|
|
|
1086
1077
|
return rows;
|
|
1087
1078
|
}
|
|
1088
1079
|
function readApplianceSecrets(raw) {
|
|
1089
|
-
if (
|
|
1080
|
+
if (require_bosch_instances.isSecretMarker(raw) || require_bosch_instances.isSecretMarkerLike(raw)) return { kind: "unresolved" };
|
|
1090
1081
|
const value = parseJsonValue(raw);
|
|
1091
1082
|
if (value === void 0 || value === null) return { kind: "none" };
|
|
1092
1083
|
const parsed = homeConnectApplianceSecretsSchema.safeParse(value);
|
|
@@ -1128,7 +1119,7 @@ function splitApplianceSecrets(appliances) {
|
|
|
1128
1119
|
};
|
|
1129
1120
|
}
|
|
1130
1121
|
/**
|
|
1131
|
-
* Before
|
|
1122
|
+
* Before the record write: keep objects as-is (serializes as `json`), parse
|
|
1132
1123
|
* textarea JSON strings into objects so they are not re-serialized as `string` and
|
|
1133
1124
|
* corrupted on read by `deserializeSetting(String(object))` — and move any credential
|
|
1134
1125
|
* still sitting on an appliance row into `applianceSecrets`, so a row is never written
|
|
@@ -1185,29 +1176,6 @@ function moveInlineSecrets(settings) {
|
|
|
1185
1176
|
...Object.keys(merged).length > 0 ? { [APPLIANCE_SECRETS_KEY]: JSON.stringify(merged) } : {}
|
|
1186
1177
|
};
|
|
1187
1178
|
}
|
|
1188
|
-
/**
|
|
1189
|
-
* One-time adopt: the settings that must be written so a pairing key found on an appliance
|
|
1190
|
-
* row moves into the keychain. `null` when nothing needs moving. The caller writes it with
|
|
1191
|
-
* {@link HOME_CONNECT_SECRET_KEYS} as `secretKeys`.
|
|
1192
|
-
*/
|
|
1193
|
-
function legacyApplianceSecretPatch(settings) {
|
|
1194
|
-
const moved = moveInlineSecrets({
|
|
1195
|
-
...settings,
|
|
1196
|
-
appliances: parseJsonValue(settings["appliances"])
|
|
1197
|
-
});
|
|
1198
|
-
const rows = moved["appliances"];
|
|
1199
|
-
const secrets = moved[APPLIANCE_SECRETS_KEY];
|
|
1200
|
-
if (!Array.isArray(rows) || typeof secrets !== "string") return null;
|
|
1201
|
-
const parsedRows = [];
|
|
1202
|
-
for (const row of rows) {
|
|
1203
|
-
const parsed = homeConnectApplianceRowSchema.safeParse(row);
|
|
1204
|
-
if (parsed.success) parsedRows.push(parsed.data);
|
|
1205
|
-
}
|
|
1206
|
-
return {
|
|
1207
|
-
appliances: parsedRows,
|
|
1208
|
-
applianceSecrets: secrets
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
1179
|
function connectionFromSettings(settings) {
|
|
1212
1180
|
const normalized = homeConnectSettingsForPersist(settings);
|
|
1213
1181
|
const rows = legacyRowsOf(normalized["appliances"]);
|
|
@@ -1238,7 +1206,7 @@ function hasNonEmptyString(settings, key) {
|
|
|
1238
1206
|
function homeConnectSettingsGap(settings) {
|
|
1239
1207
|
if (connectionFromSettings(settings) !== null) return null;
|
|
1240
1208
|
const token = settings["token"];
|
|
1241
|
-
if (
|
|
1209
|
+
if (require_bosch_instances.isSecretMarker(token) || require_bosch_instances.isSecretMarkerLike(token)) return "token-unresolved-marker";
|
|
1242
1210
|
if (!hasNonEmptyString(settings, "token")) return "token-missing";
|
|
1243
1211
|
return "schema-invalid";
|
|
1244
1212
|
}
|
|
@@ -1250,7 +1218,7 @@ function logHomeConnectSettingsGap(logger, integrationId, settings) {
|
|
|
1250
1218
|
logger.warn("Home Connect integration has incomplete account settings — skipping", { meta: {
|
|
1251
1219
|
integrationId,
|
|
1252
1220
|
gap,
|
|
1253
|
-
tokenIsSecretMarker:
|
|
1221
|
+
tokenIsSecretMarker: require_bosch_instances.isSecretMarker(token),
|
|
1254
1222
|
tokenType: typeof token,
|
|
1255
1223
|
hasToken: hasNonEmptyString(settings, "token"),
|
|
1256
1224
|
hasRefresh: hasNonEmptyString(settings, "refresh")
|
|
@@ -1478,7 +1446,7 @@ function homeConnectDishwasherEntityName(containerName) {
|
|
|
1478
1446
|
return base.length > 0 ? `Dishwasher ${base}` : "Dishwasher";
|
|
1479
1447
|
}
|
|
1480
1448
|
function deviceTypeForApplianceType(type) {
|
|
1481
|
-
if (type.toLowerCase().includes("dishwasher")) return
|
|
1449
|
+
if (type.toLowerCase().includes("dishwasher")) return require_bosch_instances.DeviceType.Container;
|
|
1482
1450
|
return null;
|
|
1483
1451
|
}
|
|
1484
1452
|
/**
|
|
@@ -1491,7 +1459,7 @@ function dishwasherContainerCandidate(app, adoptedId) {
|
|
|
1491
1459
|
const children = [{
|
|
1492
1460
|
childNativeId: `${app.haId}#dishwasher`,
|
|
1493
1461
|
name: homeConnectDishwasherEntityName(name),
|
|
1494
|
-
type:
|
|
1462
|
+
type: require_bosch_instances.DeviceType.Dishwasher,
|
|
1495
1463
|
status: "online",
|
|
1496
1464
|
metadata: {
|
|
1497
1465
|
serialNumber: app.haId,
|
|
@@ -1504,7 +1472,7 @@ function dishwasherContainerCandidate(app, adoptedId) {
|
|
|
1504
1472
|
return {
|
|
1505
1473
|
childNativeId: app.haId,
|
|
1506
1474
|
name,
|
|
1507
|
-
type:
|
|
1475
|
+
type: require_bosch_instances.DeviceType.Container,
|
|
1508
1476
|
status: "online",
|
|
1509
1477
|
metadata: {
|
|
1510
1478
|
serialNumber: app.haId,
|
|
@@ -1580,7 +1548,7 @@ function buildHomeConnectAdoptionProvider(deps) {
|
|
|
1580
1548
|
lastDiscoveryAt: null,
|
|
1581
1549
|
candidateCount: 0,
|
|
1582
1550
|
adoptedCount: 0,
|
|
1583
|
-
lastError:
|
|
1551
|
+
lastError: require_bosch_instances.errMsg(err)
|
|
1584
1552
|
};
|
|
1585
1553
|
}
|
|
1586
1554
|
},
|
|
@@ -1629,7 +1597,7 @@ function buildHomeConnectAdoptionProvider(deps) {
|
|
|
1629
1597
|
logger.warn("home-connect adopt failed", { meta: {
|
|
1630
1598
|
haId,
|
|
1631
1599
|
integrationId,
|
|
1632
|
-
error:
|
|
1600
|
+
error: require_bosch_instances.errMsg(err)
|
|
1633
1601
|
} });
|
|
1634
1602
|
failures++;
|
|
1635
1603
|
}
|
|
@@ -2403,7 +2371,7 @@ var HomeConnectSession = class {
|
|
|
2403
2371
|
this.#logger?.warn("Home Connect frame dropped — undecodable", {
|
|
2404
2372
|
tags: this.#logTags,
|
|
2405
2373
|
meta: {
|
|
2406
|
-
error:
|
|
2374
|
+
error: require_bosch_instances.errMsg(err),
|
|
2407
2375
|
droppedSoFar: n
|
|
2408
2376
|
}
|
|
2409
2377
|
});
|
|
@@ -2411,7 +2379,7 @@ var HomeConnectSession = class {
|
|
|
2411
2379
|
#warn(message, err) {
|
|
2412
2380
|
this.#logger?.warn(`Home Connect ${message}`, {
|
|
2413
2381
|
tags: this.#logTags,
|
|
2414
|
-
meta: { error:
|
|
2382
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
2415
2383
|
});
|
|
2416
2384
|
}
|
|
2417
2385
|
#handleWireClosed() {
|
|
@@ -2523,7 +2491,7 @@ var HomeConnectSession = class {
|
|
|
2523
2491
|
} catch (err) {
|
|
2524
2492
|
clearTimeout(timer);
|
|
2525
2493
|
this.#pending.delete(outgoing.msgID);
|
|
2526
|
-
reject(err instanceof Error ? err : new Error(
|
|
2494
|
+
reject(err instanceof Error ? err : new Error(require_bosch_instances.errMsg(err)));
|
|
2527
2495
|
}
|
|
2528
2496
|
});
|
|
2529
2497
|
}
|
|
@@ -2762,7 +2730,7 @@ function createProtocolHomeConnectClient(input) {
|
|
|
2762
2730
|
meta: {
|
|
2763
2731
|
haId,
|
|
2764
2732
|
attempt,
|
|
2765
|
-
error:
|
|
2733
|
+
error: require_bosch_instances.errMsg(err)
|
|
2766
2734
|
}
|
|
2767
2735
|
});
|
|
2768
2736
|
setLink("disconnected");
|
|
@@ -3171,7 +3139,7 @@ async function enrichHomeConnectConnection(connection, fetchImpl, hooks = {}) {
|
|
|
3171
3139
|
missing: []
|
|
3172
3140
|
};
|
|
3173
3141
|
const token = connection.token;
|
|
3174
|
-
if (
|
|
3142
|
+
if (require_bosch_instances.isSecretMarker(token) || require_bosch_instances.isSecretMarkerLike(token)) return {
|
|
3175
3143
|
connection,
|
|
3176
3144
|
changed: false,
|
|
3177
3145
|
tokenRotated: false,
|
|
@@ -3180,7 +3148,7 @@ async function enrichHomeConnectConnection(connection, fetchImpl, hooks = {}) {
|
|
|
3180
3148
|
const refreshStored = connection.refresh ?? "";
|
|
3181
3149
|
const state = {
|
|
3182
3150
|
token,
|
|
3183
|
-
refresh:
|
|
3151
|
+
refresh: require_bosch_instances.isSecretMarker(refreshStored) || require_bosch_instances.isSecretMarkerLike(refreshStored) ? "" : refreshStored,
|
|
3184
3152
|
rotated: false,
|
|
3185
3153
|
refreshTried: false
|
|
3186
3154
|
};
|
|
@@ -3199,7 +3167,7 @@ async function enrichHomeConnectConnection(connection, fetchImpl, hooks = {}) {
|
|
|
3199
3167
|
} catch (err) {
|
|
3200
3168
|
hooks.onWarn?.("Home Connect IDDF fetch failed", {
|
|
3201
3169
|
haId: row.haId,
|
|
3202
|
-
error:
|
|
3170
|
+
error: require_bosch_instances.errMsg(err)
|
|
3203
3171
|
});
|
|
3204
3172
|
}
|
|
3205
3173
|
if (result === null || !result.ok) {
|
|
@@ -3266,9 +3234,9 @@ async function openStoredOrAccount(connection, deps) {
|
|
|
3266
3234
|
let missing = [];
|
|
3267
3235
|
if ((working.appliances ?? []).length > 0) {
|
|
3268
3236
|
const token = working.token;
|
|
3269
|
-
if (
|
|
3237
|
+
if (require_bosch_instances.isSecretMarker(token) || require_bosch_instances.isSecretMarkerLike(token)) deps.logger?.warn("Home Connect skipping IDDF enrich — access token not resolved yet", {
|
|
3270
3238
|
tags: { integrationId: deps.integrationId },
|
|
3271
|
-
meta: { tokenIsSecretMarker:
|
|
3239
|
+
meta: { tokenIsSecretMarker: require_bosch_instances.isSecretMarker(token) }
|
|
3272
3240
|
});
|
|
3273
3241
|
const enriched = await enrichHomeConnectConnection(working, deps.fetchImpl ?? homeConnectLoginFetch, enrichLogHooks(deps.logger, deps.integrationId));
|
|
3274
3242
|
working = enriched.connection;
|
|
@@ -3396,7 +3364,7 @@ var HomeConnectIntegrationManager = class {
|
|
|
3396
3364
|
this.#refreshStatus();
|
|
3397
3365
|
this.#scheduleIddfRetry();
|
|
3398
3366
|
} catch (err) {
|
|
3399
|
-
this.#error =
|
|
3367
|
+
this.#error = require_bosch_instances.errMsg(err);
|
|
3400
3368
|
this.#lastCheckedAt = Date.now();
|
|
3401
3369
|
this.#refreshStatus();
|
|
3402
3370
|
const appliances = client.getAppliances();
|
|
@@ -3472,7 +3440,7 @@ var HomeConnectIntegrationManager = class {
|
|
|
3472
3440
|
tags: { integrationId: this.#id },
|
|
3473
3441
|
meta: {
|
|
3474
3442
|
haId: row.haId,
|
|
3475
|
-
error:
|
|
3443
|
+
error: require_bosch_instances.errMsg(err)
|
|
3476
3444
|
}
|
|
3477
3445
|
});
|
|
3478
3446
|
});
|
|
@@ -3486,7 +3454,7 @@ var HomeConnectIntegrationManager = class {
|
|
|
3486
3454
|
} catch (err) {
|
|
3487
3455
|
this.#logger.warn("Home Connect IDDF retry failed", {
|
|
3488
3456
|
tags: { integrationId: this.#id },
|
|
3489
|
-
meta: { error:
|
|
3457
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
3490
3458
|
});
|
|
3491
3459
|
}
|
|
3492
3460
|
this.#scheduleIddfRetry();
|
|
@@ -3507,7 +3475,7 @@ var HomeConnectIntegrationManager = class {
|
|
|
3507
3475
|
} catch (err) {
|
|
3508
3476
|
this.#logger.warn("HomeConnectIntegrationManager: client close failed", {
|
|
3509
3477
|
tags: { integrationId: this.#id },
|
|
3510
|
-
meta: { error:
|
|
3478
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
3511
3479
|
});
|
|
3512
3480
|
}
|
|
3513
3481
|
this.#onDisconnected(this.#id);
|
|
@@ -3566,7 +3534,7 @@ var HomeConnectClientRegistry = class {
|
|
|
3566
3534
|
} catch (err) {
|
|
3567
3535
|
this.#logger.warn("HomeConnectClientRegistry: applyConnection failed", { meta: {
|
|
3568
3536
|
integrationId,
|
|
3569
|
-
error:
|
|
3537
|
+
error: require_bosch_instances.errMsg(err)
|
|
3570
3538
|
} });
|
|
3571
3539
|
}
|
|
3572
3540
|
return;
|
|
@@ -3592,7 +3560,7 @@ var HomeConnectClientRegistry = class {
|
|
|
3592
3560
|
} catch (err) {
|
|
3593
3561
|
this.#logger.warn("HomeConnectClientRegistry: manager start failed", { meta: {
|
|
3594
3562
|
integrationId,
|
|
3595
|
-
error:
|
|
3563
|
+
error: require_bosch_instances.errMsg(err)
|
|
3596
3564
|
} });
|
|
3597
3565
|
}
|
|
3598
3566
|
}
|
|
@@ -3605,7 +3573,7 @@ var HomeConnectClientRegistry = class {
|
|
|
3605
3573
|
} catch (err) {
|
|
3606
3574
|
this.#logger.warn("HomeConnectClientRegistry: remove stop failed", { meta: {
|
|
3607
3575
|
integrationId,
|
|
3608
|
-
error:
|
|
3576
|
+
error: require_bosch_instances.errMsg(err)
|
|
3609
3577
|
} });
|
|
3610
3578
|
}
|
|
3611
3579
|
}
|
|
@@ -3621,7 +3589,7 @@ var HomeConnectClientRegistry = class {
|
|
|
3621
3589
|
} catch (err) {
|
|
3622
3590
|
this.#logger.warn("HomeConnectClientRegistry: shutdown stop failed", { meta: {
|
|
3623
3591
|
integrationId: id,
|
|
3624
|
-
error:
|
|
3592
|
+
error: require_bosch_instances.errMsg(err)
|
|
3625
3593
|
} });
|
|
3626
3594
|
}
|
|
3627
3595
|
}));
|
|
@@ -3675,7 +3643,7 @@ function buildHomeConnectConnectionTestProvider(deps) {
|
|
|
3675
3643
|
detail: account.summary
|
|
3676
3644
|
};
|
|
3677
3645
|
} catch (err) {
|
|
3678
|
-
const message =
|
|
3646
|
+
const message = require_bosch_instances.errMsg(err);
|
|
3679
3647
|
if (message.includes("rejected")) return {
|
|
3680
3648
|
outcome: "rejected",
|
|
3681
3649
|
error: "Home Connect refused this account token. Sign in again."
|
|
@@ -3691,33 +3659,23 @@ function buildHomeConnectConnectionTestProvider(deps) {
|
|
|
3691
3659
|
}
|
|
3692
3660
|
//#endregion
|
|
3693
3661
|
//#region src/home-connect/home-connect-connection-persist.ts
|
|
3694
|
-
function locked(value) {
|
|
3695
|
-
return require_dist.isSecretMarker(value) || require_dist.isSecretMarkerLike(value);
|
|
3696
|
-
}
|
|
3697
3662
|
/**
|
|
3698
3663
|
* Persist what a connection learned at runtime — enriched IDDF entity maps and a rotated
|
|
3699
|
-
* access / refresh token — in ONE
|
|
3700
|
-
*
|
|
3701
|
-
*
|
|
3702
|
-
* throws and never logs a value.
|
|
3664
|
+
* access / refresh token — in ONE patch of the record. Writes nothing when the stored secrets
|
|
3665
|
+
* cannot be read right now: the patch would replace a key it cannot see. Never throws and
|
|
3666
|
+
* never logs a value.
|
|
3703
3667
|
*/
|
|
3704
3668
|
function buildHomeConnectConnectionPersister(deps) {
|
|
3705
3669
|
return async (integrationId, connection) => {
|
|
3706
3670
|
if (!connection.appliances || connection.appliances.length === 0) return;
|
|
3707
3671
|
try {
|
|
3708
|
-
const
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
deps.logger.warn("Home Connect not persisting the connection — stored secrets are locked or unreadable", {
|
|
3713
|
-
tags: { integrationId },
|
|
3714
|
-
meta: {
|
|
3715
|
-
applianceSecrets: stored.kind,
|
|
3716
|
-
lockedTokens
|
|
3717
|
-
}
|
|
3718
|
-
});
|
|
3672
|
+
const stored = await deps.read(integrationId);
|
|
3673
|
+
if (stored === null) return;
|
|
3674
|
+
if (stored.locked) {
|
|
3675
|
+
deps.logger.warn("Home Connect not persisting the connection — stored secrets are locked or unreadable", { tags: { integrationId } });
|
|
3719
3676
|
return;
|
|
3720
3677
|
}
|
|
3678
|
+
const settings = stored.settings;
|
|
3721
3679
|
const refresh = connection.refresh ?? "";
|
|
3722
3680
|
const merged = homeConnectSettingsForPersist({
|
|
3723
3681
|
...settings,
|
|
@@ -3725,15 +3683,16 @@ function buildHomeConnectConnectionPersister(deps) {
|
|
|
3725
3683
|
...refresh.length > 0 ? { refresh } : {},
|
|
3726
3684
|
appliances: mergeAppliancesForPersist(settings["appliances"], connection.appliances)
|
|
3727
3685
|
});
|
|
3728
|
-
await deps.
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3686
|
+
await deps.patch(integrationId, {
|
|
3687
|
+
token: merged["token"],
|
|
3688
|
+
...merged["refresh"] !== void 0 ? { refresh: merged["refresh"] } : {},
|
|
3689
|
+
appliances: merged["appliances"],
|
|
3690
|
+
...merged["applianceSecrets"] !== void 0 ? { [APPLIANCE_SECRETS_KEY]: merged[APPLIANCE_SECRETS_KEY] } : {}
|
|
3732
3691
|
});
|
|
3733
3692
|
} catch (err) {
|
|
3734
3693
|
deps.logger.warn("Home Connect failed to persist the connection", {
|
|
3735
3694
|
tags: { integrationId },
|
|
3736
|
-
meta: { error:
|
|
3695
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
3737
3696
|
});
|
|
3738
3697
|
}
|
|
3739
3698
|
};
|
|
@@ -3850,7 +3809,7 @@ var OPERATION_TO_STATE = {
|
|
|
3850
3809
|
Error: "error"
|
|
3851
3810
|
};
|
|
3852
3811
|
function operationStateOf(raw) {
|
|
3853
|
-
const parsed =
|
|
3812
|
+
const parsed = require_bosch_instances.DishwasherOperationStateSchema.safeParse(raw);
|
|
3854
3813
|
return parsed.success ? parsed.data : null;
|
|
3855
3814
|
}
|
|
3856
3815
|
/** The reported state of EVERY listed event the description has — each read on its own. */
|
|
@@ -4108,14 +4067,14 @@ function dishwasherConsumablesFromFeatures(view) {
|
|
|
4108
4067
|
//#endregion
|
|
4109
4068
|
//#region src/home-connect/home-connect-child-bindings.ts
|
|
4110
4069
|
/** The capability a bound child registers. */
|
|
4111
|
-
var HomeConnectChildCapSchema =
|
|
4070
|
+
var HomeConnectChildCapSchema = require_bosch_instances._enum([
|
|
4112
4071
|
"numeric-sensor",
|
|
4113
4072
|
"power-meter",
|
|
4114
4073
|
"enum-sensor",
|
|
4115
4074
|
"binary"
|
|
4116
4075
|
]);
|
|
4117
4076
|
/** How the raw value becomes the slice value. */
|
|
4118
|
-
var HomeConnectChildConvertSchema =
|
|
4077
|
+
var HomeConnectChildConvertSchema = require_bosch_instances._enum([
|
|
4119
4078
|
"none",
|
|
4120
4079
|
"energy-total-kwh",
|
|
4121
4080
|
"event-active"
|
|
@@ -4124,18 +4083,18 @@ var HomeConnectChildConvertSchema = require_dist._enum([
|
|
|
4124
4083
|
* One appliance entity exposed as one child device. Persisted on the container, so a
|
|
4125
4084
|
* child's identity (`stableIdSuffix`) survives restarts and description reloads.
|
|
4126
4085
|
*/
|
|
4127
|
-
var HomeConnectChildBindingSchema =
|
|
4086
|
+
var HomeConnectChildBindingSchema = require_bosch_instances.object({
|
|
4128
4087
|
/** Stable, derived from the feature key — never from a uid, which differs per model. */
|
|
4129
|
-
stableIdSuffix:
|
|
4088
|
+
stableIdSuffix: require_bosch_instances.string().min(1),
|
|
4130
4089
|
/** Feature key the child reads (`BSH.Common.Status.DoorState`, `HomeConnect.Network.rssi`). */
|
|
4131
|
-
key:
|
|
4090
|
+
key: require_bosch_instances.string().min(1),
|
|
4132
4091
|
cap: HomeConnectChildCapSchema,
|
|
4133
4092
|
convert: HomeConnectChildConvertSchema,
|
|
4134
|
-
role:
|
|
4093
|
+
role: require_bosch_instances._enum(require_bosch_instances.DeviceRole),
|
|
4135
4094
|
/** Display name, without the appliance label. */
|
|
4136
|
-
name:
|
|
4095
|
+
name: require_bosch_instances.string().min(1),
|
|
4137
4096
|
/** Unit of an info field the description does not declare (`dBm` for a signal strength). */
|
|
4138
|
-
unit:
|
|
4097
|
+
unit: require_bosch_instances.string().min(1).optional()
|
|
4139
4098
|
});
|
|
4140
4099
|
function slugFeatureKey(key) {
|
|
4141
4100
|
return key.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -4171,10 +4130,10 @@ function valueClassOf(entity, current) {
|
|
|
4171
4130
|
return null;
|
|
4172
4131
|
}
|
|
4173
4132
|
function roleFor(entity, cls, unit) {
|
|
4174
|
-
if (cls === "enum") return
|
|
4175
|
-
if (cls === "binary") return entity.name.endsWith("Connected") ?
|
|
4176
|
-
if (unit === "°C" || unit === "°F") return
|
|
4177
|
-
return
|
|
4133
|
+
if (cls === "enum") return require_bosch_instances.DeviceRole.EnumSensor;
|
|
4134
|
+
if (cls === "binary") return entity.name.endsWith("Connected") ? require_bosch_instances.DeviceRole.ConnectivitySensor : require_bosch_instances.DeviceRole.BinarySensor;
|
|
4135
|
+
if (unit === "°C" || unit === "°F") return require_bosch_instances.DeviceRole.TemperatureSensor;
|
|
4136
|
+
return require_bosch_instances.DeviceRole.NumericSensor;
|
|
4178
4137
|
}
|
|
4179
4138
|
function isCumulativeEnergy(entity) {
|
|
4180
4139
|
return featureShortName(entity.name) === "EnergyConsumption" && entity.name.includes(".Statistic.");
|
|
@@ -4216,7 +4175,7 @@ function bindingForEntity(entity, current) {
|
|
|
4216
4175
|
name: "Energy total",
|
|
4217
4176
|
cap: "power-meter",
|
|
4218
4177
|
convert: "energy-total-kwh",
|
|
4219
|
-
role:
|
|
4178
|
+
role: require_bosch_instances.DeviceRole.EnergySensor
|
|
4220
4179
|
};
|
|
4221
4180
|
return {
|
|
4222
4181
|
...base,
|
|
@@ -4244,7 +4203,7 @@ function bindingForInfo(key, current) {
|
|
|
4244
4203
|
...base,
|
|
4245
4204
|
cap: "numeric-sensor",
|
|
4246
4205
|
convert: "none",
|
|
4247
|
-
role:
|
|
4206
|
+
role: require_bosch_instances.DeviceRole.NumericSensor,
|
|
4248
4207
|
...unit !== void 0 ? { unit } : {}
|
|
4249
4208
|
};
|
|
4250
4209
|
}
|
|
@@ -4252,13 +4211,13 @@ function bindingForInfo(key, current) {
|
|
|
4252
4211
|
...base,
|
|
4253
4212
|
cap: "binary",
|
|
4254
4213
|
convert: "none",
|
|
4255
|
-
role:
|
|
4214
|
+
role: require_bosch_instances.DeviceRole.BinarySensor
|
|
4256
4215
|
};
|
|
4257
4216
|
if (typeof current === "string") return {
|
|
4258
4217
|
...base,
|
|
4259
4218
|
cap: "enum-sensor",
|
|
4260
4219
|
convert: "none",
|
|
4261
|
-
role:
|
|
4220
|
+
role: require_bosch_instances.DeviceRole.EnumSensor
|
|
4262
4221
|
};
|
|
4263
4222
|
return null;
|
|
4264
4223
|
}
|
|
@@ -4323,8 +4282,8 @@ function childSliceFromValue(input) {
|
|
|
4323
4282
|
case "numeric-sensor": {
|
|
4324
4283
|
if (typeof raw !== "number" || !Number.isFinite(raw)) return null;
|
|
4325
4284
|
const unit = input.entity === void 0 ? binding.unit ?? null : resolveUnit(input.entity);
|
|
4326
|
-
const quantity = unit === null ? "none" :
|
|
4327
|
-
const stored = quantity === void 0 || quantity === "device" ? null :
|
|
4285
|
+
const quantity = unit === null ? "none" : require_bosch_instances.quantityFromUnit(unit);
|
|
4286
|
+
const stored = quantity === void 0 || quantity === "device" ? null : require_bosch_instances.ingestReading({
|
|
4328
4287
|
value: raw,
|
|
4329
4288
|
rawUnit: unit ?? void 0,
|
|
4330
4289
|
quantity
|
|
@@ -4343,47 +4302,47 @@ var LEGACY_CHILD_KEYS = {
|
|
|
4343
4302
|
"energy-forecast": {
|
|
4344
4303
|
key: "BSH.Common.Option.EnergyForecast",
|
|
4345
4304
|
cap: "numeric-sensor",
|
|
4346
|
-
role:
|
|
4305
|
+
role: require_bosch_instances.DeviceRole.NumericSensor
|
|
4347
4306
|
},
|
|
4348
4307
|
"water-forecast": {
|
|
4349
4308
|
key: "BSH.Common.Option.WaterForecast",
|
|
4350
4309
|
cap: "numeric-sensor",
|
|
4351
|
-
role:
|
|
4310
|
+
role: require_bosch_instances.DeviceRole.NumericSensor
|
|
4352
4311
|
},
|
|
4353
4312
|
"energy-consumed": {
|
|
4354
4313
|
key: "BSH.Common.Option.EnergyConsumption",
|
|
4355
4314
|
cap: "numeric-sensor",
|
|
4356
|
-
role:
|
|
4315
|
+
role: require_bosch_instances.DeviceRole.NumericSensor
|
|
4357
4316
|
},
|
|
4358
4317
|
"water-consumed": {
|
|
4359
4318
|
key: "BSH.Common.Option.WaterConsumption",
|
|
4360
4319
|
cap: "numeric-sensor",
|
|
4361
|
-
role:
|
|
4320
|
+
role: require_bosch_instances.DeviceRole.NumericSensor
|
|
4362
4321
|
},
|
|
4363
4322
|
"remote-control-level": {
|
|
4364
4323
|
key: "BSH.Common.Setting.RemoteControlLevel",
|
|
4365
4324
|
cap: "enum-sensor",
|
|
4366
|
-
role:
|
|
4325
|
+
role: require_bosch_instances.DeviceRole.EnumSensor
|
|
4367
4326
|
},
|
|
4368
4327
|
"delayed-start": {
|
|
4369
4328
|
key: "BSH.Common.Option.StartInRelative",
|
|
4370
4329
|
cap: "numeric-sensor",
|
|
4371
|
-
role:
|
|
4330
|
+
role: require_bosch_instances.DeviceRole.NumericSensor
|
|
4372
4331
|
},
|
|
4373
4332
|
"interior-light": {
|
|
4374
4333
|
key: "BSH.Common.Setting.AmbientLightEnabled",
|
|
4375
4334
|
cap: "binary",
|
|
4376
|
-
role:
|
|
4335
|
+
role: require_bosch_instances.DeviceRole.BinarySensor
|
|
4377
4336
|
},
|
|
4378
4337
|
connectivity: {
|
|
4379
4338
|
key: "BSH.Common.Status.BackendConnected",
|
|
4380
4339
|
cap: "binary",
|
|
4381
|
-
role:
|
|
4340
|
+
role: require_bosch_instances.DeviceRole.ConnectivitySensor
|
|
4382
4341
|
},
|
|
4383
4342
|
"program-phase": {
|
|
4384
4343
|
key: "Dishcare.Dishwasher.Status.ProgramPhase",
|
|
4385
4344
|
cap: "enum-sensor",
|
|
4386
|
-
role:
|
|
4345
|
+
role: require_bosch_instances.DeviceRole.EnumSensor
|
|
4387
4346
|
}
|
|
4388
4347
|
};
|
|
4389
4348
|
/**
|
|
@@ -4586,7 +4545,7 @@ var HomeConnectChildReconciler = class {
|
|
|
4586
4545
|
} catch (err) {
|
|
4587
4546
|
this.#deps.logger.warn("Home Connect child reconcile failed", {
|
|
4588
4547
|
tags: { deviceId },
|
|
4589
|
-
meta: { error:
|
|
4548
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
4590
4549
|
});
|
|
4591
4550
|
}
|
|
4592
4551
|
}
|
|
@@ -4691,12 +4650,12 @@ function assertProgramOptions(description, program, options) {
|
|
|
4691
4650
|
}
|
|
4692
4651
|
//#endregion
|
|
4693
4652
|
//#region src/home-connect/devices/home-connect-dishwasher-device.ts
|
|
4694
|
-
var homeConnectDishwasherSchema =
|
|
4695
|
-
haId:
|
|
4696
|
-
brokerId:
|
|
4697
|
-
applianceType:
|
|
4698
|
-
system:
|
|
4699
|
-
integrationId:
|
|
4653
|
+
var homeConnectDishwasherSchema = require_bosch_instances.object({
|
|
4654
|
+
haId: require_bosch_instances.string().min(1),
|
|
4655
|
+
brokerId: require_bosch_instances.string().min(1),
|
|
4656
|
+
applianceType: require_bosch_instances.string().optional(),
|
|
4657
|
+
system: require_bosch_instances.literal("home-connect").optional(),
|
|
4658
|
+
integrationId: require_bosch_instances.string().optional()
|
|
4700
4659
|
});
|
|
4701
4660
|
var DISH_CAP = "dishwasher";
|
|
4702
4661
|
var CONSUMABLES_CAP = "consumables";
|
|
@@ -4717,14 +4676,14 @@ var OPTION_NAMES = {
|
|
|
4717
4676
|
intensiveZone: ["IntensivZone", "IntensiveZone"],
|
|
4718
4677
|
varioSpeedPlus: ["VarioSpeedPlus", "VarioSpeed"]
|
|
4719
4678
|
};
|
|
4720
|
-
var HomeConnectDishwasherDevice = class extends
|
|
4679
|
+
var HomeConnectDishwasherDevice = class extends require_bosch_instances.BaseDevice {
|
|
4721
4680
|
features = [];
|
|
4722
4681
|
#haId;
|
|
4723
4682
|
#brokerId;
|
|
4724
4683
|
#unwatch = null;
|
|
4725
4684
|
constructor(ctx) {
|
|
4726
4685
|
const persisted = homeConnectDishwasherSchema.parse(ctx.persistedConfig ?? {});
|
|
4727
|
-
super(ctx, homeConnectDishwasherSchema, { type:
|
|
4686
|
+
super(ctx, homeConnectDishwasherSchema, { type: require_bosch_instances.DeviceType.Dishwasher });
|
|
4728
4687
|
this.#haId = persisted.haId;
|
|
4729
4688
|
this.#brokerId = persisted.brokerId;
|
|
4730
4689
|
this.updateSourceInfo({
|
|
@@ -4738,7 +4697,7 @@ var HomeConnectDishwasherDevice = class extends require_dist.BaseDevice {
|
|
|
4738
4697
|
this.registerConsumablesCap();
|
|
4739
4698
|
this.registerRunningCap();
|
|
4740
4699
|
this.runtimeState.setCapState(DISH_CAP, COLD_DISHWASHER_STATUS);
|
|
4741
|
-
this.runtimeState.setCapState(RUNNING_CAP,
|
|
4700
|
+
this.runtimeState.setCapState(RUNNING_CAP, require_bosch_instances.RUNNING_STATE_COLD);
|
|
4742
4701
|
this.markOnline(false);
|
|
4743
4702
|
this.#unwatch = homeConnectConnections.watch(this.#brokerId, this.#haId, (event) => this.recomputeSlice(event));
|
|
4744
4703
|
this.recomputeSlice({ kind: "client" });
|
|
@@ -4791,7 +4750,7 @@ var HomeConnectDishwasherDevice = class extends require_dist.BaseDevice {
|
|
|
4791
4750
|
this.runtimeState.setCapState(CONSUMABLES_CAP, next);
|
|
4792
4751
|
}
|
|
4793
4752
|
registerConsumablesCap() {
|
|
4794
|
-
this.ctx.registerNativeCap(
|
|
4753
|
+
this.ctx.registerNativeCap(require_bosch_instances.consumablesCapability, {
|
|
4795
4754
|
getStatus: async () => this.runtimeState.getCapState(CONSUMABLES_CAP) ?? CONSUMABLES_EMPTY,
|
|
4796
4755
|
reset: async ({ key }) => {
|
|
4797
4756
|
throw new Error(`"${key}" has no reset on a dishwasher — refilling it is the reset`);
|
|
@@ -4828,13 +4787,13 @@ var HomeConnectDishwasherDevice = class extends require_dist.BaseDevice {
|
|
|
4828
4787
|
} catch (err) {
|
|
4829
4788
|
this.ctx.logger.warn(`Home Connect ${what} failed`, {
|
|
4830
4789
|
tags: { deviceId: this.id },
|
|
4831
|
-
meta: { error:
|
|
4790
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
4832
4791
|
});
|
|
4833
4792
|
throw err;
|
|
4834
4793
|
}
|
|
4835
4794
|
}
|
|
4836
4795
|
registerDishwasherCap() {
|
|
4837
|
-
this.ctx.registerNativeCap(
|
|
4796
|
+
this.ctx.registerNativeCap(require_bosch_instances.dishwasherCapability, {
|
|
4838
4797
|
getStatus: async () => this.runtimeState.getCapState(DISH_CAP) ?? COLD_DISHWASHER_STATUS,
|
|
4839
4798
|
getOptions: async () => dishwasherOptionsFromDescription(this.view()),
|
|
4840
4799
|
start: async ({ program, options }) => {
|
|
@@ -4872,22 +4831,22 @@ var HomeConnectDishwasherDevice = class extends require_dist.BaseDevice {
|
|
|
4872
4831
|
});
|
|
4873
4832
|
}
|
|
4874
4833
|
registerRunningCap() {
|
|
4875
|
-
this.ctx.registerNativeCap(
|
|
4834
|
+
this.ctx.registerNativeCap(require_bosch_instances.runningStateCapability, { getStatus: async () => this.runtimeState.getCapState(RUNNING_CAP) ?? require_bosch_instances.RUNNING_STATE_COLD });
|
|
4876
4835
|
}
|
|
4877
4836
|
publishRunningSlice(slice) {
|
|
4878
4837
|
const fetchedAt = Date.now();
|
|
4879
4838
|
const previous = this.runtimeState.getCapState(RUNNING_CAP);
|
|
4880
|
-
const next =
|
|
4839
|
+
const next = require_bosch_instances.runningFromDishwasherOperation(slice.operationState, slice.program, previous, fetchedAt, slice.remaining);
|
|
4881
4840
|
this.runtimeState.setCapState(RUNNING_CAP, next);
|
|
4882
4841
|
}
|
|
4883
4842
|
};
|
|
4884
4843
|
//#endregion
|
|
4885
4844
|
//#region src/home-connect/devices/home-connect-event-device.ts
|
|
4886
|
-
var homeConnectEventSchema =
|
|
4887
|
-
haId:
|
|
4888
|
-
brokerId:
|
|
4889
|
-
system:
|
|
4890
|
-
integrationId:
|
|
4845
|
+
var homeConnectEventSchema = require_bosch_instances.object({
|
|
4846
|
+
haId: require_bosch_instances.string().min(1),
|
|
4847
|
+
brokerId: require_bosch_instances.string().min(1),
|
|
4848
|
+
system: require_bosch_instances.literal("home-connect").optional(),
|
|
4849
|
+
integrationId: require_bosch_instances.string().optional()
|
|
4891
4850
|
});
|
|
4892
4851
|
var EVENT_CAP = "event-emitter";
|
|
4893
4852
|
/**
|
|
@@ -4898,7 +4857,7 @@ var EVENT_CAP = "event-emitter";
|
|
|
4898
4857
|
* A value seen while the link is still being established (the handshake's initial values)
|
|
4899
4858
|
* is the baseline, not a new event: a reconnect must not announce "program finished" again.
|
|
4900
4859
|
*/
|
|
4901
|
-
var HomeConnectEventDevice = class extends
|
|
4860
|
+
var HomeConnectEventDevice = class extends require_bosch_instances.BaseDevice {
|
|
4902
4861
|
features = [];
|
|
4903
4862
|
#haId;
|
|
4904
4863
|
#brokerId;
|
|
@@ -4908,7 +4867,7 @@ var HomeConnectEventDevice = class extends require_dist.BaseDevice {
|
|
|
4908
4867
|
#unwatch = null;
|
|
4909
4868
|
constructor(ctx) {
|
|
4910
4869
|
const persisted = homeConnectEventSchema.parse(ctx.persistedConfig ?? {});
|
|
4911
|
-
super(ctx, homeConnectEventSchema, { type:
|
|
4870
|
+
super(ctx, homeConnectEventSchema, { type: require_bosch_instances.DeviceType.EventEmitter });
|
|
4912
4871
|
this.#haId = persisted.haId;
|
|
4913
4872
|
this.#brokerId = persisted.brokerId;
|
|
4914
4873
|
this.updateSourceInfo({
|
|
@@ -4977,7 +4936,7 @@ var HomeConnectEventDevice = class extends require_dist.BaseDevice {
|
|
|
4977
4936
|
timestamp: Date.now(),
|
|
4978
4937
|
seq: ++this.#seq
|
|
4979
4938
|
};
|
|
4980
|
-
this.ctx.eventBus.emit(
|
|
4939
|
+
this.ctx.eventBus.emit(require_bosch_instances.createEvent(require_bosch_instances.EventCategory.EventEmitted, {
|
|
4981
4940
|
type: "device",
|
|
4982
4941
|
id: this.stableId,
|
|
4983
4942
|
deviceId: this.id,
|
|
@@ -4986,7 +4945,7 @@ var HomeConnectEventDevice = class extends require_dist.BaseDevice {
|
|
|
4986
4945
|
this.publish(types, previous, fire);
|
|
4987
4946
|
}
|
|
4988
4947
|
registerCap() {
|
|
4989
|
-
this.ctx.registerNativeCap(
|
|
4948
|
+
this.ctx.registerNativeCap(require_bosch_instances.eventEmitterCapability, { getStatus: async () => this.runtimeState.getCapState(EVENT_CAP) ?? {
|
|
4990
4949
|
eventTypes: [],
|
|
4991
4950
|
lastEvent: null,
|
|
4992
4951
|
eventCountSinceStart: 0
|
|
@@ -4995,18 +4954,18 @@ var HomeConnectEventDevice = class extends require_dist.BaseDevice {
|
|
|
4995
4954
|
};
|
|
4996
4955
|
//#endregion
|
|
4997
4956
|
//#region src/home-connect/devices/home-connect-sensor-device.ts
|
|
4998
|
-
var homeConnectSensorSchema =
|
|
4999
|
-
haId:
|
|
5000
|
-
brokerId:
|
|
4957
|
+
var homeConnectSensorSchema = require_bosch_instances.object({
|
|
4958
|
+
haId: require_bosch_instances.string().min(1),
|
|
4959
|
+
brokerId: require_bosch_instances.string().min(1),
|
|
5001
4960
|
/**
|
|
5002
4961
|
* The entity this child reads. Absent on a child persisted by the fixed-catalog build,
|
|
5003
4962
|
* which wrote `sensorKind` instead — see {@link resolveSensorBinding}.
|
|
5004
4963
|
*/
|
|
5005
4964
|
binding: HomeConnectChildBindingSchema.optional(),
|
|
5006
4965
|
/** Fixed-catalog children only: the old kind, which is also their stable suffix. */
|
|
5007
|
-
sensorKind:
|
|
5008
|
-
system:
|
|
5009
|
-
integrationId:
|
|
4966
|
+
sensorKind: require_bosch_instances.string().min(1).optional(),
|
|
4967
|
+
system: require_bosch_instances.literal("home-connect").optional(),
|
|
4968
|
+
integrationId: require_bosch_instances.string().optional()
|
|
5010
4969
|
});
|
|
5011
4970
|
/**
|
|
5012
4971
|
* The binding of a persisted child row. A restored child's config is the row the KERNEL
|
|
@@ -5045,7 +5004,7 @@ var NEVER_BINARY = {
|
|
|
5045
5004
|
* entity it reads is its `binding.key` — one key, never "the first of several that
|
|
5046
5005
|
* happens to be present".
|
|
5047
5006
|
*/
|
|
5048
|
-
var HomeConnectSensorDevice = class extends
|
|
5007
|
+
var HomeConnectSensorDevice = class extends require_bosch_instances.BaseDevice {
|
|
5049
5008
|
features = [];
|
|
5050
5009
|
#haId;
|
|
5051
5010
|
#brokerId;
|
|
@@ -5111,32 +5070,32 @@ var HomeConnectSensorDevice = class extends require_dist.BaseDevice {
|
|
|
5111
5070
|
registerCap() {
|
|
5112
5071
|
switch (this.#binding.cap) {
|
|
5113
5072
|
case "numeric-sensor":
|
|
5114
|
-
this.ctx.registerNativeCap(
|
|
5073
|
+
this.ctx.registerNativeCap(require_bosch_instances.numericSensorCapability, { getStatus: async () => this.runtimeState.getCapState("numeric-sensor") ?? NEVER_NUMERIC });
|
|
5115
5074
|
return;
|
|
5116
5075
|
case "power-meter":
|
|
5117
|
-
this.ctx.registerNativeCap(
|
|
5076
|
+
this.ctx.registerNativeCap(require_bosch_instances.powerMeterCapability, { getStatus: async () => this.runtimeState.getCapState("power-meter") ?? NEVER_POWER });
|
|
5118
5077
|
return;
|
|
5119
5078
|
case "enum-sensor":
|
|
5120
|
-
this.ctx.registerNativeCap(
|
|
5079
|
+
this.ctx.registerNativeCap(require_bosch_instances.enumSensorCapability, { getStatus: async () => this.runtimeState.getCapState("enum-sensor") ?? NEVER_ENUM });
|
|
5121
5080
|
return;
|
|
5122
5081
|
case "binary":
|
|
5123
|
-
this.ctx.registerNativeCap(
|
|
5082
|
+
this.ctx.registerNativeCap(require_bosch_instances.binaryCapability, { getStatus: async () => this.runtimeState.getCapState("binary") ?? NEVER_BINARY });
|
|
5124
5083
|
return;
|
|
5125
5084
|
}
|
|
5126
5085
|
}
|
|
5127
5086
|
};
|
|
5128
5087
|
//#endregion
|
|
5129
5088
|
//#region src/home-connect/devices/home-connect-appliance-device.ts
|
|
5130
|
-
var homeConnectApplianceSchema =
|
|
5131
|
-
haId:
|
|
5132
|
-
brokerId:
|
|
5133
|
-
applianceType:
|
|
5089
|
+
var homeConnectApplianceSchema = require_bosch_instances.object({
|
|
5090
|
+
haId: require_bosch_instances.string().min(1),
|
|
5091
|
+
brokerId: require_bosch_instances.string().min(1),
|
|
5092
|
+
applianceType: require_bosch_instances.string().optional(),
|
|
5134
5093
|
/** One entry per appliance entity exposed as a child, maintained from the appliance's description. */
|
|
5135
|
-
entities:
|
|
5094
|
+
entities: require_bosch_instances.array(require_bosch_instances.unknown()).optional(),
|
|
5136
5095
|
/** Children persisted before the description-derived model; read only to keep them alive. */
|
|
5137
|
-
children:
|
|
5138
|
-
system:
|
|
5139
|
-
integrationId:
|
|
5096
|
+
children: require_bosch_instances.array(require_bosch_instances.unknown()).optional(),
|
|
5097
|
+
system: require_bosch_instances.literal("home-connect").optional(),
|
|
5098
|
+
integrationId: require_bosch_instances.string().optional()
|
|
5140
5099
|
});
|
|
5141
5100
|
/** Device metadata keys the `/ci/info` fields map onto (the conventional `DeviceMetadata` keys). */
|
|
5142
5101
|
var INFO_TO_METADATA = {
|
|
@@ -5151,7 +5110,7 @@ var INFO_TO_METADATA = {
|
|
|
5151
5110
|
mac: "mac"
|
|
5152
5111
|
};
|
|
5153
5112
|
/** One Home Connect appliance as a container: the dishwasher, an event emitter, and one child per entity. */
|
|
5154
|
-
var HomeConnectApplianceDevice = class extends
|
|
5113
|
+
var HomeConnectApplianceDevice = class extends require_bosch_instances.BaseDevice {
|
|
5155
5114
|
features = [];
|
|
5156
5115
|
integrationId;
|
|
5157
5116
|
brokerId;
|
|
@@ -5161,7 +5120,7 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5161
5120
|
#lastInfo = "";
|
|
5162
5121
|
constructor(ctx) {
|
|
5163
5122
|
const cfg = homeConnectApplianceSchema.parse(ctx.persistedConfig ?? {});
|
|
5164
|
-
super(ctx, homeConnectApplianceSchema, { type:
|
|
5123
|
+
super(ctx, homeConnectApplianceSchema, { type: require_bosch_instances.DeviceType.Container });
|
|
5165
5124
|
this.brokerId = cfg.brokerId;
|
|
5166
5125
|
this.haId = cfg.haId;
|
|
5167
5126
|
this.integrationId = cfg.integrationId;
|
|
@@ -5208,7 +5167,7 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5208
5167
|
} catch (err) {
|
|
5209
5168
|
this.ctx.logger.warn("Home Connect could not publish the appliance info", {
|
|
5210
5169
|
tags: { deviceId: this.id },
|
|
5211
|
-
meta: { error:
|
|
5170
|
+
meta: { error: require_bosch_instances.errMsg(err) }
|
|
5212
5171
|
});
|
|
5213
5172
|
}
|
|
5214
5173
|
}
|
|
@@ -5226,7 +5185,7 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5226
5185
|
const out = [{
|
|
5227
5186
|
stableIdSuffix: "dishwasher",
|
|
5228
5187
|
meta: {
|
|
5229
|
-
type:
|
|
5188
|
+
type: require_bosch_instances.DeviceType.Dishwasher,
|
|
5230
5189
|
name: homeConnectDishwasherEntityName(containerName),
|
|
5231
5190
|
linkDeviceId: this.id,
|
|
5232
5191
|
...integration
|
|
@@ -5240,7 +5199,7 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5240
5199
|
if (bindings.some((b) => b.convert === "event-active")) out.push({
|
|
5241
5200
|
stableIdSuffix: "events",
|
|
5242
5201
|
meta: {
|
|
5243
|
-
type:
|
|
5202
|
+
type: require_bosch_instances.DeviceType.EventEmitter,
|
|
5244
5203
|
name: `${containerName} — Events`,
|
|
5245
5204
|
linkDeviceId: this.id,
|
|
5246
5205
|
...integration
|
|
@@ -5251,7 +5210,7 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5251
5210
|
for (const binding of bindings) out.push({
|
|
5252
5211
|
stableIdSuffix: binding.stableIdSuffix,
|
|
5253
5212
|
meta: {
|
|
5254
|
-
type:
|
|
5213
|
+
type: require_bosch_instances.DeviceType.Sensor,
|
|
5255
5214
|
role: binding.role,
|
|
5256
5215
|
name: `${containerName} — ${binding.name}`,
|
|
5257
5216
|
linkDeviceId: this.id,
|
|
@@ -5266,57 +5225,151 @@ var HomeConnectApplianceDevice = class extends require_dist.BaseDevice {
|
|
|
5266
5225
|
return out;
|
|
5267
5226
|
}
|
|
5268
5227
|
};
|
|
5269
|
-
|
|
5270
|
-
|
|
5228
|
+
var homeConnectConnectionsEntryKind = require_bosch_instances.defineEntryKind("connections", {
|
|
5229
|
+
title: "Home Connect accounts",
|
|
5230
|
+
schema: require_bosch_instances.entryBody({
|
|
5231
|
+
token: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
5232
|
+
refresh: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
5233
|
+
/** JSON of `haId -> { psk, iv64? }`: one string in the keychain. */
|
|
5234
|
+
applianceSecrets: require_bosch_instances.string().optional().meta({ secret: true }),
|
|
5235
|
+
appliances: require_bosch_instances.array(homeConnectApplianceRowSchema).optional(),
|
|
5236
|
+
accountLabel: require_bosch_instances.string().optional()
|
|
5237
|
+
}),
|
|
5238
|
+
writeTrigger: "an operator signs in, edits or removes a Home Connect account, or a connection stores what it learned",
|
|
5239
|
+
maxDataBytes: 1024 * 1024
|
|
5240
|
+
});
|
|
5241
|
+
/** Every kind this addon declares, keyed by its name. */
|
|
5242
|
+
var homeConnectRecordKinds = { connections: homeConnectConnectionsEntryKind };
|
|
5243
|
+
var LEGACY_KEYS = [
|
|
5244
|
+
"token",
|
|
5245
|
+
"refresh",
|
|
5246
|
+
APPLIANCE_SECRETS_KEY,
|
|
5247
|
+
"appliances",
|
|
5248
|
+
"accountLabel",
|
|
5249
|
+
"accountEmail",
|
|
5250
|
+
"accountPassword",
|
|
5251
|
+
"authorizeUrl",
|
|
5252
|
+
"verifier",
|
|
5253
|
+
"callback",
|
|
5254
|
+
"state"
|
|
5255
|
+
];
|
|
5256
|
+
var SECRET_KEYS = [
|
|
5257
|
+
"token",
|
|
5258
|
+
"refresh",
|
|
5259
|
+
APPLIANCE_SECRETS_KEY
|
|
5260
|
+
];
|
|
5261
|
+
/** The `appliances` row value as an array: a JSON string is parsed, anything else is as stored. */
|
|
5262
|
+
function parseStoredRows(raw) {
|
|
5263
|
+
if (typeof raw !== "string") return raw;
|
|
5264
|
+
try {
|
|
5265
|
+
return JSON.parse(raw);
|
|
5266
|
+
} catch {
|
|
5267
|
+
return;
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
function text(value) {
|
|
5271
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
5272
|
+
}
|
|
5271
5273
|
/**
|
|
5272
|
-
*
|
|
5273
|
-
*
|
|
5274
|
-
*
|
|
5275
|
-
*
|
|
5276
|
-
* `null` when there is nothing to move — settings already migrated, or the stored keys are
|
|
5277
|
-
* locked (the move could not see what it would replace). Idempotent.
|
|
5274
|
+
* The record body of one account, from its legacy rows (secrets resolved). A pairing key still
|
|
5275
|
+
* sitting on an appliance row (the shape before the keychain move) is carried into
|
|
5276
|
+
* `applianceSecrets` here, once, and never again.
|
|
5278
5277
|
*/
|
|
5279
|
-
function
|
|
5280
|
-
const
|
|
5281
|
-
if (
|
|
5282
|
-
const
|
|
5283
|
-
|
|
5284
|
-
|
|
5278
|
+
function homeConnectBodyFromRows(values, log, integration) {
|
|
5279
|
+
const dropped = Object.keys(values).filter((key) => !LEGACY_KEYS.includes(key));
|
|
5280
|
+
if (dropped.length > 0) log.warn(`integration '${integration.id}': legacy settings nothing reads any more are dropped: ${dropped.join(", ")}`);
|
|
5281
|
+
const normalized = homeConnectSettingsForPersist({ ...values });
|
|
5282
|
+
const storedRows = parseStoredRows(values["appliances"]);
|
|
5283
|
+
if (Array.isArray(storedRows)) for (const row of storedRows) {
|
|
5284
|
+
const parsed = homeConnectApplianceRowSchema.safeParse(row);
|
|
5285
|
+
if (parsed.success) continue;
|
|
5286
|
+
const haId = typeof row === "object" && row !== null && typeof Reflect.get(row, "haId") === "string" ? String(Reflect.get(row, "haId")) : "(no haId)";
|
|
5287
|
+
const paths = parsed.error.issues.map((issue) => issue.path.join(".") || "(row)").join(", ");
|
|
5288
|
+
log.warn(`integration '${integration.id}': appliance row '${haId}' could not be read and is dropped (invalid: ${paths})`);
|
|
5289
|
+
}
|
|
5290
|
+
const appliances = [];
|
|
5291
|
+
const keptRows = normalized["appliances"];
|
|
5292
|
+
if (Array.isArray(keptRows)) for (const row of keptRows) {
|
|
5293
|
+
const parsed = homeConnectApplianceRowSchema.safeParse(row);
|
|
5294
|
+
if (parsed.success) appliances.push(parsed.data);
|
|
5295
|
+
}
|
|
5296
|
+
const token = text(normalized["token"]);
|
|
5297
|
+
const refresh = text(normalized["refresh"]);
|
|
5298
|
+
const applianceSecrets = text(normalized[APPLIANCE_SECRETS_KEY]);
|
|
5299
|
+
const accountLabel = text(normalized["accountLabel"]);
|
|
5300
|
+
return {
|
|
5301
|
+
name: integration.name,
|
|
5302
|
+
...token !== void 0 ? { token } : {},
|
|
5303
|
+
...refresh !== void 0 ? { refresh } : {},
|
|
5304
|
+
...applianceSecrets !== void 0 ? { applianceSecrets } : {},
|
|
5305
|
+
...appliances.length > 0 ? { appliances } : {},
|
|
5306
|
+
...accountLabel !== void 0 ? { accountLabel } : {}
|
|
5285
5307
|
};
|
|
5308
|
+
}
|
|
5309
|
+
/** The whole `bosch-home-connect` catalogue (D987): offered to the engine at `onInitialize`. */
|
|
5310
|
+
var HOME_CONNECT_MIGRATIONS = [require_bosch_instances.boschRowsMigration({
|
|
5311
|
+
addonId: "bosch-home-connect",
|
|
5312
|
+
id: "bosch-home-connect/2026-11-05-integration-rows-to-record",
|
|
5313
|
+
description: "Move every Home Connect account into a typed instance record keyed by its registry id (pairing keys still on an appliance row go to the appliance secrets), then clear the legacy integration settings rows and their keychain values",
|
|
5314
|
+
fixtureMark: "a legacy setting key the migration does not know",
|
|
5315
|
+
kind: homeConnectConnectionsEntryKind,
|
|
5316
|
+
bodyFrom: homeConnectBodyFromRows
|
|
5317
|
+
})];
|
|
5318
|
+
function homeConnectRowsPorts(deps) {
|
|
5319
|
+
return require_bosch_instances.boschRowsPorts({
|
|
5320
|
+
...deps,
|
|
5321
|
+
kind: homeConnectConnectionsEntryKind
|
|
5322
|
+
});
|
|
5323
|
+
}
|
|
5324
|
+
async function homeConnectStoredOf(instances, id) {
|
|
5325
|
+
const read = await instances.readResolved(id);
|
|
5326
|
+
if (read === null) return null;
|
|
5286
5327
|
return {
|
|
5287
|
-
settings:
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
}
|
|
5328
|
+
settings: { ...read.body },
|
|
5329
|
+
locked: read.secrets === "unavailable"
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
function buildHomeConnectInstances(records, deps) {
|
|
5333
|
+
const handle = records.kind(homeConnectConnectionsEntryKind);
|
|
5334
|
+
const instances = new require_bosch_instances.RecordIntegrationInstances(records, homeConnectConnectionsEntryKind, {
|
|
5335
|
+
description: {
|
|
5336
|
+
role: "devices",
|
|
5337
|
+
instanceMode: "multiple",
|
|
5338
|
+
kind: "account",
|
|
5339
|
+
canTest: true
|
|
5340
|
+
},
|
|
5341
|
+
formSchema: () => buildHomeConnectFormSchema(),
|
|
5342
|
+
initialBody: (name) => ({ name }),
|
|
5343
|
+
configured: (body) => connectionFromSettings(body) !== null,
|
|
5344
|
+
onChanged: async (_id, change) => {
|
|
5345
|
+
if (change !== "deleted") await deps.onChanged();
|
|
5346
|
+
},
|
|
5347
|
+
test: (input) => require_bosch_instances.testBoschInstance({
|
|
5348
|
+
...input,
|
|
5349
|
+
tester: deps.tester,
|
|
5350
|
+
handle,
|
|
5351
|
+
secretKeys: SECRET_KEYS
|
|
5352
|
+
})
|
|
5353
|
+
});
|
|
5354
|
+
return {
|
|
5355
|
+
instances,
|
|
5356
|
+
handle,
|
|
5357
|
+
provider: require_bosch_instances.keepBodyKeys(instances.toProvider(), new Set(Object.keys(homeConnectConnectionsEntryKind.schema.shape)))
|
|
5306
5358
|
};
|
|
5307
5359
|
}
|
|
5308
5360
|
//#endregion
|
|
5309
5361
|
//#region src/home-connect/addon.ts
|
|
5310
|
-
var BoschHomeConnectProviderAddon = class extends
|
|
5362
|
+
var BoschHomeConnectProviderAddon = class extends require_bosch_instances.BaseDeviceProvider {
|
|
5311
5363
|
addonId = "bosch-home-connect";
|
|
5312
5364
|
providerName = "Bosch Home Connect";
|
|
5313
5365
|
deviceClasses = {
|
|
5314
|
-
[
|
|
5315
|
-
[
|
|
5316
|
-
[
|
|
5317
|
-
[
|
|
5366
|
+
[require_bosch_instances.DeviceType.Container]: HomeConnectApplianceDevice,
|
|
5367
|
+
[require_bosch_instances.DeviceType.Dishwasher]: HomeConnectDishwasherDevice,
|
|
5368
|
+
[require_bosch_instances.DeviceType.Sensor]: HomeConnectSensorDevice,
|
|
5369
|
+
[require_bosch_instances.DeviceType.EventEmitter]: HomeConnectEventDevice
|
|
5318
5370
|
};
|
|
5319
5371
|
clients = null;
|
|
5372
|
+
instances = null;
|
|
5320
5373
|
reconciler = null;
|
|
5321
5374
|
loginProxy = null;
|
|
5322
5375
|
loginPlane = null;
|
|
@@ -5325,6 +5378,20 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5325
5378
|
}
|
|
5326
5379
|
async onInitialize() {
|
|
5327
5380
|
const regs = this.providerRegistrationsFromSuper(await super.onInitialize());
|
|
5381
|
+
const records = this.ctx.records;
|
|
5382
|
+
if (records === void 0) throw new Error("bosch-home-connect: ctx.records is required (hub-only addon)");
|
|
5383
|
+
await records.declare(homeConnectRecordKinds);
|
|
5384
|
+
await this.runMigrations(require_bosch_instances.defineAddonMigrations(HOME_CONNECT_MIGRATIONS, homeConnectRowsPorts({
|
|
5385
|
+
api: require_bosch_instances.defaultIntegrationApiOver(this.ctx.api),
|
|
5386
|
+
secrets: this.ctx.secrets ?? null,
|
|
5387
|
+
records,
|
|
5388
|
+
warn: (message) => this.ctx.logger.warn(message)
|
|
5389
|
+
})));
|
|
5390
|
+
const built = buildHomeConnectInstances(records, {
|
|
5391
|
+
onChanged: () => this.reconcileIntegrations(),
|
|
5392
|
+
tester: this.buildConnectionTestProvider()
|
|
5393
|
+
});
|
|
5394
|
+
this.instances = built;
|
|
5328
5395
|
await this.serveLoginProxy();
|
|
5329
5396
|
this.clients = new HomeConnectClientRegistry(this.ctx.logger, { onConnectionUpdated: (integrationId, connection) => {
|
|
5330
5397
|
this.persistIntegrationAppliances(integrationId, connection);
|
|
@@ -5344,12 +5411,16 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5344
5411
|
providers: [
|
|
5345
5412
|
...regs,
|
|
5346
5413
|
{
|
|
5347
|
-
capability:
|
|
5414
|
+
capability: require_bosch_instances.deviceAdoptionCapability,
|
|
5348
5415
|
provider: this.buildAdoptionProvider()
|
|
5349
5416
|
},
|
|
5350
5417
|
{
|
|
5351
|
-
capability:
|
|
5418
|
+
capability: require_bosch_instances.connectionTestCapability,
|
|
5352
5419
|
provider: this.buildConnectionTestProvider()
|
|
5420
|
+
},
|
|
5421
|
+
{
|
|
5422
|
+
capability: require_bosch_instances.integrationInstancesCapability,
|
|
5423
|
+
provider: built.provider
|
|
5353
5424
|
}
|
|
5354
5425
|
],
|
|
5355
5426
|
customActions: homeConnectActions,
|
|
@@ -5382,7 +5453,7 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5382
5453
|
}
|
|
5383
5454
|
/** The hub's HTTPS face for the vendor's login pages (D955): admin session only. */
|
|
5384
5455
|
async serveLoginProxy() {
|
|
5385
|
-
const proxy = new
|
|
5456
|
+
const proxy = new require_bosch_instances.VendorLoginProxy({
|
|
5386
5457
|
addonId: this.ctx.id,
|
|
5387
5458
|
logger: this.ctx.logger
|
|
5388
5459
|
});
|
|
@@ -5394,7 +5465,7 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5394
5465
|
}) ?? null;
|
|
5395
5466
|
if (this.loginPlane !== null) this.loginProxy = proxy;
|
|
5396
5467
|
} catch (err) {
|
|
5397
|
-
this.ctx.logger.warn("Home Connect login proxy failed to serve — sign-in falls back to paste", { meta: { error:
|
|
5468
|
+
this.ctx.logger.warn("Home Connect login proxy failed to serve — sign-in falls back to paste", { meta: { error: require_bosch_instances.errMsg(err) } });
|
|
5398
5469
|
}
|
|
5399
5470
|
}
|
|
5400
5471
|
async onShutdown() {
|
|
@@ -5406,9 +5477,10 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5406
5477
|
try {
|
|
5407
5478
|
await this.clients?.shutdown();
|
|
5408
5479
|
} catch (err) {
|
|
5409
|
-
this.ctx.logger.warn("Home Connect shutdown error", { meta: { error:
|
|
5480
|
+
this.ctx.logger.warn("Home Connect shutdown error", { meta: { error: require_bosch_instances.errMsg(err) } });
|
|
5410
5481
|
}
|
|
5411
5482
|
this.clients = null;
|
|
5483
|
+
this.instances = null;
|
|
5412
5484
|
await super.onShutdown();
|
|
5413
5485
|
}
|
|
5414
5486
|
requireClients() {
|
|
@@ -5420,14 +5492,14 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5420
5492
|
if (typeof integrationId !== "string" || integrationId.length === 0) throw new Error("home-connect generateStableId: brokerId required");
|
|
5421
5493
|
const haId = config?.["haId"];
|
|
5422
5494
|
if (typeof haId !== "string" || haId.length === 0) throw new Error("home-connect generateStableId: haId required");
|
|
5423
|
-
if (type ===
|
|
5495
|
+
if (type === require_bosch_instances.DeviceType.Container) return `home-connect:${integrationId}:${haId}:appliance`;
|
|
5424
5496
|
return `home-connect:${integrationId}:${haId}`;
|
|
5425
5497
|
}
|
|
5426
5498
|
/** Enriched IDDF maps and a rotated token pair, in one keychain-aware write. */
|
|
5427
5499
|
persistIntegrationAppliances(integrationId, connection) {
|
|
5428
5500
|
return buildHomeConnectConnectionPersister({
|
|
5429
|
-
|
|
5430
|
-
|
|
5501
|
+
read: (id) => homeConnectStoredOf(this.requireInstances().instances, id),
|
|
5502
|
+
patch: (id, patch) => this.requireInstances().handle.patch(id, patch),
|
|
5431
5503
|
logger: this.ctx.logger
|
|
5432
5504
|
})(integrationId, connection);
|
|
5433
5505
|
}
|
|
@@ -5436,15 +5508,14 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5436
5508
|
try {
|
|
5437
5509
|
const mine = (await this.ctx.api.integrations.list.query()).filter((i) => i.addonId === this.ctx.id && i.enabled);
|
|
5438
5510
|
const surviving = /* @__PURE__ */ new Set();
|
|
5439
|
-
const secretMoves = [];
|
|
5440
5511
|
for (const integration of mine) try {
|
|
5441
|
-
const stored = await this.
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
const settings =
|
|
5512
|
+
const stored = await homeConnectStoredOf(this.requireInstances().instances, integration.id);
|
|
5513
|
+
if (stored === null) continue;
|
|
5514
|
+
if (stored.locked) {
|
|
5515
|
+
this.ctx.logger.warn("Home Connect reconcile skipped: the keychain is locked", { meta: { integrationId: integration.id } });
|
|
5516
|
+
continue;
|
|
5517
|
+
}
|
|
5518
|
+
const settings = stored.settings;
|
|
5448
5519
|
const connection = connectionFromSettings(settings);
|
|
5449
5520
|
if (!connection) {
|
|
5450
5521
|
logHomeConnectSettingsGap(this.ctx.logger, integration.id, settings);
|
|
@@ -5465,10 +5536,9 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5465
5536
|
} catch (err) {
|
|
5466
5537
|
this.ctx.logger.warn("Home Connect reconcile failed for integration", { meta: {
|
|
5467
5538
|
integrationId: integration.id,
|
|
5468
|
-
error:
|
|
5539
|
+
error: require_bosch_instances.errMsg(err)
|
|
5469
5540
|
} });
|
|
5470
5541
|
}
|
|
5471
|
-
for (const move of secretMoves) move.persist();
|
|
5472
5542
|
const vanished = reg.list().filter((id) => !surviving.has(id));
|
|
5473
5543
|
await reg.retain(surviving);
|
|
5474
5544
|
const deviceReg = this.ctx.kernel.deviceRegistry;
|
|
@@ -5482,7 +5552,7 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5482
5552
|
logger: this.ctx.logger
|
|
5483
5553
|
});
|
|
5484
5554
|
} catch (err) {
|
|
5485
|
-
this.ctx.logger.warn("Home Connect integration reconcile failed", { meta: { error:
|
|
5555
|
+
this.ctx.logger.warn("Home Connect integration reconcile failed", { meta: { error: require_bosch_instances.errMsg(err) } });
|
|
5486
5556
|
}
|
|
5487
5557
|
}
|
|
5488
5558
|
subscribeIntegrationLifecycle() {
|
|
@@ -5491,9 +5561,9 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5491
5561
|
if (typeof addonId === "string" && addonId !== this.ctx.id) return;
|
|
5492
5562
|
this.reconcileIntegrations();
|
|
5493
5563
|
};
|
|
5494
|
-
this.ctx.eventBus.subscribe({ category:
|
|
5495
|
-
this.ctx.eventBus.subscribe({ category:
|
|
5496
|
-
this.ctx.eventBus.subscribe({ category:
|
|
5564
|
+
this.ctx.eventBus.subscribe({ category: require_bosch_instances.EventCategory.IntegrationEnabled }, handler);
|
|
5565
|
+
this.ctx.eventBus.subscribe({ category: require_bosch_instances.EventCategory.IntegrationDisabled }, handler);
|
|
5566
|
+
this.ctx.eventBus.subscribe({ category: require_bosch_instances.EventCategory.IntegrationDeleted }, handler);
|
|
5497
5567
|
}
|
|
5498
5568
|
buildConnectionTestProvider() {
|
|
5499
5569
|
return buildHomeConnectConnectionTestProvider({ logger: this.ctx.logger });
|
|
@@ -5520,7 +5590,7 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5520
5590
|
adoptDevice: async ({ haId, integrationId, type, name, applianceType }) => {
|
|
5521
5591
|
const devices = this.ctx.kernel.devices;
|
|
5522
5592
|
if (!devices) throw new Error("home-connect adopt: devices unavailable");
|
|
5523
|
-
if (type !==
|
|
5593
|
+
if (type !== require_bosch_instances.DeviceType.Container) throw new Error(`home-connect adopt: expected Container, got ${type}`);
|
|
5524
5594
|
const config = {
|
|
5525
5595
|
haId,
|
|
5526
5596
|
brokerId: integrationId,
|
|
@@ -5531,7 +5601,7 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5531
5601
|
};
|
|
5532
5602
|
const stableId = this.generateStableId(type, config);
|
|
5533
5603
|
const device = await devices.create(stableId, HomeConnectApplianceDevice, config, null, {
|
|
5534
|
-
type:
|
|
5604
|
+
type: require_bosch_instances.DeviceType.Container,
|
|
5535
5605
|
name,
|
|
5536
5606
|
integrationId
|
|
5537
5607
|
});
|
|
@@ -5596,9 +5666,6 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5596
5666
|
logger: this.ctx.logger
|
|
5597
5667
|
}, trigger);
|
|
5598
5668
|
}
|
|
5599
|
-
globalSettingsSchema() {
|
|
5600
|
-
return buildHomeConnectFormSchema();
|
|
5601
|
-
}
|
|
5602
5669
|
async supportsManualCreation() {
|
|
5603
5670
|
return false;
|
|
5604
5671
|
}
|
|
@@ -5608,6 +5675,10 @@ var BoschHomeConnectProviderAddon = class extends require_dist.BaseDeviceProvide
|
|
|
5608
5675
|
async onCreateDevice(_type, _config) {
|
|
5609
5676
|
throw new Error("Home Connect devices are adopted from the gateway");
|
|
5610
5677
|
}
|
|
5678
|
+
requireInstances() {
|
|
5679
|
+
if (!this.instances) throw new Error("Home Connect provider not initialised");
|
|
5680
|
+
return this.instances;
|
|
5681
|
+
}
|
|
5611
5682
|
setIntegrationDevicesOnline(integrationId, online) {
|
|
5612
5683
|
const reg = this.ctx.kernel.deviceRegistry;
|
|
5613
5684
|
if (!reg) return;
|