@mp-consulting/homebridge-philips-ambilight-tv 1.5.13 → 1.5.14

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.
@@ -739,26 +739,57 @@
739
739
  // EDIT SOURCES SCREEN
740
740
  // ============================================================================
741
741
 
742
+ // Session cache of the raw source list fetched from each TV, keyed by a stable
743
+ // TV id. Reopening the sources screen renders from this cache instead of
744
+ // issuing another /get-sources request. That sidesteps a Homebridge Config UI
745
+ // X bug (#14) where the response to a *second* request is dropped — it tries
746
+ // to postMessage to a stale/detached plugin iframe (contentWindow === null)
747
+ // and throws — leaving the screen stuck on "Fetching sources". Use "Refresh
748
+ // from TV" to deliberately re-fetch.
749
+ const sourcesCache = new Map();
750
+ const sourcesCacheKey = (tv) => tv.mac || tv.ip;
751
+
752
+ /** Merge freshly-fetched (or cached) raw sources with the TV's saved config
753
+ * and render the two-column list. */
754
+ const showSources = (tv, rawSources) => {
755
+ const withCustom = injectCustomApps(rawSources, tv.customApps);
756
+ const existingConfig = tv.sources || [];
757
+ state.sources = mergeSourcesWithConfig(withCustom, existingConfig);
758
+ renderSourcesList();
759
+ $('sourcesLoadingSpinner').style.display = 'none';
760
+ $('sourcesErrorContainer').style.display = 'none';
761
+ $('sourcesListContainer').style.display = 'block';
762
+ };
763
+
742
764
  const openEditSourcesScreen = async (index) => {
743
765
  state.editingSourcesTvIndex = index;
744
766
  const tv = state.configuredTvs[index];
745
767
 
746
768
  $('editSourcesTvName').textContent = tv.name;
747
- $('sourcesLoadingSpinner').style.display = 'block';
748
769
  $('sourcesListContainer').style.display = 'none';
749
770
  $('sourcesErrorContainer').style.display = 'none';
750
771
 
751
772
  showScreen('editSourcesScreen');
752
773
 
774
+ // Render from the session cache when we already fetched this TV's sources,
775
+ // avoiding a second request that Config UI X may fail to deliver (#14).
776
+ const cached = sourcesCache.get(sourcesCacheKey(tv));
777
+ if (cached) {
778
+ $('sourcesLoadingSpinner').style.display = 'none';
779
+ showSources(tv, cached);
780
+ return;
781
+ }
782
+
783
+ $('sourcesLoadingSpinner').style.display = 'block';
753
784
  await loadSources(tv);
754
785
  };
755
786
 
756
787
  let sourcesLoading = false;
757
788
 
758
789
  const loadSources = async (tv) => {
759
- // Guard against overlapping fetches: a hung request keeps its promise
760
- // pending for the full 20s, and firing more only stacks dead requests
761
- // behind it. Ignore re-triggers until the current one settles.
790
+ // Guard against overlapping fetches: a hung request (see #14) keeps its
791
+ // promise pending for the full 20s, and firing more only stacks dead
792
+ // requests behind it. Ignore re-triggers until the current one settles.
762
793
  if (sourcesLoading) {
763
794
  return;
764
795
  }
@@ -774,15 +805,9 @@
774
805
  );
775
806
 
776
807
  if (result.success) {
777
- // Inject configured custom apps (the TV doesn't report these), then
778
- // merge with existing source config (order and visibility).
779
- const withCustom = injectCustomApps(result.sources, tv.customApps);
780
- const existingConfig = tv.sources || [];
781
- state.sources = mergeSourcesWithConfig(withCustom, existingConfig);
782
- renderSourcesList();
783
-
784
- $('sourcesLoadingSpinner').style.display = 'none';
785
- $('sourcesListContainer').style.display = 'block';
808
+ // Cache the raw list so reopening the screen doesn't re-request (#14).
809
+ sourcesCache.set(sourcesCacheKey(tv), result.sources);
810
+ showSources(tv, result.sources);
786
811
  } else {
787
812
  showSourcesError(result.error);
788
813
  }
@@ -1036,12 +1061,17 @@
1036
1061
  tv.sources = [];
1037
1062
  await saveConfig();
1038
1063
 
1039
- // Show loading state
1040
- $('sourcesLoadingSpinner').style.display = 'block';
1041
- $('sourcesListContainer').style.display = 'none';
1042
-
1043
- // Re-fetch sources from TV (will use default order since config is cleared)
1044
- await loadSources(tv);
1064
+ // Re-render with the default order. Prefer the cached list so we don't fire
1065
+ // another /get-sources request (Config UI X may drop its response, #14);
1066
+ // only re-fetch if we somehow have no cache yet.
1067
+ const cached = sourcesCache.get(sourcesCacheKey(tv));
1068
+ if (cached) {
1069
+ showSources(tv, cached);
1070
+ } else {
1071
+ $('sourcesLoadingSpinner').style.display = 'block';
1072
+ $('sourcesListContainer').style.display = 'none';
1073
+ await loadSources(tv);
1074
+ }
1045
1075
 
1046
1076
  homebridge.toast.success('Source order reset to original');
1047
1077
  };
@@ -1171,6 +1201,15 @@
1171
1201
  $('sourcesErrorContainer').style.display = 'none';
1172
1202
  loadSources(state.configuredTvs[state.editingSourcesTvIndex]);
1173
1203
  });
1204
+ $('refreshSourcesBtn').addEventListener('click', () => {
1205
+ const tv = state.configuredTvs[state.editingSourcesTvIndex];
1206
+ // Drop the cached list so the user gets a genuine re-fetch from the TV.
1207
+ sourcesCache.delete(sourcesCacheKey(tv));
1208
+ $('sourcesListContainer').style.display = 'none';
1209
+ $('sourcesErrorContainer').style.display = 'none';
1210
+ $('sourcesLoadingSpinner').style.display = 'block';
1211
+ loadSources(tv);
1212
+ });
1174
1213
 
1175
1214
  // ============================================================================
1176
1215
  // INITIALIZATION
@@ -489,9 +489,14 @@
489
489
  </div>
490
490
  </div>
491
491
  <div class="d-flex justify-content-between mt-2">
492
- <button id="resetSourcesOrderBtn" class="btn btn-outline-secondary btn-sm">
493
- <i class="bi bi-arrow-counterclockwise"></i> Reset
494
- </button>
492
+ <div class="d-flex gap-2">
493
+ <button id="resetSourcesOrderBtn" class="btn btn-outline-secondary btn-sm">
494
+ <i class="bi bi-arrow-counterclockwise"></i> Reset
495
+ </button>
496
+ <button id="refreshSourcesBtn" class="btn btn-outline-secondary btn-sm" title="Re-fetch the source and app list from the TV">
497
+ <i class="bi bi-arrow-clockwise"></i> Refresh from TV
498
+ </button>
499
+ </div>
495
500
  <button id="doneEditSourcesBtn" class="btn btn-primary btn-sm">
496
501
  <i class="bi bi-check-lg"></i> Done
497
502
  </button>
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@mp-consulting/homebridge-philips-ambilight-tv",
3
3
  "displayName": "Philips Ambilight TV",
4
4
  "type": "module",
5
- "version": "1.5.13",
5
+ "version": "1.5.14",
6
6
  "description": "Homebridge plugin to control Philips Ambilight TV as a HomeKit Television accessory.",
7
7
  "author": "Mickael Palma",
8
8
  "license": "(Apache-2.0 AND MIT)",