@xiboplayer/xmr 0.2.0 → 0.3.1
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/README.md +25 -337
- package/package.json +2 -2
- package/src/index.js +2 -0
package/README.md
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
# @xiboplayer/xmr
|
|
1
|
+
# @xiboplayer/xmr
|
|
2
2
|
|
|
3
|
-
**XMR
|
|
3
|
+
**XMR WebSocket client for real-time Xibo CMS commands.**
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Listens for push commands from the CMS over WebSocket with automatic reconnection:
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
9
|
+
- **collectNow** — trigger an immediate collection cycle
|
|
10
|
+
- **screenshot** — request a display screenshot
|
|
11
|
+
- **changeLayout** — switch to a specific layout
|
|
12
|
+
- **overlayLayout** — show an overlay layout
|
|
13
|
+
- **revertToSchedule** — return to the scheduled playlist
|
|
14
|
+
- **purgeAll** — clear all cached content
|
|
15
|
+
- **dataUpdate** — notify of updated widget data
|
|
16
|
+
- **triggerWebhook** — fire a webhook action
|
|
17
|
+
- **commandAction** — execute a shell command
|
|
18
|
+
- **criteriaUpdate** — update geo/criteria filters
|
|
18
19
|
|
|
19
20
|
## Installation
|
|
20
21
|
|
|
@@ -22,339 +23,26 @@ The XMR package provides WebSocket integration with Xibo CMS, enabling real-time
|
|
|
22
23
|
npm install @xiboplayer/xmr
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
```javascript
|
|
28
|
-
import { XmrWrapper } from '@xiboplayer/xmr';
|
|
29
|
-
|
|
30
|
-
// Create wrapper
|
|
31
|
-
const config = {
|
|
32
|
-
cmsAddress: 'https://cms.example.com',
|
|
33
|
-
hardwareKey: 'player-hw-key-123',
|
|
34
|
-
xmrChannel: 'player-channel' // Optional, defaults to player-{hardwareKey}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const player = {
|
|
38
|
-
collect: async () => { /* ... */ },
|
|
39
|
-
captureScreenshot: async () => { /* ... */ },
|
|
40
|
-
changeLayout: async (layoutId) => { /* ... */ },
|
|
41
|
-
reportGeoLocation: async (data) => { /* ... */ },
|
|
42
|
-
updateStatus: (status) => { /* ... */ }
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const xmr = new XmrWrapper(config, player);
|
|
46
|
-
|
|
47
|
-
// Start connection
|
|
48
|
-
const connected = await xmr.start('wss://cms.example.com:9505', 'cms-key-123');
|
|
49
|
-
|
|
50
|
-
if (connected) {
|
|
51
|
-
console.log('XMR connected');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check connection status
|
|
55
|
-
if (xmr.isConnected()) {
|
|
56
|
-
console.log('Currently connected');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Stop connection
|
|
60
|
-
await xmr.stop();
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Supported Commands
|
|
64
|
-
|
|
65
|
-
### Core Commands
|
|
66
|
-
|
|
67
|
-
| Command | Description | Player Method |
|
|
68
|
-
|---------|-------------|---------------|
|
|
69
|
-
| `collectNow` | Force immediate XMDS sync | `player.collect()` |
|
|
70
|
-
| `screenShot` | Capture screenshot | `player.captureScreenshot()` |
|
|
71
|
-
| `changeLayout` | Switch to specific layout | `player.changeLayout(layoutId)` |
|
|
72
|
-
| `licenceCheck` | Validate license (no-op for Linux) | None |
|
|
73
|
-
| `rekey` | Rotate RSA keys | None (future) |
|
|
74
|
-
|
|
75
|
-
### New in v0.0.6
|
|
76
|
-
|
|
77
|
-
| Command | Description | Player Method |
|
|
78
|
-
|---------|-------------|---------------|
|
|
79
|
-
| `criteriaUpdate` | Update display criteria | `player.collect()` |
|
|
80
|
-
| `currentGeoLocation` | Report geo location | `player.reportGeoLocation(data)` |
|
|
81
|
-
|
|
82
|
-
See [XMR_COMMANDS.md](./XMR_COMMANDS.md) for complete command reference.
|
|
83
|
-
|
|
84
|
-
## Connection Lifecycle
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
┌──────────┐
|
|
88
|
-
│ start() │
|
|
89
|
-
└─────┬────┘
|
|
90
|
-
│
|
|
91
|
-
▼
|
|
92
|
-
┌──────────┐
|
|
93
|
-
┌─────│Connected │◄─────┐
|
|
94
|
-
│ └─────┬────┘ │
|
|
95
|
-
│ │ │
|
|
96
|
-
Network │ │Commands │Auto
|
|
97
|
-
Error │ │Flowing │Reconnect
|
|
98
|
-
│ │ │
|
|
99
|
-
▼ ▼ │
|
|
100
|
-
┌──────────┐ ┌──────────┐
|
|
101
|
-
│Disconnect│────────────│Reconnect │
|
|
102
|
-
└─────┬────┘ Backoff └──────────┘
|
|
103
|
-
│
|
|
104
|
-
│stop()
|
|
105
|
-
▼
|
|
106
|
-
┌──────────┐
|
|
107
|
-
│ Stopped │
|
|
108
|
-
└──────────┘
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Reconnection Logic
|
|
112
|
-
|
|
113
|
-
- **Exponential backoff**: 5s, 10s, 15s, 20s, 25s, 30s, 35s, 40s, 45s, 50s
|
|
114
|
-
- **Max attempts**: 10 (then waits for next collection cycle)
|
|
115
|
-
- **Intentional shutdown**: No reconnect when `stop()` is called
|
|
116
|
-
- **Connection events**: `connected`, `disconnected`, `error`
|
|
117
|
-
|
|
118
|
-
## API Reference
|
|
119
|
-
|
|
120
|
-
### XmrWrapper
|
|
121
|
-
|
|
122
|
-
#### Constructor
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
new XmrWrapper(config, player)
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Parameters**:
|
|
129
|
-
- `config` (Object): Player configuration
|
|
130
|
-
- `hardwareKey` (string): Player hardware key
|
|
131
|
-
- `xmrChannel` (string, optional): Custom channel ID
|
|
132
|
-
- `player` (Object): Player instance with command handlers
|
|
133
|
-
|
|
134
|
-
#### Methods
|
|
135
|
-
|
|
136
|
-
##### start(xmrUrl, cmsKey)
|
|
137
|
-
|
|
138
|
-
Start XMR connection.
|
|
139
|
-
|
|
140
|
-
```javascript
|
|
141
|
-
await xmr.start('wss://cms.example.com:9505', 'cms-key-123');
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
**Returns**: `Promise<boolean>` - True if connected, false if failed
|
|
145
|
-
|
|
146
|
-
##### stop()
|
|
147
|
-
|
|
148
|
-
Stop XMR connection and cancel reconnection.
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
await xmr.stop();
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
##### isConnected()
|
|
155
|
-
|
|
156
|
-
Check connection status.
|
|
157
|
-
|
|
158
|
-
```javascript
|
|
159
|
-
const connected = xmr.isConnected();
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Returns**: `boolean`
|
|
163
|
-
|
|
164
|
-
##### send(action, data)
|
|
165
|
-
|
|
166
|
-
Send message to CMS (for future features).
|
|
167
|
-
|
|
168
|
-
```javascript
|
|
169
|
-
await xmr.send('customAction', { key: 'value' });
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
**Returns**: `Promise<boolean>` - True if sent successfully
|
|
173
|
-
|
|
174
|
-
### Player Interface
|
|
175
|
-
|
|
176
|
-
Your player object should implement these methods:
|
|
177
|
-
|
|
178
|
-
```javascript
|
|
179
|
-
{
|
|
180
|
-
// Required
|
|
181
|
-
collect: async () => Promise<void>,
|
|
182
|
-
captureScreenshot: async () => Promise<void>,
|
|
183
|
-
changeLayout: async (layoutId) => Promise<void>,
|
|
184
|
-
|
|
185
|
-
// Optional
|
|
186
|
-
reportGeoLocation: async (data) => Promise<void>,
|
|
187
|
-
updateStatus: (status) => void
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Testing
|
|
192
|
-
|
|
193
|
-
### Run Tests
|
|
194
|
-
|
|
195
|
-
```bash
|
|
196
|
-
# Run all tests
|
|
197
|
-
npm test
|
|
198
|
-
|
|
199
|
-
# Watch mode
|
|
200
|
-
npm run test:watch
|
|
201
|
-
|
|
202
|
-
# Coverage report
|
|
203
|
-
npm run test:coverage
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Test Coverage
|
|
207
|
-
|
|
208
|
-
- ✅ 48 test cases
|
|
209
|
-
- ✅ Constructor and initialization
|
|
210
|
-
- ✅ Connection lifecycle (start, stop, reconnect)
|
|
211
|
-
- ✅ All 7 CMS commands
|
|
212
|
-
- ✅ Error handling and edge cases
|
|
213
|
-
- ✅ Memory management and cleanup
|
|
214
|
-
|
|
215
|
-
See [XMR_TESTING.md](./XMR_TESTING.md) for comprehensive testing guide.
|
|
216
|
-
|
|
217
|
-
## Configuration
|
|
218
|
-
|
|
219
|
-
### From registerDisplay
|
|
220
|
-
|
|
221
|
-
XMR settings are typically received from CMS `registerDisplay` response:
|
|
222
|
-
|
|
223
|
-
```json
|
|
224
|
-
{
|
|
225
|
-
"settings": {
|
|
226
|
-
"xmrWebSocketAddress": "wss://cms.example.com:9505",
|
|
227
|
-
"xmrCmsKey": "abcdef123456",
|
|
228
|
-
"xmrChannel": "player-hw-key-123"
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Use these values with `start()`:
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
const { xmrWebSocketAddress, xmrCmsKey } = settings;
|
|
237
|
-
await xmr.start(xmrWebSocketAddress, xmrCmsKey);
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Custom Channel
|
|
241
|
-
|
|
242
|
-
Override default channel (player-{hardwareKey}):
|
|
26
|
+
## Usage
|
|
243
27
|
|
|
244
28
|
```javascript
|
|
245
|
-
|
|
246
|
-
hardwareKey: 'hw-123',
|
|
247
|
-
xmrChannel: 'custom-channel-name' // Use this instead
|
|
248
|
-
};
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Error Handling
|
|
252
|
-
|
|
253
|
-
All command handlers include error handling:
|
|
29
|
+
import { XmrClient } from '@xiboplayer/xmr';
|
|
254
30
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
await this.player.collect();
|
|
260
|
-
console.log('[XMR] collectNow completed successfully');
|
|
261
|
-
} catch (error) {
|
|
262
|
-
console.error('[XMR] collectNow failed:', error);
|
|
263
|
-
}
|
|
31
|
+
const xmr = new XmrClient({
|
|
32
|
+
wsUrl: 'wss://your-cms.example.com/xmr',
|
|
33
|
+
channel: 'display-channel-key',
|
|
34
|
+
rsaKey: 'display-rsa-key',
|
|
264
35
|
});
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
Errors don't:
|
|
268
|
-
- ❌ Crash the player
|
|
269
|
-
- ❌ Disconnect XMR
|
|
270
|
-
- ❌ Prevent other commands
|
|
271
|
-
|
|
272
|
-
They do:
|
|
273
|
-
- ✅ Log to console
|
|
274
|
-
- ✅ Preserve connection state
|
|
275
|
-
- ✅ Continue processing other commands
|
|
276
|
-
|
|
277
|
-
## Troubleshooting
|
|
278
|
-
|
|
279
|
-
### Connection Issues
|
|
280
|
-
|
|
281
|
-
**Problem**: XMR won't connect
|
|
282
|
-
|
|
283
|
-
**Solution**:
|
|
284
|
-
1. Verify XMR enabled in CMS settings
|
|
285
|
-
2. Check firewall allows port 9505
|
|
286
|
-
3. Verify `xmrWebSocketAddress` from registerDisplay
|
|
287
|
-
4. Check browser console for errors
|
|
288
|
-
|
|
289
|
-
### Commands Not Executing
|
|
290
|
-
|
|
291
|
-
**Problem**: collectNow sent but nothing happens
|
|
292
|
-
|
|
293
|
-
**Solution**:
|
|
294
|
-
1. Check `xmr.isConnected()` returns true
|
|
295
|
-
2. Verify player methods exist (`player.collect`, etc.)
|
|
296
|
-
3. Check console for command reception logs
|
|
297
|
-
4. Look for errors in command handler
|
|
298
|
-
|
|
299
|
-
### Reconnection Loops
|
|
300
|
-
|
|
301
|
-
**Problem**: Keeps reconnecting forever
|
|
302
|
-
|
|
303
|
-
**Solution**:
|
|
304
|
-
1. Verify XMR server is running
|
|
305
|
-
2. Check cmsKey is correct
|
|
306
|
-
3. Monitor `reconnectAttempts` counter
|
|
307
|
-
4. Manually stop: `await xmr.stop()`
|
|
308
36
|
|
|
309
|
-
|
|
37
|
+
xmr.on('collectNow', () => player.collect());
|
|
38
|
+
xmr.on('screenshot', () => captureScreenshot());
|
|
39
|
+
xmr.connect();
|
|
40
|
+
```
|
|
310
41
|
|
|
311
42
|
## Dependencies
|
|
312
43
|
|
|
313
|
-
- `@
|
|
314
|
-
- `@xiboplayer/utils` - Logging and utilities
|
|
315
|
-
|
|
316
|
-
## Version History
|
|
317
|
-
|
|
318
|
-
### v0.9.0 (Current)
|
|
319
|
-
- ✅ Upgraded to @xibosignage/xibo-communication-framework@0.0.6
|
|
320
|
-
- ✅ Added `criteriaUpdate` command
|
|
321
|
-
- ✅ Added `currentGeoLocation` command
|
|
322
|
-
- ✅ Intentional shutdown flag (no reconnect on stop)
|
|
323
|
-
- ✅ Comprehensive test suite (48 tests)
|
|
324
|
-
- ✅ Complete documentation
|
|
325
|
-
|
|
326
|
-
### Previous Versions
|
|
327
|
-
- Basic XMR integration
|
|
328
|
-
- Core commands (collectNow, screenShot, changeLayout)
|
|
329
|
-
- Automatic reconnection
|
|
330
|
-
|
|
331
|
-
## Contributing
|
|
332
|
-
|
|
333
|
-
When adding new XMR commands:
|
|
334
|
-
|
|
335
|
-
1. Add event handler in `setupEventHandlers()`
|
|
336
|
-
2. Update [XMR_COMMANDS.md](./XMR_COMMANDS.md)
|
|
337
|
-
3. Add tests in `xmr-wrapper.test.js`
|
|
338
|
-
4. Implement player method if needed
|
|
339
|
-
|
|
340
|
-
See [XMR_TESTING.md](./XMR_TESTING.md#adding-new-commands) for details.
|
|
341
|
-
|
|
342
|
-
## Related Packages
|
|
343
|
-
|
|
344
|
-
- [@xiboplayer/core](../../core/docs/) - Player core orchestration
|
|
345
|
-
- [@xiboplayer/xmds](../../xmds/docs/) - XMDS SOAP client
|
|
346
|
-
- [@xiboplayer/schedule](../../schedule/docs/) - Schedule engine
|
|
347
|
-
|
|
348
|
-
## Support
|
|
349
|
-
|
|
350
|
-
- **GitHub Issues**: https://github.com/xibo/xibo-players/issues
|
|
351
|
-
- **Documentation**: [XMR_COMMANDS.md](./XMR_COMMANDS.md), [XMR_TESTING.md](./XMR_TESTING.md)
|
|
352
|
-
- **Xibo Community**: https://community.xibo.org.uk
|
|
44
|
+
- `@xiboplayer/utils` — logger, events
|
|
353
45
|
|
|
354
46
|
---
|
|
355
47
|
|
|
356
|
-
**
|
|
357
|
-
|
|
358
|
-
## License
|
|
359
|
-
|
|
360
|
-
AGPL-3.0-or-later
|
|
48
|
+
**Part of the [XiboPlayer SDK](https://github.com/xibo-players/xiboplayer)** | [MCP Server](https://github.com/xibo-players/xiboplayer/tree/main/mcp-server) for AI-assisted development
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/xmr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "XMR WebSocket client for real-time Xibo CMS commands",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@xibosignage/xibo-communication-framework": "^0.0.6",
|
|
12
|
-
"@xiboplayer/utils": "0.
|
|
12
|
+
"@xiboplayer/utils": "0.3.1"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"vitest": "^2.0.0"
|
package/src/index.js
CHANGED