senza-sdk 4.4.4-86e7d7a.0 → 4.4.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.
@@ -0,0 +1,181 @@
1
+ import { DisplayManager as DisplayManagerInterface } from "../interface/displayManager";
2
+ import { sdkLogger, getRestResponse } from "./utils";
3
+ import { parseEdid } from "./edidParser";
4
+ import { EventListenersManager } from "./eventListenersManager";
5
+ import { bus, Events } from "./eventBus";
6
+ import { lifecycle } from "./lifecycle";
7
+
8
+
9
+ export class DisplayManager extends DisplayManagerInterface {
10
+
11
+ constructor() {
12
+ super();
13
+ /**
14
+ * Event listeners manager for the displayManager events
15
+ * @type {EventListenersManager}
16
+ * @private
17
+ */
18
+ this._eventManager = new EventListenersManager({
19
+ timeoutMs: 10000 // Default timeout of 10 seconds, can be overridden by _setDefaultTimeout
20
+ });
21
+
22
+ /**
23
+ * @type {boolean}
24
+ * @private
25
+ */
26
+ this._isInitialized = false;
27
+ }
28
+
29
+ async _updateDisplayInfo() {
30
+ try {
31
+ const displayInfoResponse = await getRestResponse("display-info");
32
+ const displayInfo = this._parseDisplayInfo(displayInfoResponse);
33
+ this._setDisplayProperties(displayInfo);
34
+ } catch (e) {
35
+ sdkLogger.error(e);
36
+ this._setDisplayProperties({ connection: this.DisplayConnectionStatus.UNKNOWN });
37
+ }
38
+ }
39
+
40
+ async _init() {
41
+ sdkLogger.log("Initializing DisplayManager");
42
+ await this._updateDisplayInfo();
43
+ if (!this._isInitialized) {
44
+ this._isInitialized = true;
45
+ this._addSenzaEventListeners();
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @private Add event listeners for system events
51
+ */
52
+ _addSenzaEventListeners() {
53
+ bus.addEventListener(Events.LifecycleForeground, () => {
54
+ this._moveToForegroundHasBeenCalled = true;
55
+ });
56
+
57
+ typeof document !== "undefined" && document.addEventListener("hs/displayInfoChanged", async (event) => {
58
+ sdkLogger.info("Got hs/displayInfoChanged event with detail", JSON.stringify(event?.detail));
59
+
60
+ try {
61
+ const displayInfo = this._parseDisplayInfo(event.detail.displayInfo);
62
+ this._setDisplayProperties(displayInfo);
63
+
64
+ const timeBeforeCallbacks = Date.now();
65
+
66
+ // Dispatch event to application and allow a chance to move to foreground.
67
+ // If there are no callbacks or the application doesn't move to foreground, the UI will be disconnected.
68
+ await this._eventManager.dispatch("displayinfochanged", { displayInfo });
69
+
70
+ const callbackDuration = Date.now() - timeBeforeCallbacks;
71
+ sdkLogger.log(`All callbacks for displayInfoChanged are finished within ${callbackDuration}ms`);
72
+ } catch (error) {
73
+ sdkLogger.error("Failed to handle displayInfo update", error);
74
+ }
75
+
76
+ const isTriggering = lifecycle.triggerEvent.type === "displayInfoChanged" && lifecycle._triggerEventFcid && lifecycle._triggerEventFcid === event.detail.fcid;
77
+ if (isTriggering) {
78
+ if (!this._moveToForegroundHasBeenCalled && window.cefQuery) {
79
+ sdkLogger.log("Application is about to be disconnected since didn't move to foreground");
80
+ const message = { type: "disconnect" };
81
+ const request = { target: "TC", waitForResponse: false, message: JSON.stringify(message) };
82
+ window.cefQuery({
83
+ request: JSON.stringify(request),
84
+ persistent: false,
85
+ onSuccess: () => {
86
+ sdkLogger.log("disconnect request successfully sent");
87
+ },
88
+ onFailure: (code, msg) => {
89
+ sdkLogger.error(`disconnect request failed: ${code} ${msg}`);
90
+ }
91
+ });
92
+ }
93
+ }
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Set the default timeout for display manager event listeners
99
+ * @param {number} timeout - Timeout in milliseconds for display manager event listeners
100
+ * @private
101
+ */
102
+ _setDefaultTimeout(timeout) {
103
+ if (typeof timeout === "number" && timeout > 0) {
104
+ this._eventManager.timeoutMs = timeout;
105
+ sdkLogger.log(`displayManager event listener timeout set to ${timeout}ms`);
106
+ } else {
107
+ sdkLogger.warn(`Invalid timeout value: ${timeout}. Must be a positive number.`);
108
+ }
109
+ }
110
+
111
+ _setDisplayProperties(displayInfo) {
112
+
113
+ if (displayInfo.connection !== undefined) {
114
+ this.connection = displayInfo.connection;
115
+ }
116
+
117
+ // If the connection is disconnected/unknown, reset all the display properties
118
+ if (this.connection !== this.DisplayConnectionStatus.CONNECTED) {
119
+ this.selected = undefined;
120
+ this.resolution = undefined;
121
+ this.framerate = undefined;
122
+ this.security = undefined;
123
+ this.make = undefined;
124
+ this.model = undefined;
125
+ return;
126
+ }
127
+
128
+ for (const [key, value] of Object.entries(displayInfo)) {
129
+ if (value !== undefined) {
130
+ this[key] = value;
131
+ }
132
+ }
133
+ }
134
+
135
+ _parseDisplayInfo(displayInfoResponseStr) {
136
+ const displayInfoResponse = JSON.parse(displayInfoResponseStr);
137
+
138
+ const displayInfo = {
139
+ connection: displayInfoResponse?.connection,
140
+ selected: displayInfoResponse?.connectedDisplayInfo?.selected,
141
+ resolution: displayInfoResponse?.connectedDisplayInfo?.resolution,
142
+ framerate: displayInfoResponse?.connectedDisplayInfo?.framerate,
143
+ security: displayInfoResponse?.connectedDisplayInfo?.hdcpVersion
144
+ };
145
+
146
+
147
+ if (displayInfoResponse?.connectedDisplayInfo?.edid) {
148
+ try {
149
+ const { manufacturerName, modelName } = parseEdid(displayInfoResponse.connectedDisplayInfo.edid);
150
+ displayInfo.make = manufacturerName ? manufacturerName : "unknown";
151
+ displayInfo.model = modelName ? modelName : "unknown";
152
+ } catch (error) {
153
+ sdkLogger.error("Failed to parse EDID", error);
154
+ displayInfo.make = "unknown";
155
+ displayInfo.model = "unknown";
156
+ }
157
+ }
158
+ return displayInfo;
159
+ }
160
+
161
+ addEventListener(type, callback) {
162
+ this._eventManager.addEventListener(type, callback);
163
+ }
164
+
165
+ removeEventListener(type, callback) {
166
+ this._eventManager.removeEventListener(type, callback);
167
+ }
168
+ }
169
+
170
+
171
+ /**
172
+ * @module
173
+ * @type {DisplayManager}
174
+ * @example
175
+ * import { displayManager } from "senza-sdk";
176
+ * const connection = await displayManager.connection;
177
+ * console.info(connection);
178
+ *
179
+ * @return {DisplayManager} pointer to the DisplayManager singleton
180
+ */
181
+ export const displayManager = new DisplayManager();
@@ -0,0 +1,125 @@
1
+ const MANUFACTURER_MAP = {
2
+ // Major PC & Display Brands
3
+ ACR: "Acer",
4
+ APP: "Apple",
5
+ AOC: "AOC",
6
+ AUO: "AU Optronics",
7
+ BNQ: "BenQ",
8
+ BOE: "BOE Technology",
9
+ CMN: "Innolux (Chimei Innolux)",
10
+ CMO: "Chi Mei Optoelectronics",
11
+ CPQ: "Compaq",
12
+ DELL: "Dell", // some EDIDs use DEL
13
+ DEL: "Dell",
14
+ EIZ: "EIZO",
15
+ FUS: "Fujitsu",
16
+ HPN: "HP",
17
+ HWP: "HP",
18
+ IVM: "Iiyama",
19
+ LEN: "Lenovo",
20
+ LPL: "LG Philips LCD",
21
+ LGE: "LG Electronics",
22
+ GSM: "LG Electronics (GoldStar)",
23
+ MEI: "Panasonic (Matsushita)",
24
+ NEC: "NEC",
25
+ NVD: "Nvidia",
26
+ PHL: "Philips",
27
+ PHI: "Philips",
28
+ SAM: "Samsung",
29
+ SNY: "Sony",
30
+ TOS: "Toshiba",
31
+ VSC: "ViewSonic",
32
+ VIZ: "Vizio",
33
+
34
+ // Panel Makers / ODMs
35
+ CPT: "Chunghwa Picture Tubes",
36
+ HSD: "HannStar Display",
37
+ QDS: "Quanta Display",
38
+ SEC: "Samsung Electronics Display",
39
+ SHP: "Sharp",
40
+ TMX: "Tianma",
41
+ CMI: "Chi Mei Innolux",
42
+
43
+ // Other common consumer brands
44
+ JVC: "JVC (Victor Company of Japan)",
45
+ PIO: "Pioneer",
46
+ FUN: "Funai",
47
+ GRN: "Grundig",
48
+ MAG: "MAG Innovision",
49
+ TCL: "TCL",
50
+ HAI: "Haier",
51
+ HIT: "Hitachi",
52
+ SMC: "Sumitomo",
53
+ WAC: "Wacom",
54
+ ASUS: "ASUSTek", // often ASU
55
+ ASU: "ASUS",
56
+ GIG: "Gigabyte",
57
+ MSI: "Micro-Star International",
58
+
59
+ // Catch-alls / misc seen in monitors
60
+ ENC: "Energizer",
61
+ ZOW: "Zowie",
62
+ TTX: "Tatung",
63
+ KDS: "KDS",
64
+ HIS: "HIS",
65
+ XXX: "TCL",
66
+ XMD: "Xiaomi"
67
+ };
68
+
69
+ // --- helpers ---
70
+ function hexToBytes(hex) {
71
+ const clean = hex.replace(/[^0-9a-fA-F]/g, "");
72
+ if (clean.length % 2 !== 0) throw new Error("Hex length must be even");
73
+ const out = new Uint8Array(clean.length / 2);
74
+ for (let i = 0; i < clean.length; i += 2) {
75
+ out[i / 2] = parseInt(clean.slice(i, i + 2), 16);
76
+ }
77
+ return out;
78
+ }
79
+
80
+ function decodeEisaId(word) {
81
+ const a = (word >> 10 & 0x1f) + 64;
82
+ const b = (word >> 5 & 0x1f) + 64;
83
+ const c = (word & 0x1f) + 64;
84
+ return [a, b, c].map(n => n >= 65 && n <= 90 ? String.fromCharCode(n) : "?").join("");
85
+ }
86
+
87
+ function sanitizeEdidString(bytes) {
88
+ let out = "";
89
+ for (const b of bytes) {
90
+ if (b === 0x00 || b === 0x0a) break;
91
+ if (b >= 0x20 && b <= 0x7e) out += String.fromCharCode(b);
92
+ }
93
+ return out.trim();
94
+ }
95
+
96
+
97
+ export function parseEdid(input) {
98
+ const edid = input instanceof Uint8Array ? input : hexToBytes(input);
99
+
100
+ // Manufacturer ID (bytes 8–9)
101
+ const mfgWord = edid[8] << 8 | edid[9];
102
+ const manufacturerId = decodeEisaId(mfgWord);
103
+ const manufacturerName = MANUFACTURER_MAP[manufacturerId] || manufacturerId;
104
+
105
+ // Product code (bytes 10–11)
106
+ const productCode = (edid[10] | edid[11] << 8) >>> 0;
107
+ const modelNumber = String(productCode);
108
+
109
+ // Monitor name descriptor (0xFC) and serial number descriptor (0xFF) in base block
110
+ let modelName;
111
+ let serialNumber;
112
+ for (let off = 0x36; off <= 0x7e - 18; off += 18) {
113
+ const d = edid.slice(off, off + 18);
114
+ if (d[0] === 0x00 && d[1] === 0x00 && d[2] === 0x00) {
115
+ if (d[3] === 0xFC) {
116
+ modelName = sanitizeEdidString(d.slice(5, 18));
117
+ } else if (d[3] === 0xFF) {
118
+ serialNumber = sanitizeEdidString(d.slice(5, 18));
119
+ }
120
+ }
121
+ }
122
+
123
+ return { manufacturerName, modelName, modelNumber, serialNumber };
124
+ }
125
+
@@ -62,6 +62,12 @@ class Lifecycle extends LifecycleInterface {
62
62
  this._autoBackgroundOnUIDelay = DEFAULT_AUTO_BACKGROUND_UI_DELAY;
63
63
  }
64
64
 
65
+ // List of event types that should use the _eventManager
66
+ static _waitForListenersEvents = [
67
+ "userdisconnected","beforestatechange"
68
+ // Add more event types here if needed in the future
69
+ ];
70
+
65
71
  /**
66
72
  * @private Initialize the lifecycle
67
73
  * @param {Object} uiStreamerSettings - UI-streamer portion of the settings taken from session info
@@ -177,7 +183,7 @@ class Lifecycle extends LifecycleInterface {
177
183
  const event = new Event("onstatechange");
178
184
  event.state = e.detail;
179
185
  this._state = event.state;
180
- if (this._isAutoBackgroundEnabled() && this.state === this.UiState.FOREGROUND) {
186
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
181
187
  this._startCountdown();
182
188
  }
183
189
  this.dispatchEvent(event);
@@ -213,7 +219,7 @@ class Lifecycle extends LifecycleInterface {
213
219
 
214
220
  // Add playModeChange listener
215
221
  remotePlayer.addEventListener("playModeChange", (event) => {
216
- if (this._isAutoBackgroundEnabled() && this.state === this.UiState.FOREGROUND) {
222
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
217
223
  sdkLogger.log("Resetting auto background timer due to play mode change", event.detail.isPlaying);
218
224
  this._startCountdown(event.detail.isPlaying);
219
225
  }
@@ -221,6 +227,14 @@ class Lifecycle extends LifecycleInterface {
221
227
  sdkLogger.log("[Lifecycle] Added event listeners for system events");
222
228
  }
223
229
 
230
+ /**
231
+ * @private Checks if the UI is in foreground or transitioning to foreground.
232
+ * @returns {boolean}
233
+ */
234
+ _isForegroundOrTransitioning() {
235
+ return this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND;
236
+ }
237
+
224
238
  /**
225
239
  * @private Checks if auto background is enabled including overrides.
226
240
  * @returns {boolean}
@@ -284,7 +298,7 @@ class Lifecycle extends LifecycleInterface {
284
298
  }
285
299
 
286
300
  // Start or stop countdown based on new configuration
287
- if (this._isAutoBackgroundEnabled()) {
301
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
288
302
  this._startCountdown();
289
303
  } else {
290
304
  this._stopCountdown();
@@ -364,7 +378,7 @@ class Lifecycle extends LifecycleInterface {
364
378
 
365
379
  set autoBackground(enabled) {
366
380
  this._autoBackground = enabled;
367
- if (this._isAutoBackgroundEnabled()) {
381
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
368
382
  this._startCountdown();
369
383
  } else {
370
384
  this._stopCountdown();
@@ -377,7 +391,7 @@ class Lifecycle extends LifecycleInterface {
377
391
 
378
392
  set autoBackgroundDelay(delay) {
379
393
  this._autoBackgroundOnVideoDelay = delay;
380
- if (this._isAutoBackgroundEnabled() && remotePlayer._isPlaying) {
394
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning() && remotePlayer._isPlaying) {
381
395
  this._startCountdown();
382
396
  }
383
397
  }
@@ -388,7 +402,7 @@ class Lifecycle extends LifecycleInterface {
388
402
 
389
403
  set autoBackgroundOnUIDelay(delay) {
390
404
  this._autoBackgroundOnUIDelay = delay;
391
- if (this._isAutoBackgroundEnabled() && !remotePlayer._isPlaying) {
405
+ if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning() && !remotePlayer._isPlaying) {
392
406
  this._startCountdown();
393
407
  }
394
408
  }
@@ -408,9 +422,24 @@ class Lifecycle extends LifecycleInterface {
408
422
  // Only start countdown if delay is positive
409
423
  if (timeoutDelay > 0) {
410
424
  sdkLogger.debug("Starting countdown timeout", timeoutDelay, isPlaying ? "(video)" : "(UI)");
411
- this._countdown = setTimeout(() => {
425
+ this._countdown = setTimeout(async () => {
412
426
  sdkLogger.debug("Countdown timeout reached, moving to background");
413
- this.moveToBackground();
427
+
428
+ // Create the event with cancelable: true
429
+ const event = new Event("beforestatechange", { cancelable: true });
430
+ event.state = this.UiState.BACKGROUND;
431
+ // Use the event manager to dispatch the event and wait for all listeners
432
+ await this._eventManager.dispatch("beforestatechange", event);
433
+ // Check if any listener called preventDefault()
434
+ if (event.defaultPrevented) {
435
+ sdkLogger.info("moveToBackground was prevented by a listener");
436
+ // Restart the countdown since the move was cancelled
437
+ if (this._isForegroundOrTransitioning()) {
438
+ this._startCountdown();
439
+ }
440
+ } else {
441
+ this.moveToBackground();
442
+ }
414
443
  }, timeoutDelay * 1000);
415
444
  } else {
416
445
  sdkLogger.debug("Countdown not started - delay is negative or zero");
@@ -424,8 +453,8 @@ class Lifecycle extends LifecycleInterface {
424
453
  if (this._countdown) {
425
454
  sdkLogger.debug("Stopping countdown timer");
426
455
  clearTimeout(this._countdown);
456
+ this._countdown = null;
427
457
  }
428
- this._countdown = null;
429
458
  }
430
459
 
431
460
  /**
@@ -459,7 +488,7 @@ class Lifecycle extends LifecycleInterface {
459
488
  moveToForeground() {
460
489
  if (window.cefQuery) {
461
490
  const inTransition = this._isInTransition();
462
- if (inTransition || this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND) {
491
+ if (inTransition || this._isForegroundOrTransitioning()) {
463
492
  sdkLogger.warn(`lifecycle moveToForeground: No need to transition to foreground, state: ${this._state} transition: ${inTransition}`);
464
493
  return Promise.resolve(false);
465
494
  }
@@ -771,23 +800,19 @@ class Lifecycle extends LifecycleInterface {
771
800
  return Promise.reject("disconnect is not supported if NOT running e2e");
772
801
  }
773
802
 
803
+
774
804
  addEventListener(type, listener, options) {
775
- if (type === "userdisconnected") {
776
- // Use the event manager for userdisconnected events
805
+ if (Lifecycle._waitForListenersEvents.includes(type)) {
777
806
  this._eventManager.addEventListener(type, listener);
778
807
  } else {
779
- // For all other event types, use the parent class implementation
780
808
  super.addEventListener(type, listener, options);
781
809
  }
782
810
  }
783
811
 
784
-
785
812
  removeEventListener(type, listener, options) {
786
- if (type === "userdisconnected") {
787
- // Use the event manager for userdisconnected events
813
+ if (Lifecycle._waitForListenersEvents.includes(type)) {
788
814
  this._eventManager.removeEventListener(type, listener);
789
815
  } else {
790
- // For all other event types, use the parent class implementation
791
816
  super.removeEventListener(type, listener, options);
792
817
  }
793
818
  }
@@ -246,6 +246,7 @@ class RemotePlayer extends RemotePlayerInterface {
246
246
 
247
247
  typeof document !== "undefined" && document.addEventListener("hs/EOS", () => {
248
248
  sdkLogger.info("Got hs/EOS event");
249
+ this._changePlayMode(false);
249
250
  this.dispatchEvent(new Event("ended"));
250
251
  });
251
252
 
@@ -790,10 +791,12 @@ class RemotePlayer extends RemotePlayerInterface {
790
791
  fcid: FCID
791
792
  };
792
793
  if (this._uiAvSyncIntervalMs !== undefined) {
793
- message.uiAvSyncIntervalMs = this._uiAvSyncIntervalMs;
794
+ message.connectorSettings = message.connectorSettings || {};
795
+ message.connectorSettings.uiAvSyncIntervalMs = this._uiAvSyncIntervalMs;
794
796
  }
795
797
  if (this._abrAvSyncIntervalMs !== undefined) {
796
- message.abrAvSyncIntervalMs = this._abrAvSyncIntervalMs;
798
+ message.connectorSettings = message.connectorSettings || {};
799
+ message.connectorSettings.abrAvSyncIntervalMs = this._abrAvSyncIntervalMs;
797
800
  }
798
801
  if (this._remotePlayerApiVersion >= 2) {
799
802
  message.type = "remotePlayer.load";
@@ -1,5 +1,6 @@
1
1
  export { Lifecycle } from "./lifecycle.js";
2
2
  export { DeviceManager } from "./deviceManager.js";
3
+ export { DisplayManager } from "./displayManager.js";
3
4
  export { PlatformManager } from "./platformManager.js";
4
5
  export { AlarmManager } from "./alarmManager.js";
5
6
  export { MessageManager } from "./messageManager.js";
@@ -1,32 +1,9 @@
1
1
  import { sdkLogger, noop } from "./utils.js";
2
2
 
3
- /**
4
- * @event DeviceManager#hdmistatuschanged
5
- * @description Fired when the HDMI connection status changes.<br>
6
- * @property {HdmiStatus} hdmiStatus - the status of the HDMI connection.
7
- * @example
8
- * deviceManager.addEventListener("hdmistatuschanged", (e) => {
9
- * console.info("HDMI status changed:", e.detail.hdmiStatus);
10
- * });
11
- * @private
12
- */
13
-
14
3
  /**
15
4
  * DeviceManager is a singleton class that manages the device.<br>
16
- * @fires hdmistatuschanged
17
5
  */
18
6
  export class DeviceManager extends EventTarget {
19
- /**
20
- * @typedef {Object} HdmiStatus - The HDMI status of the device
21
- * @property {string} ACTIVE - The device is connected to the active HDMI source.
22
- * @property {string} INACTIVE - The active HDMI source is different than the one connected to the device or the TV is in standby.
23
- * @property {string} UNKNOWN - The status is unknown. This can happen if the TV does not support HDMI status detection.
24
- */
25
- HdmiStatus = Object.freeze({
26
- ACTIVE: "active",
27
- INACTIVE: "inactive",
28
- UNKNOWN: "unknown"
29
- });
30
7
 
31
8
  /**
32
9
  * @property {object} DeviceInfo
@@ -54,16 +31,6 @@ export class DeviceManager extends EventTarget {
54
31
 
55
32
  }
56
33
 
57
- /**
58
- * Get the current HDMI status from the connector
59
- * @return {Promise<HdmiStatus>} Promise which is resolved when getHdmiStatus has been successfully performed
60
- * Failure to getHdmiStatus for any reason, results in the promise being rejected.
61
- * @private
62
- */
63
- getHdmiStatus() {
64
- return noop("DeviceManager.getHdmiStatus");
65
- }
66
-
67
34
  /**
68
35
  * Reboot the device
69
36
  * @return {Promise} Promise which is resolved when the reboot command has been successfully processed.
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @event DisplayManager#displayinfochanged
3
+ * @description Fired when the display information changes.
4
+ * @property {DisplayInfoChanged} [detail.displayInfo] - the updated display information.
5
+ * @example
6
+ * @private
7
+ * displayManager.addEventListener("displayinfochanged", (e) => {
8
+ * console.info("Display information changed:", e.detail.displayInfo);
9
+ * });
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} DisplayInfoChanged
14
+ * @property {DisplayConnectionStatus | undefined} connection Display connection status.
15
+ * @property {DisplaySelectedStatus | undefined} selected Display selected status.
16
+ * @property {DisplayResolution | undefined} resolution Display resolution information.
17
+ * @property {number | undefined} framerate Display frame rate.
18
+ * @property {string | undefined} security Display HDCP / security level.
19
+ * @property {string | undefined} make Display vendor (make) information.
20
+ * @property {string | undefined} model Display model information.
21
+ * @private
22
+ */
23
+
24
+ /**
25
+ * DisplayManager exposes information about the connected display / sink device.
26
+ * It allows querying several negotiated HDMI parameters and (for a subset) receiving change notifications.
27
+ * @private
28
+ * @fires displayinfochanged
29
+ */
30
+ export class DisplayManager extends EventTarget {
31
+ /**
32
+ * @typedef {Object} DisplayConnectionStatus
33
+ * @property {string} CONNECTED Display is connected and communication established.
34
+ * @property {string} DISCONNECTED Display cable removed / not detected.
35
+ * @property {string} UNKNOWN The platform cannot determine (e.g. unsupported).
36
+ * @private
37
+ */
38
+ DisplayConnectionStatus = Object.freeze({
39
+ CONNECTED: "connected",
40
+ DISCONNECTED: "disconnected",
41
+ UNKNOWN: "unknown"
42
+ });
43
+
44
+ /**
45
+ * @typedef {Object} DisplaySelectedStatus
46
+ * @property {string} ACTIVE The display input where the device is connected is currently active.
47
+ * @property {string} INACTIVE The device is connected but another source is active / TV in standby.
48
+ * @property {string} UNKNOWN The platform cannot determine (e.g. unsupported).
49
+ * @private
50
+ */
51
+ DisplaySelectedStatus = Object.freeze({
52
+ ACTIVE: "active",
53
+ INACTIVE: "inactive",
54
+ UNKNOWN: "unknown"
55
+ });
56
+
57
+ /**
58
+ * @typedef {Object} DisplayResolution
59
+ * @property {number} width Negotiated active horizontal pixels
60
+ * @property {number} height Negotiated active vertical pixels
61
+ * @property {boolean} interlace True if the negotiated mode is interlaced
62
+ * @private
63
+ */
64
+
65
+ /**
66
+ * Display connection status.
67
+ * @property {DisplayConnectionStatus} connection
68
+ */
69
+ connection = this.DisplayConnectionStatus.DISCONNECTED;
70
+
71
+ /**
72
+ * Whether the display input this device is connected to is currently active.
73
+ * undefined when the display is not connected.
74
+ * @property {DisplaySelectedStatus} selected
75
+ */
76
+ selected;
77
+
78
+ /**
79
+ * Negotiated resolution information.
80
+ * undefined when the display is not connected.
81
+ * @property {DisplayResolution} resolution
82
+ */
83
+ resolution;
84
+
85
+ /**
86
+ * Negotiated frame rate (frames per second).
87
+ * undefined when the display is not connected.
88
+ * @property {number} framerate
89
+ */
90
+ framerate;
91
+
92
+ /**
93
+ * Negotiated HDCP / security level.
94
+ * undefined when the display is not connected.
95
+ * @property {string} security
96
+ */
97
+ security;
98
+
99
+ /**
100
+ * Vendor (make) information parsed from EDID.
101
+ * undefined when the display is not connected.
102
+ * @property {string} make
103
+ */
104
+ make;
105
+
106
+ /**
107
+ * Model information parsed from EDID.
108
+ * undefined when the display is not connected.
109
+ * @property {string} model
110
+ */
111
+ model;
112
+ }
113
+
114
+
115
+ /**
116
+ * @module
117
+ * @type {DisplayManager}
118
+ * @private
119
+ * @example
120
+ * import { displayManager } from "senza-sdk";
121
+ * const connection = await displayManager.connection;
122
+ * console.info(connection);
123
+ *
124
+ * @return {DisplayManager} pointer to the DisplayManager singleton
125
+ */
126
+ "needed for the module doc comment to be recognized";