homebridge-nuheat2 1.2.16 → 1.2.17

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 CHANGED
@@ -4,6 +4,13 @@ All notable changes to this project should be documented in this file
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.17] - 2026-04-29
8
+
9
+ ### Fixed
10
+
11
+ - Treat the built-in Nuheat public client ID as PKCE-only even when it is explicitly saved in Advanced OAuth settings
12
+ - Ignore and clear stale saved client secrets when users return to the built-in PKCE public client
13
+
7
14
  ## [1.2.16] - 2026-04-29
8
15
 
9
16
  ### Changed
@@ -1,4 +1,5 @@
1
1
  const PLATFORM_NAME = "NuHeat";
2
+ const BUILT_IN_CLIENT_ID = "homebridge-nuheat2_260421";
2
3
 
3
4
  const elements = {
4
5
  name: document.getElementById("name"),
@@ -139,7 +140,9 @@ function renderConfig(config) {
139
140
 
140
141
  elements.clientId.value = config.clientId || "";
141
142
  elements.clientSecret.value = "";
142
- state.hasClientSecret = Boolean(config.clientSecret);
143
+ state.hasClientSecret = Boolean(
144
+ config.clientSecret && config.clientId !== BUILT_IN_CLIENT_ID,
145
+ );
143
146
  elements.clientSecret.placeholder = state.hasClientSecret
144
147
  ? "Saved secret (leave blank to keep)"
145
148
  : "Optional client secret";
@@ -313,6 +316,7 @@ async function saveOauth() {
313
316
  const clientId = elements.clientId.value.trim();
314
317
  const clientSecret = elements.clientSecret.value;
315
318
  const redirectUri = elements.redirectUri.value.trim();
319
+ const usesBuiltInClient = !clientId || clientId === BUILT_IN_CLIENT_ID;
316
320
 
317
321
  if (!clientId && clientSecret) {
318
322
  showToast("error", "A client secret requires a Nuheat client ID.");
@@ -321,24 +325,25 @@ async function saveOauth() {
321
325
  }
322
326
 
323
327
  const patch = {
324
- clientId: clientId || undefined,
328
+ clientId: usesBuiltInClient ? undefined : clientId,
325
329
  redirectUri: redirectUri && redirectUri !== "http://localhost" ? redirectUri : undefined,
326
330
  };
327
331
 
328
- if (clientSecret) {
332
+ if (clientSecret && !usesBuiltInClient) {
329
333
  patch.clientSecret = clientSecret;
330
- } else if (!clientId || clientId !== (state.config?.clientId || "")) {
334
+ } else if (usesBuiltInClient || clientId !== (state.config?.clientId || "")) {
331
335
  patch.clientSecret = undefined;
332
336
  }
333
337
 
334
338
  await persistPatch(patch);
335
- if (clientSecret) {
336
- state.hasClientSecret = true;
337
- elements.clientSecret.value = "";
338
- elements.clientSecret.placeholder = "Saved secret (leave blank to keep)";
339
- updateStatuses();
340
- renderDiagnostics();
341
- }
339
+ state.hasClientSecret = Boolean(state.config?.clientSecret);
340
+ elements.clientId.value = state.config?.clientId || "";
341
+ elements.clientSecret.value = "";
342
+ elements.clientSecret.placeholder = state.hasClientSecret
343
+ ? "Saved secret (leave blank to keep)"
344
+ : "Optional client secret";
345
+ updateStatuses();
346
+ renderDiagnostics();
342
347
  showToast("success", "OAuth settings saved.");
343
348
  }
344
349
 
@@ -437,8 +442,9 @@ function updateBehaviorStatus() {
437
442
  }
438
443
 
439
444
  function updateOauthStatus() {
440
- const hasClientId = elements.clientId.value.trim().length > 0;
441
- const hasClientSecret = hasUsableClientSecret(elements.clientId.value.trim());
445
+ const clientId = elements.clientId.value.trim();
446
+ const hasClientId = clientId.length > 0 && clientId !== BUILT_IN_CLIENT_ID;
447
+ const hasClientSecret = hasUsableClientSecret(clientId);
442
448
  const text = hasClientId
443
449
  ? hasClientSecret
444
450
  ? "Custom legacy OAuth"
@@ -448,6 +454,10 @@ function updateOauthStatus() {
448
454
  }
449
455
 
450
456
  function hasUsableClientSecret(clientId) {
457
+ if (!clientId || clientId === BUILT_IN_CLIENT_ID) {
458
+ return false;
459
+ }
460
+
451
461
  if (elements.clientSecret.value) {
452
462
  return true;
453
463
  }
@@ -577,13 +587,16 @@ function getHoldSummary(value) {
577
587
  }
578
588
 
579
589
  function getOauthMode(config) {
590
+ if (!config.clientId || config.clientId === BUILT_IN_CLIENT_ID) {
591
+ return "built-in PKCE public client";
592
+ }
593
+
580
594
  if (config.clientId && hasUsableClientSecret(config.clientId)) {
581
595
  return "configured confidential client";
582
596
  }
583
597
  if (config.clientId) {
584
598
  return "configured PKCE public client";
585
599
  }
586
- return "built-in PKCE public client";
587
600
  }
588
601
 
589
602
  function normalizeHoldLength(value) {
package/lib/NuHeatAPI.js CHANGED
@@ -33,15 +33,16 @@ class NuHeatAPI {
33
33
  this.log = log;
34
34
  const configuredClientId = options.clientId || process.env.NUHEAT_API_CLIENT_ID || "";
35
35
  const configuredClientSecret = options.clientSecret || process.env.NUHEAT_API_CLIENT_SECRET || "";
36
+ const usingBuiltInPublicClient = !configuredClientId || configuredClientId === settings_1.NUHEAT_API_CLIENT_ID;
36
37
  this.oauthClientId = configuredClientId || settings_1.NUHEAT_API_CLIENT_ID;
37
- this.oauthClientSecret = configuredClientId
38
- ? configuredClientSecret || settings_1.NUHEAT_API_CLIENT_SECRET
39
- : "";
38
+ this.oauthClientSecret = usingBuiltInPublicClient
39
+ ? ""
40
+ : configuredClientSecret || settings_1.NUHEAT_API_CLIENT_SECRET;
40
41
  this.oauthRedirectUri =
41
42
  options.redirectUri ||
42
43
  process.env.NUHEAT_API_REDIRECT_URI ||
43
44
  settings_1.NUHEAT_API_REDIRECT_URI;
44
- this.usingBuiltInClient = !configuredClientId;
45
+ this.usingBuiltInClient = usingBuiltInPublicClient;
45
46
  this.usePkce = !this.oauthClientSecret;
46
47
  this.pkceCodeVerifier = "";
47
48
  this.headers = new HeadersCtor();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-nuheat2",
3
- "version": "1.2.16",
3
+ "version": "1.2.17",
4
4
  "description": "Homebridge Platform for NuHeat Signature Thermostats",
5
5
  "main": "index.js",
6
6
  "scripts": {