senza-sdk 4.4.5 → 4.4.7
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/dist/bundle.js +1 -1
- package/dist/implementation.bundle.js +1 -1
- package/package.json +2 -2
- package/src/implementation/deviceManager.js +254 -3
- package/src/implementation/lifecycle.js +326 -7
- package/src/interface/deviceManager.js +126 -0
- package/src/interface/lifecycle.js +30 -4
- package/src/interface/version.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "senza-sdk",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.7",
|
|
4
4
|
"main": "./src/api.js",
|
|
5
5
|
"description": "API for Senza application",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"eslint-plugin-jest": "^28.11.0",
|
|
35
35
|
"globals": "^16.0.0",
|
|
36
36
|
"jest": "^30.2.0",
|
|
37
|
-
"jest-environment-jsdom"
|
|
37
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
38
38
|
"jsdoc-to-markdown": "^7.1.1",
|
|
39
39
|
"webpack": "^5.72.1",
|
|
40
40
|
"webpack-cli": "^5.1.4"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeviceManager as DeviceManagerInterface
|
|
1
|
+
import { DeviceManager as DeviceManagerInterface} from "../interface/deviceManager";
|
|
2
2
|
import { getFCID, sdkLogger, getRestResponse } from "./utils";
|
|
3
3
|
import { sessionInfo } from "./SessionInfo";
|
|
4
4
|
|
|
@@ -9,6 +9,186 @@ let wifi_status_last_update = 0;
|
|
|
9
9
|
const WIFI_STATUS_CACHE_SECONDS = 5;
|
|
10
10
|
const FACTORY_RESET_TIMEOUT_SECONDS = 5;
|
|
11
11
|
|
|
12
|
+
const byteSize = str => new Blob([str]).size;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* MessageChannel is not extended from the interface as its objects are only instantiated internally.
|
|
16
|
+
* The implementation here needs to match the interface definition which exists purely for documentation.
|
|
17
|
+
*/
|
|
18
|
+
class MessageChannel extends EventTarget {
|
|
19
|
+
constructor(deviceManager, channelType) {
|
|
20
|
+
super();
|
|
21
|
+
switch (channelType) {
|
|
22
|
+
case deviceManager.DeviceMessageChannelType.SZE_HOST_APP:
|
|
23
|
+
this._type = deviceManager.DeviceMessageChannelType.SZE_HOST_APP;
|
|
24
|
+
this._description = "Message channel for bidirectional communication with the Senza Embedded device's host application";
|
|
25
|
+
this._capabilities = {
|
|
26
|
+
send: true,
|
|
27
|
+
receive: true,
|
|
28
|
+
dataTypes: ["string"],
|
|
29
|
+
maxMessageSize: 60 * 1024 // 60KB - to account for SCTP default max message size of 64Kb minus the wrapping object overhead
|
|
30
|
+
};
|
|
31
|
+
break;
|
|
32
|
+
case deviceManager.DeviceMessageChannelType.USB_SERIAL:
|
|
33
|
+
this._type = deviceManager.DeviceMessageChannelType.USB_SERIAL;
|
|
34
|
+
this._description = "Message channel for sending messages to the device's USB serial interface";
|
|
35
|
+
this._capabilities = {
|
|
36
|
+
send: true,
|
|
37
|
+
receive: false,
|
|
38
|
+
dataTypes: ["string"],
|
|
39
|
+
maxMessageSize: 60 * 1024 // 60KB
|
|
40
|
+
};
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
throw new Error("Invalid channel type");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this._open = true;
|
|
47
|
+
this._manager = deviceManager;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Calls EventTarget's addEventListener after checking if channel is open
|
|
52
|
+
*/
|
|
53
|
+
addEventListener(type, callback) {
|
|
54
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
55
|
+
EventTarget.prototype.addEventListener.call(this, type, callback);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Calls EventTarget's removeEventListener after checking if channel is open
|
|
60
|
+
*/
|
|
61
|
+
removeEventListener(type, callback) {
|
|
62
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
63
|
+
EventTarget.prototype.removeEventListener.call(this, type, callback);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Marks channel as closed and not usable anymore. Also removes it from the DeviceManager's createdChannels map.
|
|
68
|
+
*/
|
|
69
|
+
dispose() {
|
|
70
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
71
|
+
this._open = false;
|
|
72
|
+
if (this._manager && typeof this._manager._removeChannel === "function") {
|
|
73
|
+
this._manager._removeChannel(this._type);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the type of this message channel.
|
|
79
|
+
* @returns {DeviceMessageChannelType} The channel type
|
|
80
|
+
*/
|
|
81
|
+
get type() {
|
|
82
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
83
|
+
return this._type;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get the description of this message channel.
|
|
88
|
+
* @returns {string} The channel description
|
|
89
|
+
*/
|
|
90
|
+
get description() {
|
|
91
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
92
|
+
return this._description;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get the capabilities of this message channel.
|
|
97
|
+
* @returns {Object} The capabilities object containing send/receive flags, supported data types, etc.
|
|
98
|
+
*/
|
|
99
|
+
get capabilities() {
|
|
100
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
101
|
+
return this._capabilities;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @private
|
|
106
|
+
* Performs checks to see if a message can be sent through this channel.
|
|
107
|
+
* @param {*} message The message to check
|
|
108
|
+
*/
|
|
109
|
+
_checkMessageBeforeSend(message) {
|
|
110
|
+
if (!this._open) throw new Error("Channel is closed");
|
|
111
|
+
if (typeof window === "undefined" || typeof window.cefQuery !== "function") {
|
|
112
|
+
throw new Error("cefQuery is not available");
|
|
113
|
+
}
|
|
114
|
+
if (typeof message !== "string" && !(message instanceof String)) {
|
|
115
|
+
throw new Error("message must be a string");
|
|
116
|
+
}
|
|
117
|
+
// Enforce maxMessageSize limit
|
|
118
|
+
const maxSize = this.capabilities.maxMessageSize;
|
|
119
|
+
const messageSize = byteSize(message);
|
|
120
|
+
if (messageSize > maxSize) {
|
|
121
|
+
throw new Error(`Message size (${messageSize} bytes) exceeds maximum allowed size (${maxSize} bytes)`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Senza Embedded Host App Message Channel
|
|
128
|
+
*/
|
|
129
|
+
class SzeHostAppMessageChannel extends MessageChannel {
|
|
130
|
+
_docListener = (e) => {
|
|
131
|
+
const logger = sdkLogger.withFields({ "FCID": e?.detail?.fcid });
|
|
132
|
+
logger.log("Got hs/deviceMsgSzeHostApp", JSON.stringify(e?.detail));
|
|
133
|
+
|
|
134
|
+
const event = new Event("message");
|
|
135
|
+
event.message = e?.detail?.message || "";
|
|
136
|
+
this.dispatchEvent(event);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
constructor(deviceManager) {
|
|
140
|
+
super(deviceManager, deviceManager.DeviceMessageChannelType.SZE_HOST_APP);
|
|
141
|
+
// Listen for device host app to web app messages and forward to EventTarget listeners
|
|
142
|
+
if (typeof document !== "undefined") {
|
|
143
|
+
document.addEventListener("hs/deviceMsgSzeHostApp", this._docListener);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async sendMessage(message) {
|
|
148
|
+
this._checkMessageBeforeSend(message);
|
|
149
|
+
const tc_message = { type: "deviceMsgSzeHostApp", message: message };
|
|
150
|
+
const request = {
|
|
151
|
+
target: "TC",
|
|
152
|
+
waitForResponse: false,
|
|
153
|
+
message: JSON.stringify(tc_message)
|
|
154
|
+
};
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
window.cefQuery({
|
|
157
|
+
request: JSON.stringify(request),
|
|
158
|
+
persistent: false,
|
|
159
|
+
onSuccess: () => {
|
|
160
|
+
resolve();
|
|
161
|
+
},
|
|
162
|
+
onFailure: (code, msg) => {
|
|
163
|
+
reject(new Error(`Request failed: ${code} ${msg}`));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
dispose() {
|
|
170
|
+
if (typeof document !== "undefined") {
|
|
171
|
+
document.removeEventListener("hs/deviceMsgSzeHostApp", this._docListener);
|
|
172
|
+
}
|
|
173
|
+
super.dispose();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* USB Serial Message Channel implementation
|
|
179
|
+
*/
|
|
180
|
+
class UsbSerialMessageChannel extends MessageChannel {
|
|
181
|
+
constructor(deviceManager) {
|
|
182
|
+
super(deviceManager, deviceManager.DeviceMessageChannelType.USB_SERIAL);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async sendMessage(message) {
|
|
186
|
+
this._checkMessageBeforeSend(message);
|
|
187
|
+
// Use the existing sendDataToDevice functionality
|
|
188
|
+
return getDeviceManagerInstance().sendDataToDevice(message);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
12
192
|
async function getWifiApData() {
|
|
13
193
|
// Wi-Fi access point data is static, so it needs to be retrieved only once
|
|
14
194
|
if (!wifi_ap_data) {
|
|
@@ -38,6 +218,13 @@ class DeviceManager extends DeviceManagerInterface {
|
|
|
38
218
|
|
|
39
219
|
constructor() {
|
|
40
220
|
super();
|
|
221
|
+
this.availableMessageChannels = [
|
|
222
|
+
// Future enhancement: add available channels based on cfg or some device info
|
|
223
|
+
this.DeviceMessageChannelType.SZE_HOST_APP,
|
|
224
|
+
this.DeviceMessageChannelType.USB_SERIAL
|
|
225
|
+
];
|
|
226
|
+
// Track created channels in a map to enforce one-per-type limit, i.e. only one SZE_HOST_APP channel at a time
|
|
227
|
+
this.createdChannels = new Map();
|
|
41
228
|
}
|
|
42
229
|
|
|
43
230
|
get deviceInfo() {
|
|
@@ -202,10 +389,67 @@ class DeviceManager extends DeviceManagerInterface {
|
|
|
202
389
|
await Promise.all([getWifiApData(), getWifiStatus()]);
|
|
203
390
|
return { ...wifi_ap_data, ...wifi_status };
|
|
204
391
|
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Returns a list of available message channels for the device.
|
|
395
|
+
* @returns {DeviceMessageChannelType[]} An array of DeviceMessageChannelType values representing the available message channels.
|
|
396
|
+
*/
|
|
397
|
+
getAvailableMessageChannels() {
|
|
398
|
+
return this.availableMessageChannels || [];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Create a message channel of the specified type.
|
|
403
|
+
* Only one channel of each type can be created at a time.
|
|
404
|
+
* @param {DeviceMessageChannelType} type The type of message channel to create.
|
|
405
|
+
* @returns {MessageChannel} A MessageChannel object representing the created message channel.
|
|
406
|
+
* @throws {Error} If the specified type is not supported or already exists.
|
|
407
|
+
*/
|
|
408
|
+
createMessageChannel(type) {
|
|
409
|
+
const channelType = this.availableMessageChannels.find(c => c === type);
|
|
410
|
+
const availableTypes = this.availableMessageChannels.join(", ");
|
|
411
|
+
if (!channelType) {
|
|
412
|
+
throw new Error(`Unsupported channel type: ${type}. Available types: ${availableTypes}`);
|
|
413
|
+
}
|
|
414
|
+
// Check if a channel of this type already exists
|
|
415
|
+
if (this.createdChannels.has(type)) {
|
|
416
|
+
throw new Error(`A message channel of type '${type}' already exists. Only one channel per type is allowed.`);
|
|
417
|
+
}
|
|
418
|
+
// Create channel implementation based on type
|
|
419
|
+
let channel;
|
|
420
|
+
if (type === this.DeviceMessageChannelType.SZE_HOST_APP) {
|
|
421
|
+
channel = new SzeHostAppMessageChannel(this);
|
|
422
|
+
} else if (type === this.DeviceMessageChannelType.USB_SERIAL) {
|
|
423
|
+
channel = new UsbSerialMessageChannel(this);
|
|
424
|
+
}
|
|
425
|
+
// else channel type is unsupported, but is already checked above
|
|
426
|
+
|
|
427
|
+
this.createdChannels.set(type, channel);
|
|
428
|
+
return channel;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Remove a message channel of the specified type.
|
|
433
|
+
* @private
|
|
434
|
+
* @param {DeviceMessageChannelType} type The type of message channel to remove.
|
|
435
|
+
*/
|
|
436
|
+
_removeChannel(type) {
|
|
437
|
+
if (!this.createdChannels || !this.createdChannels.has(type)) {
|
|
438
|
+
throw new Error(`No channel of type '${type}' exists.`);
|
|
439
|
+
}
|
|
440
|
+
this.createdChannels.delete(type);
|
|
441
|
+
}
|
|
205
442
|
}
|
|
206
443
|
|
|
207
|
-
|
|
444
|
+
// Create the singleton instance
|
|
445
|
+
const deviceManagerInstance = new DeviceManager();
|
|
446
|
+
|
|
447
|
+
// Function to get the singleton instance (for forward reference)
|
|
448
|
+
function getDeviceManagerInstance() {
|
|
449
|
+
return deviceManagerInstance;
|
|
450
|
+
}
|
|
208
451
|
|
|
452
|
+
/**
|
|
209
453
|
* @module
|
|
210
454
|
* @example
|
|
211
455
|
* import { deviceManager } from "senza-sdk";
|
|
@@ -214,6 +458,13 @@ class DeviceManager extends DeviceManagerInterface {
|
|
|
214
458
|
* await deviceManager.clearWifi();
|
|
215
459
|
* deviceManager.reboot();
|
|
216
460
|
*
|
|
461
|
+
* // Message channels
|
|
462
|
+
* const channels = deviceManager.getAvailableMessageChannels();
|
|
463
|
+
* const channel = deviceManager.createMessageChannel(deviceManager.DeviceMessageChannelType.SZE_HOST_APP);
|
|
464
|
+
* channel.addEventListener('message', (event) => {
|
|
465
|
+
* console.log(`Received: ${event.message}`);
|
|
466
|
+
* });
|
|
467
|
+
*
|
|
217
468
|
* @return {DeviceManager} pointer to the DeviceManager singleton
|
|
218
469
|
*/
|
|
219
|
-
export const deviceManager =
|
|
470
|
+
export const deviceManager = deviceManagerInstance;
|