@camstack/addon-provider-bosch 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,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-LJMt_ZXW.mjs";
2
+ import { n as cascadeRemoveDevicesForIntegration, r as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-DKdrL0V9.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
  },