senza-sdk 4.4.2-efcb53d.0 → 4.4.4-86e7d7a.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "senza-sdk",
3
- "version": "4.4.2-efcb53d.0",
3
+ "version": "4.4.4-86e7d7a.0",
4
4
  "main": "./src/api.js",
5
5
  "description": "API for Senza application",
6
6
  "license": "MIT",
@@ -2,6 +2,7 @@ import { AlarmManager as AlarmManagerInterface } from "../interface/alarmManager
2
2
  import { getFCID, sdkLogger } from "./utils.js";
3
3
  import { EventListenersManager } from "./eventListenersManager.js";
4
4
  import { lifecycle } from "./lifecycle.js";
5
+ import { bus, Events } from "./eventBus";
5
6
 
6
7
  class AlarmManager extends AlarmManagerInterface {
7
8
  constructor() {
@@ -38,6 +39,8 @@ class AlarmManager extends AlarmManagerInterface {
38
39
  * @private Add event listeners for system events
39
40
  */
40
41
  _addSenzaEventListeners() {
42
+ bus.addEventListener(Events.LifecycleForeground, () => this._moveToForegroundCalled());
43
+
41
44
  typeof document !== "undefined" && document.addEventListener("hs/alarmFiredEvent", async (e) => {
42
45
 
43
46
  if (e.detail?.alarmName) {
@@ -100,6 +100,12 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
100
100
  alarmManager._setDefaultTimeout(alarmTimeout);
101
101
  }
102
102
 
103
+ // Set default device manager event timeout using UI-Streamer settings
104
+ const deviceManagerEventTimeout = sessionInfoObj?.settings?.["ui-streamer"]?.deviceManagerEventTimeout;
105
+ if (deviceManagerEventTimeout) {
106
+ deviceManager._setDefaultTimeout(deviceManagerEventTimeout);
107
+ }
108
+
103
109
  // Get trigger event
104
110
  const triggerEventStr = await new Promise((resolve) => {
105
111
  const FCID = getFCID();
@@ -133,6 +139,7 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
133
139
  await remotePlayer._init(sessionInfoObj, triggerEvent);
134
140
  alarmManager._init();
135
141
  messageManager._init();
142
+ deviceManager._init();
136
143
  sdkLogger.log("All submodules initialized");
137
144
 
138
145
 
@@ -1,13 +1,16 @@
1
1
  import { DeviceManager as DeviceManagerInterface } from "../interface/deviceManager";
2
- import { getFCID, sdkLogger, getRestResponse } from "./utils";
2
+ import { getFCID, sdkLogger, getRestResponse, clearTimer, SenzaError } from "./utils";
3
3
  import { sessionInfo } from "./SessionInfo";
4
+ import { EventListenersManager } from "./eventListenersManager";
5
+ import { bus, Events } from "./eventBus";
6
+ import { lifecycle } from "./lifecycle";
4
7
 
5
8
  let wifi_ap_data;
6
9
  let wifi_status;
7
10
  let wifi_status_last_update = 0;
8
11
 
9
12
  const WIFI_STATUS_CACHE_SECONDS = 5;
10
- const FACTORY_RESET_TIMEOUT_SECONDS = 5;
13
+ const API_CONFIRMATION_TIMEOUT_MS = 5000;
11
14
 
12
15
  async function getWifiApData() {
13
16
  // Wi-Fi access point data is static, so it needs to be retrieved only once
@@ -38,6 +41,134 @@ class DeviceManager extends DeviceManagerInterface {
38
41
 
39
42
  constructor() {
40
43
  super();
44
+
45
+ /**
46
+ * Mapping of CEC active source status to HdmiStatus.
47
+ * @type {Object}
48
+ * @private
49
+ */
50
+ this._cecActiveSourceStatusMap = Object.freeze({
51
+ active: this.HdmiStatus.ACTIVE,
52
+ inactive: this.HdmiStatus.INACTIVE,
53
+ unknown: this.HdmiStatus.UNKNOWN
54
+ });
55
+
56
+ /**
57
+ * Event listeners manager for the deviceManager events
58
+ * @type {EventListenersManager}
59
+ * @private
60
+ */
61
+ this._eventManager = new EventListenersManager({
62
+ timeoutMs: 10000 // Default timeout of 10 seconds, can be overridden by _setDefaultTimeout
63
+ });
64
+
65
+ /**
66
+ * @type {boolean}
67
+ * @private
68
+ */
69
+ this._isInitialized = false;
70
+ }
71
+
72
+ /**
73
+ * @private
74
+ */
75
+ _init() {
76
+ sdkLogger.log("Initializing DeviceManager");
77
+ if (!this._isInitialized) {
78
+ this._isInitialized = true;
79
+ this._addSenzaEventListeners();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Translate the HdmiStatus information to a single HdmiStatus for the application
85
+ * 1) cecStatus is a configuration setting from the client. If false, we always return UNKNOWN to the application
86
+ * 2) If cecStatus is true, we check the hdmiStatus. If not "connected", the HDMI status is definitely INACTIVE.
87
+ * 3) If hdmiStatus is "connected" and cecStatus is true, we rely on the cecActiveSourceStatus to determine the HdmiStatus
88
+ * @private
89
+ */
90
+ _translateHdmiStatus(hdmiStatusStr) {
91
+ const hdmiStatusObj = JSON.parse(hdmiStatusStr ?? "{}"); // Object containing the 3 statuses
92
+ let hdmiStatus = this.HdmiStatus.UNKNOWN;
93
+ if (hdmiStatusObj?.cecStatus) {
94
+ if (hdmiStatusObj.hdmiStatus !== "connected") {
95
+ hdmiStatus = this.HdmiStatus.INACTIVE;
96
+ } else {
97
+ const cecActive = this._cecActiveSourceStatusMap[hdmiStatusObj.cecActiveSourceStatus];
98
+ hdmiStatus = cecActive ?? this.HdmiStatus.UNKNOWN;
99
+ if (!cecActive) {
100
+ sdkLogger.warn(`Unknown CEC active source status: ${hdmiStatusObj.cecActiveSourceStatus}`);
101
+ }
102
+ }
103
+ } else {
104
+ sdkLogger.warn("cec is disabled or no hdmiStatus");
105
+ }
106
+ sdkLogger.log("HDMI status is:", hdmiStatus);
107
+ return hdmiStatus;
108
+ }
109
+
110
+ /**
111
+ * @private Add event listeners for system events
112
+ */
113
+ _addSenzaEventListeners() {
114
+ bus.addEventListener(Events.LifecycleForeground, () => {
115
+ this._moveToForegroundHasBeenCalled = true;
116
+ });
117
+
118
+ typeof document !== "undefined" && document.addEventListener("hs/hdmiStatusChanged", async (event) => {
119
+ sdkLogger.info("Got hs/hdmiStatusChanged event with detail", JSON.stringify(event?.detail));
120
+
121
+ const hdmiStatus = this._translateHdmiStatus(event?.detail?.hdmiStatus);
122
+
123
+ const timeBeforeCallbacks = Date.now();
124
+
125
+ // Dispatch event to application and allow a chance to move to foreground.
126
+ // If there are no callbacks or the application doesn't move to foreground, the UI will be disconnected.
127
+ await this._eventManager.dispatch("hdmistatuschanged", {hdmiStatus});
128
+
129
+ const callbackDuration = Date.now() - timeBeforeCallbacks;
130
+ sdkLogger.log(`All callbacks for hdmiStatusChanged are finished within ${callbackDuration}ms`);
131
+ const isTriggering = lifecycle.triggerEvent.type === "hdmiStatusChanged" && lifecycle._triggerEventFcid && lifecycle._triggerEventFcid === event.detail.fcid;
132
+ if (isTriggering) {
133
+ if (!this._moveToForegroundHasBeenCalled && window.cefQuery) {
134
+ sdkLogger.log("Application is about to be disconnected since didn't move to foreground");
135
+ const message = { type: "disconnect" };
136
+ const request = { target: "TC", waitForResponse: false, message: JSON.stringify(message) };
137
+ window.cefQuery({
138
+ request: JSON.stringify(request),
139
+ persistent: false,
140
+ onSuccess: () => {
141
+ sdkLogger.log("disconnect request successfully sent");
142
+ },
143
+ onFailure: (code, msg) => {
144
+ sdkLogger.error(`disconnect request failed: ${code} ${msg}`);
145
+ }
146
+ });
147
+ }
148
+ }
149
+ });
150
+ }
151
+
152
+ /**
153
+ * Set the default timeout for device manager event listeners
154
+ * @param {number} timeout - Timeout in milliseconds for device manager event listeners
155
+ * @private
156
+ */
157
+ _setDefaultTimeout(timeout) {
158
+ if (typeof timeout === "number" && timeout > 0) {
159
+ this._eventManager.timeoutMs = timeout;
160
+ sdkLogger.log(`DeviceManager event listener timeout set to ${timeout}ms`);
161
+ } else {
162
+ sdkLogger.warn(`Invalid timeout value: ${timeout}. Must be a positive number.`);
163
+ }
164
+ }
165
+
166
+ addEventListener(type, callback) {
167
+ this._eventManager.addEventListener(type, callback);
168
+ }
169
+
170
+ removeEventListener(type, callback) {
171
+ this._eventManager.removeEventListener(type, callback);
41
172
  }
42
173
 
43
174
  get deviceInfo() {
@@ -57,6 +188,46 @@ class DeviceManager extends DeviceManagerInterface {
57
188
  return super.deviceInfo;
58
189
  }
59
190
 
191
+ getHdmiStatus() {
192
+ if (window.cefQuery) {
193
+ const FCID = getFCID();
194
+ const message = {
195
+ type: "getHdmiStatus",
196
+ fcid: FCID
197
+ };
198
+ const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
199
+ sdkLogger.log("Sending getHdmiStatus request");
200
+ return new Promise((resolve, reject) => {
201
+ let timerId = 0;
202
+ const timeBeforeSendingRequest = Date.now();
203
+ const queryId = window.cefQuery({
204
+ request: JSON.stringify(request),
205
+ persistent: false,
206
+ onSuccess: (data) => {
207
+ timerId = clearTimer(timerId);
208
+ const duration = Date.now() - timeBeforeSendingRequest;
209
+ sdkLogger.withFields({ duration }).log(`getHdmiStatus completed successfully after ${duration} ms with data: ${data}`);
210
+ const hdmiStatus = this._translateHdmiStatus(data ? data : "{}");
211
+ resolve(hdmiStatus);
212
+ },
213
+ onFailure: (code, msg) => {
214
+ const duration = Date.now() - timeBeforeSendingRequest;
215
+ sdkLogger.withFields({ duration }).log(`getHdmiStatus failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
216
+ timerId = clearTimer(timerId);
217
+ reject(new SenzaError(code, msg));
218
+ }
219
+ });
220
+ sdkLogger.log(`window.cefQuery for getHdmiStatus returned query id ${queryId}`);
221
+ const timeout = API_CONFIRMATION_TIMEOUT_MS + 1000;
222
+ timerId = setTimeout(() => {
223
+ sdkLogger.log(`getHdmiStatus reached timeout of ${timeout} ms, canceling query id ${queryId}`);
224
+ window.cefQueryCancel(queryId);
225
+ reject(new SenzaError(6000, `getHdmiStatus reached timeout of ${timeout} ms`));
226
+ }, timeout, queryId);
227
+ });
228
+ }
229
+ }
230
+
60
231
  reboot() {
61
232
  return new Promise((resolve, reject) => {
62
233
  if (window.cefQuery) {
@@ -173,12 +344,12 @@ class DeviceManager extends DeviceManagerInterface {
173
344
  reject(`factoryReset failed: ${code} ${msg}`);
174
345
  }
175
346
  });
176
-
347
+ const timeout = API_CONFIRMATION_TIMEOUT_MS + 1000;
177
348
  timeoutHandler = setTimeout(() => {
178
- logger.error(`factoryReset failed: reached timeout of ${FACTORY_RESET_TIMEOUT_SECONDS * 1000} ms`);
349
+ logger.error(`factoryReset failed: reached timeout of ${timeout} ms`);
179
350
  window.cefQueryCancel(queryId);
180
- reject(`factoryReset failed: reached timeout of ${FACTORY_RESET_TIMEOUT_SECONDS * 1000} ms`);
181
- }, FACTORY_RESET_TIMEOUT_SECONDS * 1000);
351
+ reject(`factoryReset failed: reached timeout of ${timeout} ms`);
352
+ }, timeout);
182
353
  });
183
354
  }
184
355
 
@@ -0,0 +1,5 @@
1
+ class EventBus extends EventTarget {}
2
+ export const bus = new EventBus();
3
+ export const Events = {
4
+ LifecycleForeground: "lifecycle:foreground"
5
+ };
@@ -14,6 +14,7 @@ import {
14
14
  import { EventListenersManager } from "./eventListenersManager.js";
15
15
  import { sessionInfo } from "./SessionInfo.js";
16
16
  import { DEFAULT_REMOTE_PLAYER_CONFIRMATION_TIMEOUT, remotePlayer } from "./remotePlayer.js";
17
+ import {bus, Events} from "./eventBus";
17
18
 
18
19
  // Default values for autoBackground settings. These values are used if the UIStreamer settings are not provided.
19
20
  const DEFAULT_AUTO_BACKGROUND_VIDEO_DELAY = 30;
@@ -136,6 +137,8 @@ class Lifecycle extends LifecycleInterface {
136
137
  this._triggerEvent.data = { eventCode: triggerEvent.eventCode, errorCode: triggerEvent.errorCode };
137
138
  } else if (triggerEvent.type === "getLicense") {
138
139
  sdkLogger.info("The license request is available on the license-request event which is triggered after uiReady is called");
140
+ } else if (triggerEvent.type === "hdmiStatusChanged") {
141
+ sdkLogger.info("The hdmiStatusChanged data is sent in the callback after uiReady is called");
139
142
  }
140
143
  }
141
144
  if (triggerEvent.fcid) {
@@ -461,7 +464,7 @@ class Lifecycle extends LifecycleInterface {
461
464
  return Promise.resolve(false);
462
465
  }
463
466
  this._inTransitionToForeground = true;
464
- alarmManager._moveToForegroundCalled();
467
+ bus.dispatchEvent(new Event(Events.LifecycleForeground));
465
468
  const FCID = getFCID();
466
469
  if (this._remotePlayerApiVersion >= 2) {
467
470
  // Only update to playing UI if we started seeking in ABR. But, if we are seeking while already paused, keep the target seek state as is.
@@ -1,9 +1,33 @@
1
1
  import { sdkLogger, noop } from "./utils.js";
2
2
 
3
3
  /**
4
- * DeviceManager is a singleton class that manages the device
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
+ /**
15
+ * DeviceManager is a singleton class that manages the device.<br>
16
+ * @fires hdmistatuschanged
5
17
  */
6
18
  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
  * @property {object} DeviceInfo
9
33
  * @property {string} DeviceInfo.deviceId
@@ -30,6 +54,16 @@ export class DeviceManager extends EventTarget {
30
54
 
31
55
  }
32
56
 
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
+
33
67
  /**
34
68
  * Reboot the device
35
69
  * @return {Promise} Promise which is resolved when the reboot command has been successfully processed.
@@ -1 +1 @@
1
- export const version = "4.4.2-efcb53d.0";
1
+ export const version = "4.4.4-86e7d7a.0";