@camstack/addon-provider-bosch 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-B-Un7bqT.js");
3
- const require_account_integration_offline = require("../account-integration-offline-BBqEtCgz.js");
2
+ const require_dist = require("../dist-CIIror0W.js");
3
+ const require_account_integration_offline = require("../account-integration-offline-BhjIPfv5.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");
@@ -589,6 +589,14 @@ function tokensFromBody(body) {
589
589
  refresh: typeof refresh === "string" ? refresh : ""
590
590
  };
591
591
  }
592
+ function isInvalidGrant(body) {
593
+ try {
594
+ const parsed = JSON.parse(body);
595
+ return isRecord$1(parsed) && parsed["error"] === "invalid_grant";
596
+ } catch {
597
+ return false;
598
+ }
599
+ }
592
600
  function accountSummary(appliances) {
593
601
  if (appliances.length === 0) return "Signed in. No appliances on this account yet.";
594
602
  const names = appliances.map((app) => app.name).join(", ");
@@ -752,27 +760,34 @@ function beginHomeConnectSignIn() {
752
760
  state
753
761
  };
754
762
  }
755
- function callbackParams(callback) {
756
- let url;
757
- try {
758
- url = new URL(callback.trim());
759
- } catch {
760
- throw new Error("Paste the full redirect address from the browser");
763
+ var LOGIN_MESSAGES = {
764
+ empty: "Nothing was pasted. Paste the address the browser tried to open after you signed in.",
765
+ "no-code": "No sign-in code was found in what you pasted. Copy the whole address that starts with hcauth://, or just the code.",
766
+ "state-missing": "The pasted address has no state. Copy the complete address, or create the login link again.",
767
+ "state-mismatch": "State mismatch: this address belongs to a different sign-in attempt. Create the login link again and use the address that follows it.",
768
+ denied: "Home Connect refused the sign-in. Create the login link again and approve the access.",
769
+ "code-rejected": "The code was already used or has expired. Create the login link again; a code works once and only for a few minutes."
770
+ };
771
+ /** A failure the operator can act on; `reason` is stable, the message is for the form. */
772
+ var HomeConnectLoginError = class extends Error {
773
+ name = "HomeConnectLoginError";
774
+ reason;
775
+ constructor(reason) {
776
+ super(LOGIN_MESSAGES[reason]);
777
+ this.reason = reason;
761
778
  }
762
- const code = url.searchParams.get("code") ?? url.hash.replace(/^#/, "").match(/(?:^|&)code=([^&]+)/)?.[1];
763
- const state = url.searchParams.get("state") ?? "";
764
- if (!code) throw new Error("Paste the full redirect address from the browser");
779
+ };
780
+ function callbackParams(callback, expectedState) {
781
+ const parsed = require_account_integration_offline.parseOAuthCallback(callback, expectedState);
782
+ if (!parsed.ok) throw new HomeConnectLoginError(parsed.reason);
765
783
  return {
766
- code,
767
- grantType: url.searchParams.get("grant_type") ?? "authorization_code",
768
- state
784
+ code: parsed.code,
785
+ grantType: parsed.params.get("grant_type") ?? "authorization_code"
769
786
  };
770
787
  }
771
788
  /** Exchange the pasted redirect for the account token and each appliance profile. */
772
789
  async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetch) {
773
- const parsed = callbackParams(input.callback);
774
- if (parsed.state !== input.state) throw new Error("State mismatch. Create the login link again.");
775
- const { code, grantType } = parsed;
790
+ const { code, grantType } = callbackParams(input.callback, input.state);
776
791
  const token = await exchange(fetchImpl, new CookieJar(), `${OAUTH_BASE}token`, "POST", { "content-type": "application/x-www-form-urlencoded" }, new URLSearchParams({
777
792
  grant_type: grantType,
778
793
  client_id: APP_ID$1,
@@ -780,7 +795,10 @@ async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetc
780
795
  code_verifier: input.verifier,
781
796
  redirect_uri: REDIRECT_URI
782
797
  }).toString());
783
- if (token.status >= 400) throw new Error("Home Connect sign-in was rejected");
798
+ if (token.status >= 400) {
799
+ if (isInvalidGrant(token.body)) throw new HomeConnectLoginError("code-rejected");
800
+ throw new Error("Home Connect sign-in was rejected");
801
+ }
784
802
  return fetchHomeConnectAccount(tokensFromBody(token.body), fetchImpl);
785
803
  }
786
804
  //#endregion
@@ -1233,7 +1251,9 @@ function buildHomeConnectFormSchema() {
1233
1251
  return { sections: [{
1234
1252
  id: "gateway",
1235
1253
  title: "Home Connect",
1236
- description: "Create the login link and open it in the browser. After signing in, paste the full address the browser tried to open (it starts with hcauth://). The appliances are downloaded and then spoken to on the local network.",
1254
+ titleKey: "provider-bosch:home-connect.s_gateway.title",
1255
+ description: "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
1256
+ descriptionKey: "provider-bosch:home-connect.s_gateway.description",
1237
1257
  columns: 1,
1238
1258
  fields: [
1239
1259
  {
@@ -1241,6 +1261,7 @@ function buildHomeConnectFormSchema() {
1241
1261
  addonId: "bosch-home-connect",
1242
1262
  key: "__home-connect-begin",
1243
1263
  label: "Home Connect",
1264
+ labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-begin.label",
1244
1265
  buttonLabel: "Create login link",
1245
1266
  action: "beginSignIn",
1246
1267
  variant: "primary",
@@ -1255,13 +1276,15 @@ function buildHomeConnectFormSchema() {
1255
1276
  type: "textarea",
1256
1277
  key: "authorizeUrl",
1257
1278
  label: "Login link",
1279
+ labelKey: "provider-bosch:home-connect.s_gateway.authorizeUrl.label",
1258
1280
  required: false,
1259
1281
  omitFromSettings: true
1260
1282
  },
1261
1283
  {
1262
- type: "text",
1284
+ type: "textarea",
1263
1285
  key: "callback",
1264
- label: "Redirect address",
1286
+ label: "Redirect address or code",
1287
+ labelKey: "provider-bosch:home-connect.s_gateway.callback.label",
1265
1288
  required: false,
1266
1289
  placeholder: "hcauth://auth/prod?code=…&state=…",
1267
1290
  omitFromSettings: true
@@ -1270,6 +1293,7 @@ function buildHomeConnectFormSchema() {
1270
1293
  type: "password",
1271
1294
  key: "state",
1272
1295
  label: "Login state",
1296
+ labelKey: "provider-bosch:home-connect.s_gateway.state.label",
1273
1297
  required: false,
1274
1298
  omitFromSettings: true,
1275
1299
  showWhen: pairedOnly
@@ -1278,6 +1302,7 @@ function buildHomeConnectFormSchema() {
1278
1302
  type: "password",
1279
1303
  key: "verifier",
1280
1304
  label: "Login verifier",
1305
+ labelKey: "provider-bosch:home-connect.s_gateway.verifier.label",
1281
1306
  required: false,
1282
1307
  omitFromSettings: true,
1283
1308
  showWhen: pairedOnly
@@ -1287,6 +1312,7 @@ function buildHomeConnectFormSchema() {
1287
1312
  addonId: "bosch-home-connect",
1288
1313
  key: "__home-connect-finish",
1289
1314
  label: "Finish",
1315
+ labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-finish.label",
1290
1316
  buttonLabel: "Complete sign-in",
1291
1317
  action: "finishSignIn",
1292
1318
  variant: "primary",
@@ -1307,6 +1333,7 @@ function buildHomeConnectFormSchema() {
1307
1333
  type: "password",
1308
1334
  key: "token",
1309
1335
  label: "Access token",
1336
+ labelKey: "provider-bosch:home-connect.s_gateway.token.label",
1310
1337
  required: true,
1311
1338
  showWhen: pairedOnly
1312
1339
  },
@@ -1314,6 +1341,7 @@ function buildHomeConnectFormSchema() {
1314
1341
  type: "password",
1315
1342
  key: "refresh",
1316
1343
  label: "Refresh token",
1344
+ labelKey: "provider-bosch:home-connect.s_gateway.refresh.label",
1317
1345
  required: false,
1318
1346
  showWhen: pairedOnly
1319
1347
  },
@@ -1321,6 +1349,7 @@ function buildHomeConnectFormSchema() {
1321
1349
  type: "textarea",
1322
1350
  key: "appliances",
1323
1351
  label: "Appliances",
1352
+ labelKey: "provider-bosch:home-connect.s_gateway.appliances.label",
1324
1353
  required: true,
1325
1354
  showWhen: pairedOnly
1326
1355
  },
@@ -1,5 +1,5 @@
1
- import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, N as union, O as literal, P as unknown, S as customAction, T as isSecretMarkerLike, a as binaryCapability, b as errMsg, c as deviceAdoptionCapability, d as eventEmitterCapability, j as record, l as dishwasherCapability, m as powerMeterCapability, n as BaseDeviceProvider, p as numericSensorCapability, r as DishwasherOperationStateSchema, s as connectionTestCapability, t as BaseDevice, u as enumSensorCapability, v as DeviceRole, w as isSecretMarker, x as createEvent, y as DeviceType } from "../dist-BiZi19Lw.mjs";
2
- import { n as cascadeRemoveDevicesForIntegration, t as setIntegrationDevicesOnline } from "../account-integration-offline-BfoLYzxz.mjs";
1
+ import { A as object, C as defineCustomActions, D as array, E as _enum, F as EventCategory, M as string, N as union, O as literal, P as unknown, S as customAction, T as isSecretMarkerLike, a as binaryCapability, b as errMsg, c as deviceAdoptionCapability, d as eventEmitterCapability, j as record, l as dishwasherCapability, m as powerMeterCapability, n as BaseDeviceProvider, p as numericSensorCapability, r as DishwasherOperationStateSchema, s as connectionTestCapability, t as BaseDevice, u as enumSensorCapability, v as DeviceRole, w as isSecretMarker, x as createEvent, y as DeviceType } from "../dist-DNznrlG_.mjs";
2
+ import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-DW2D0vf8.mjs";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { connect } from "node:tls";
5
5
  import { inflateRawSync } from "node:zlib";
@@ -588,6 +588,14 @@ function tokensFromBody(body) {
588
588
  refresh: typeof refresh === "string" ? refresh : ""
589
589
  };
590
590
  }
591
+ function isInvalidGrant(body) {
592
+ try {
593
+ const parsed = JSON.parse(body);
594
+ return isRecord$1(parsed) && parsed["error"] === "invalid_grant";
595
+ } catch {
596
+ return false;
597
+ }
598
+ }
591
599
  function accountSummary(appliances) {
592
600
  if (appliances.length === 0) return "Signed in. No appliances on this account yet.";
593
601
  const names = appliances.map((app) => app.name).join(", ");
@@ -751,27 +759,34 @@ function beginHomeConnectSignIn() {
751
759
  state
752
760
  };
753
761
  }
754
- function callbackParams(callback) {
755
- let url;
756
- try {
757
- url = new URL(callback.trim());
758
- } catch {
759
- throw new Error("Paste the full redirect address from the browser");
762
+ var LOGIN_MESSAGES = {
763
+ empty: "Nothing was pasted. Paste the address the browser tried to open after you signed in.",
764
+ "no-code": "No sign-in code was found in what you pasted. Copy the whole address that starts with hcauth://, or just the code.",
765
+ "state-missing": "The pasted address has no state. Copy the complete address, or create the login link again.",
766
+ "state-mismatch": "State mismatch: this address belongs to a different sign-in attempt. Create the login link again and use the address that follows it.",
767
+ denied: "Home Connect refused the sign-in. Create the login link again and approve the access.",
768
+ "code-rejected": "The code was already used or has expired. Create the login link again; a code works once and only for a few minutes."
769
+ };
770
+ /** A failure the operator can act on; `reason` is stable, the message is for the form. */
771
+ var HomeConnectLoginError = class extends Error {
772
+ name = "HomeConnectLoginError";
773
+ reason;
774
+ constructor(reason) {
775
+ super(LOGIN_MESSAGES[reason]);
776
+ this.reason = reason;
760
777
  }
761
- const code = url.searchParams.get("code") ?? url.hash.replace(/^#/, "").match(/(?:^|&)code=([^&]+)/)?.[1];
762
- const state = url.searchParams.get("state") ?? "";
763
- if (!code) throw new Error("Paste the full redirect address from the browser");
778
+ };
779
+ function callbackParams(callback, expectedState) {
780
+ const parsed = parseOAuthCallback(callback, expectedState);
781
+ if (!parsed.ok) throw new HomeConnectLoginError(parsed.reason);
764
782
  return {
765
- code,
766
- grantType: url.searchParams.get("grant_type") ?? "authorization_code",
767
- state
783
+ code: parsed.code,
784
+ grantType: parsed.params.get("grant_type") ?? "authorization_code"
768
785
  };
769
786
  }
770
787
  /** Exchange the pasted redirect for the account token and each appliance profile. */
771
788
  async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetch) {
772
- const parsed = callbackParams(input.callback);
773
- if (parsed.state !== input.state) throw new Error("State mismatch. Create the login link again.");
774
- const { code, grantType } = parsed;
789
+ const { code, grantType } = callbackParams(input.callback, input.state);
775
790
  const token = await exchange(fetchImpl, new CookieJar(), `${OAUTH_BASE}token`, "POST", { "content-type": "application/x-www-form-urlencoded" }, new URLSearchParams({
776
791
  grant_type: grantType,
777
792
  client_id: APP_ID$1,
@@ -779,7 +794,10 @@ async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetc
779
794
  code_verifier: input.verifier,
780
795
  redirect_uri: REDIRECT_URI
781
796
  }).toString());
782
- if (token.status >= 400) throw new Error("Home Connect sign-in was rejected");
797
+ if (token.status >= 400) {
798
+ if (isInvalidGrant(token.body)) throw new HomeConnectLoginError("code-rejected");
799
+ throw new Error("Home Connect sign-in was rejected");
800
+ }
783
801
  return fetchHomeConnectAccount(tokensFromBody(token.body), fetchImpl);
784
802
  }
785
803
  //#endregion
@@ -1232,7 +1250,9 @@ function buildHomeConnectFormSchema() {
1232
1250
  return { sections: [{
1233
1251
  id: "gateway",
1234
1252
  title: "Home Connect",
1235
- description: "Create the login link and open it in the browser. After signing in, paste the full address the browser tried to open (it starts with hcauth://). The appliances are downloaded and then spoken to on the local network.",
1253
+ titleKey: "provider-bosch:home-connect.s_gateway.title",
1254
+ description: "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
1255
+ descriptionKey: "provider-bosch:home-connect.s_gateway.description",
1236
1256
  columns: 1,
1237
1257
  fields: [
1238
1258
  {
@@ -1240,6 +1260,7 @@ function buildHomeConnectFormSchema() {
1240
1260
  addonId: "bosch-home-connect",
1241
1261
  key: "__home-connect-begin",
1242
1262
  label: "Home Connect",
1263
+ labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-begin.label",
1243
1264
  buttonLabel: "Create login link",
1244
1265
  action: "beginSignIn",
1245
1266
  variant: "primary",
@@ -1254,13 +1275,15 @@ function buildHomeConnectFormSchema() {
1254
1275
  type: "textarea",
1255
1276
  key: "authorizeUrl",
1256
1277
  label: "Login link",
1278
+ labelKey: "provider-bosch:home-connect.s_gateway.authorizeUrl.label",
1257
1279
  required: false,
1258
1280
  omitFromSettings: true
1259
1281
  },
1260
1282
  {
1261
- type: "text",
1283
+ type: "textarea",
1262
1284
  key: "callback",
1263
- label: "Redirect address",
1285
+ label: "Redirect address or code",
1286
+ labelKey: "provider-bosch:home-connect.s_gateway.callback.label",
1264
1287
  required: false,
1265
1288
  placeholder: "hcauth://auth/prod?code=…&state=…",
1266
1289
  omitFromSettings: true
@@ -1269,6 +1292,7 @@ function buildHomeConnectFormSchema() {
1269
1292
  type: "password",
1270
1293
  key: "state",
1271
1294
  label: "Login state",
1295
+ labelKey: "provider-bosch:home-connect.s_gateway.state.label",
1272
1296
  required: false,
1273
1297
  omitFromSettings: true,
1274
1298
  showWhen: pairedOnly
@@ -1277,6 +1301,7 @@ function buildHomeConnectFormSchema() {
1277
1301
  type: "password",
1278
1302
  key: "verifier",
1279
1303
  label: "Login verifier",
1304
+ labelKey: "provider-bosch:home-connect.s_gateway.verifier.label",
1280
1305
  required: false,
1281
1306
  omitFromSettings: true,
1282
1307
  showWhen: pairedOnly
@@ -1286,6 +1311,7 @@ function buildHomeConnectFormSchema() {
1286
1311
  addonId: "bosch-home-connect",
1287
1312
  key: "__home-connect-finish",
1288
1313
  label: "Finish",
1314
+ labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-finish.label",
1289
1315
  buttonLabel: "Complete sign-in",
1290
1316
  action: "finishSignIn",
1291
1317
  variant: "primary",
@@ -1306,6 +1332,7 @@ function buildHomeConnectFormSchema() {
1306
1332
  type: "password",
1307
1333
  key: "token",
1308
1334
  label: "Access token",
1335
+ labelKey: "provider-bosch:home-connect.s_gateway.token.label",
1309
1336
  required: true,
1310
1337
  showWhen: pairedOnly
1311
1338
  },
@@ -1313,6 +1340,7 @@ function buildHomeConnectFormSchema() {
1313
1340
  type: "password",
1314
1341
  key: "refresh",
1315
1342
  label: "Refresh token",
1343
+ labelKey: "provider-bosch:home-connect.s_gateway.refresh.label",
1316
1344
  required: false,
1317
1345
  showWhen: pairedOnly
1318
1346
  },
@@ -1320,6 +1348,7 @@ function buildHomeConnectFormSchema() {
1320
1348
  type: "textarea",
1321
1349
  key: "appliances",
1322
1350
  label: "Appliances",
1351
+ labelKey: "provider-bosch:home-connect.s_gateway.appliances.label",
1323
1352
  required: true,
1324
1353
  showWhen: pairedOnly
1325
1354
  },