jmri-client 4.2.0-beta.1 → 5.0.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.
Files changed (60) hide show
  1. package/README.md +18 -1
  2. package/dist/cjs/index.js +2382 -31
  3. package/dist/esm/index.js +2333 -17
  4. package/docs/API.md +39 -4
  5. package/docs/BROWSER.md +4 -4
  6. package/docs/EXAMPLES.md +42 -0
  7. package/docs/MIGRATION.md +30 -1
  8. package/package.json +17 -18
  9. package/dist/cjs/client.js +0 -366
  10. package/dist/cjs/core/connection-state-manager.js +0 -84
  11. package/dist/cjs/core/heartbeat-manager.js +0 -79
  12. package/dist/cjs/core/index.js +0 -25
  13. package/dist/cjs/core/message-queue.js +0 -59
  14. package/dist/cjs/core/reconnection-manager.js +0 -97
  15. package/dist/cjs/core/websocket-adapter.js +0 -135
  16. package/dist/cjs/core/websocket-client.js +0 -388
  17. package/dist/cjs/managers/index.js +0 -25
  18. package/dist/cjs/managers/light-manager.js +0 -111
  19. package/dist/cjs/managers/power-manager.js +0 -90
  20. package/dist/cjs/managers/roster-manager.js +0 -118
  21. package/dist/cjs/managers/system-connections-manager.js +0 -28
  22. package/dist/cjs/managers/throttle-manager.js +0 -233
  23. package/dist/cjs/managers/turnout-manager.js +0 -111
  24. package/dist/cjs/mocks/index.js +0 -12
  25. package/dist/cjs/mocks/mock-data.js +0 -237
  26. package/dist/cjs/mocks/mock-response-manager.js +0 -290
  27. package/dist/cjs/types/client-options.js +0 -66
  28. package/dist/cjs/types/events.js +0 -16
  29. package/dist/cjs/types/index.js +0 -23
  30. package/dist/cjs/types/jmri-messages.js +0 -95
  31. package/dist/cjs/types/throttle.js +0 -19
  32. package/dist/cjs/utils/exponential-backoff.js +0 -40
  33. package/dist/cjs/utils/index.js +0 -21
  34. package/dist/cjs/utils/message-id.js +0 -40
  35. package/dist/esm/client.js +0 -362
  36. package/dist/esm/core/connection-state-manager.js +0 -80
  37. package/dist/esm/core/heartbeat-manager.js +0 -75
  38. package/dist/esm/core/index.js +0 -9
  39. package/dist/esm/core/message-queue.js +0 -55
  40. package/dist/esm/core/reconnection-manager.js +0 -93
  41. package/dist/esm/core/websocket-adapter.js +0 -98
  42. package/dist/esm/core/websocket-client.js +0 -384
  43. package/dist/esm/managers/index.js +0 -9
  44. package/dist/esm/managers/light-manager.js +0 -107
  45. package/dist/esm/managers/power-manager.js +0 -86
  46. package/dist/esm/managers/roster-manager.js +0 -114
  47. package/dist/esm/managers/system-connections-manager.js +0 -24
  48. package/dist/esm/managers/throttle-manager.js +0 -229
  49. package/dist/esm/managers/turnout-manager.js +0 -107
  50. package/dist/esm/mocks/index.js +0 -6
  51. package/dist/esm/mocks/mock-data.js +0 -234
  52. package/dist/esm/mocks/mock-response-manager.js +0 -286
  53. package/dist/esm/types/client-options.js +0 -62
  54. package/dist/esm/types/events.js +0 -13
  55. package/dist/esm/types/index.js +0 -7
  56. package/dist/esm/types/jmri-messages.js +0 -89
  57. package/dist/esm/types/throttle.js +0 -15
  58. package/dist/esm/utils/exponential-backoff.js +0 -36
  59. package/dist/esm/utils/index.js +0 -5
  60. package/dist/esm/utils/message-id.js +0 -36
package/docs/API.md CHANGED
@@ -65,6 +65,9 @@ client.on('heartbeat:timeout', () => { });
65
65
  // Get current power state
66
66
  const state: PowerState = await client.getPower();
67
67
 
68
+ // Get power state for a specific hardware connection (see System Connections)
69
+ const state: PowerState = await client.getPower('L'); // LocoNet prefix
70
+
68
71
  // PowerState enum values (from JMRI JSON protocol):
69
72
  // PowerState.UNKNOWN = 0 (state cannot be determined)
70
73
  // PowerState.ON = 2 (power is on)
@@ -83,10 +86,15 @@ switch (state) {
83
86
  break;
84
87
  }
85
88
 
86
- // Set power
89
+ // Set power (all connections, or a specific one)
87
90
  await client.setPower(PowerState.ON);
88
- await client.powerOn(); // Convenience method
89
- await client.powerOff(); // Convenience method
91
+ await client.setPower(PowerState.ON, 'L'); // LocoNet only
92
+
93
+ // Convenience methods — all accept an optional prefix
94
+ await client.powerOn(); // All connections
95
+ await client.powerOn('L'); // LocoNet only
96
+ await client.powerOff(); // All connections
97
+ await client.powerOff('D'); // DCC++ only
90
98
 
91
99
  // Listen for power changes (including UNKNOWN states)
92
100
  client.on('power:changed', (state: PowerState) => {
@@ -96,6 +104,29 @@ client.on('power:changed', (state: PowerState) => {
96
104
  });
97
105
  ```
98
106
 
107
+ ## System Connections
108
+
109
+ Discover the hardware connections JMRI has configured and their short prefix strings. Use these prefixes to target power and throttle commands at a specific connection when multiple are active.
110
+
111
+ ```typescript
112
+ // List all available system connections
113
+ const connections: SystemConnectionData[] = await client.getSystemConnections();
114
+ // [
115
+ // { name: 'LocoNet', prefix: 'L', userName: 'LocoNet' },
116
+ // { name: 'DCC++', prefix: 'D' }
117
+ // ]
118
+
119
+ // Use a prefix to target a specific connection
120
+ const [loconet] = connections.filter(c => c.name === 'LocoNet');
121
+ await client.powerOn(loconet.prefix);
122
+
123
+ // Acquire a throttle on a specific connection
124
+ const throttleId = await client.acquireThrottle({
125
+ address: 3,
126
+ prefix: loconet.prefix
127
+ });
128
+ ```
129
+
99
130
  ## Roster Management
100
131
 
101
132
  ```typescript
@@ -150,6 +181,9 @@ client.on('light:changed', (name: string, state: LightState) => {
150
181
  // Acquire throttle
151
182
  const throttleId = await client.acquireThrottle({ address: 3 });
152
183
 
184
+ // Acquire throttle on a specific hardware connection
185
+ const throttleId = await client.acquireThrottle({ address: 3, prefix: 'L' });
186
+
153
187
  // Control speed (0.0 to 1.0)
154
188
  await client.setThrottleSpeed(throttleId, 0.0); // Stopped
155
189
  await client.setThrottleSpeed(throttleId, 0.5); // Half speed
@@ -237,6 +271,7 @@ import {
237
271
  TurnoutData,
238
272
  ThrottleState,
239
273
  ThrottleFunctionKey,
240
- ConnectionState
274
+ ConnectionState,
275
+ SystemConnectionData
241
276
  } from 'jmri-client';
242
277
  ```
package/docs/BROWSER.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Browser Usage Guide
2
2
 
3
- jmri-client v3.2.0+ supports both Node.js and browser environments. This guide shows how to use jmri-client in web applications.
3
+ jmri-client supports both Node.js and browser environments. This guide shows how to use jmri-client in web applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -112,7 +112,7 @@ For quick prototypes without a build step, use the browser bundle:
112
112
 
113
113
  <script type="module">
114
114
  // Use the browser bundle from CDN
115
- import { JmriClient } from 'https://unpkg.com/jmri-client@3.3.0/dist/browser/jmri-client.js';
115
+ import { JmriClient } from 'https://unpkg.com/jmri-client@5.0.0/dist/browser/jmri-client.js';
116
116
 
117
117
  const client = new JmriClient({
118
118
  host: 'localhost',
@@ -144,8 +144,8 @@ For quick prototypes without a build step, use the browser bundle:
144
144
  ```
145
145
 
146
146
  Alternative CDNs:
147
- - **unpkg**: `https://unpkg.com/jmri-client@3.3.0/dist/browser/jmri-client.js`
148
- - **jsDelivr**: `https://cdn.jsdelivr.net/npm/jmri-client@3.3.0/dist/browser/jmri-client.js`
147
+ - **unpkg**: `https://unpkg.com/jmri-client@5.0.0/dist/browser/jmri-client.js`
148
+ - **jsDelivr**: `https://cdn.jsdelivr.net/npm/jmri-client@5.0.0/dist/browser/jmri-client.js`
149
149
 
150
150
  ## React Example
151
151
 
package/docs/EXAMPLES.md CHANGED
@@ -283,6 +283,48 @@ async function runLayout() {
283
283
  runLayout();
284
284
  ```
285
285
 
286
+ ## Multi-Connection / Hardware Prefix
287
+
288
+ When JMRI is configured with more than one hardware connection (e.g. both LocoNet and DCC++), use `getSystemConnections()` to discover the available prefixes, then pass them to power and throttle commands to target a specific connection.
289
+
290
+ ```typescript
291
+ import { JmriClient } from 'jmri-client';
292
+
293
+ const client = new JmriClient({ host: 'jmri.local' });
294
+
295
+ client.on('connected', async () => {
296
+ // Discover what hardware connections JMRI has configured
297
+ const connections = await client.getSystemConnections();
298
+ console.log('Available connections:');
299
+ for (const conn of connections) {
300
+ console.log(` ${conn.name} (prefix: "${conn.prefix}")`);
301
+ }
302
+ // Available connections:
303
+ // LocoNet (prefix: "L")
304
+ // DCC++ (prefix: "D")
305
+
306
+ // Power on via LocoNet only
307
+ await client.powerOn('L');
308
+
309
+ // Acquire a throttle on LocoNet
310
+ const throttle = await client.acquireThrottle({ address: 3, prefix: 'L' });
311
+ await client.setThrottleDirection(throttle, true);
312
+ await client.setThrottleSpeed(throttle, 0.3);
313
+
314
+ await new Promise(resolve => setTimeout(resolve, 5000));
315
+
316
+ await client.setThrottleSpeed(throttle, 0);
317
+ await client.releaseThrottle(throttle);
318
+
319
+ // Power off via LocoNet only
320
+ await client.powerOff('L');
321
+
322
+ await client.disconnect();
323
+ });
324
+ ```
325
+
326
+ Single-connection layouts work exactly as before — just omit the prefix and JMRI routes to the default connection.
327
+
286
328
  ## CommonJS Usage
287
329
 
288
330
  ```javascript
package/docs/MIGRATION.md CHANGED
@@ -1,4 +1,33 @@
1
- # Migration Guide: v2.x to v3.x
1
+ # Migration Guide
2
+
3
+ ## v4.x to v5.0
4
+
5
+ ### Breaking Changes
6
+
7
+ #### Node.js version requirement
8
+
9
+ **v4.x:** Node.js 18+
10
+ **v5.0:** Node.js 22+
11
+
12
+ Node 18 reached end-of-life in April 2025 and Node 20 in April 2026. v5.0 requires Node 22 (Active LTS, EOL April 2027) or later.
13
+
14
+ ```bash
15
+ node --version # Should be >= 22.0.0
16
+ ```
17
+
18
+ #### No API changes
19
+
20
+ There are no API-level breaking changes between v4.x and v5.0. All existing code targeting v4.x will work without modification.
21
+
22
+ ### What's new in v5.0
23
+
24
+ - Node.js 22+ required (drops EOL Node 18 and 20)
25
+ - Built with TypeScript 6
26
+ - esbuild-based build pipeline — faster, smaller outputs, no more dual `tsc` compilation
27
+
28
+ ---
29
+
30
+ ## v2.x to v3.x
2
31
 
3
32
  ## Overview
4
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jmri-client",
3
- "version": "4.2.0-beta.1",
3
+ "version": "5.0.0",
4
4
  "description": "WebSocket client for JMRI with real-time updates and throttle control - works in both Node.js and browsers",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -8,19 +8,18 @@
8
8
  "types": "./dist/types/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/types/index.d.ts",
11
12
  "browser": "./dist/browser/jmri-client.js",
12
13
  "import": "./dist/esm/index.js",
13
- "require": "./dist/cjs/index.js",
14
- "types": "./dist/types/index.d.ts"
14
+ "require": "./dist/cjs/index.js"
15
15
  }
16
16
  },
17
17
  "scripts": {
18
18
  "prepublishOnly": "npm run build",
19
- "build": "npm run build:cjs && npm run build:esm && npm run build:types && npm run build:browser",
20
- "build:cjs": "tsc -p tsconfig.json",
21
- "build:esm": "tsc -p tsconfig.esm.json",
22
- "build:types": "tsc -p tsconfig.json --declaration --emitDeclarationOnly --outDir dist/types",
23
- "build:browser": "node build-browser.mjs",
19
+ "clean": "rm -rf dist",
20
+ "build": "npm run clean && node build.mjs && npm run build:types",
21
+ "build:types": "tsc -p tsconfig.json",
22
+ "typecheck": "tsc -p tsconfig.json --noEmit",
24
23
  "test": "jest --no-cache --coverage",
25
24
  "test:browser": "node ./tests/integration/serve-test.mjs",
26
25
  "test:reconnect": "node ./tests/integration/forced-reconnect.mjs",
@@ -59,21 +58,21 @@
59
58
  "url": "https://github.com/yamanote1138/jmri-client/issues"
60
59
  },
61
60
  "engines": {
62
- "node": ">=18"
61
+ "node": ">=22"
63
62
  },
64
63
  "devDependencies": {
65
- "@types/jest": "^29.0.3",
66
- "@types/node": "^18.19.0",
67
- "@types/ws": "^8.5.10",
68
- "esbuild": "^0.27.3",
69
- "jest": "^29.0.3",
70
- "playwright": "^1.58.2",
71
- "ts-jest": "^29.4.6",
72
- "typescript": "^5.3.3"
64
+ "@types/jest": "^30.0.0",
65
+ "@types/node": "^22.0.0",
66
+ "@types/ws": "^8.18.0",
67
+ "esbuild": "^0.28.0",
68
+ "jest": "^30.0.0",
69
+ "playwright": "^1.60.0",
70
+ "ts-jest": "^29.4.11",
71
+ "typescript": "^6.0.0"
73
72
  },
74
73
  "dependencies": {
75
74
  "eventemitter3": "^5.0.4",
76
- "ws": "^8.16.0"
75
+ "ws": "^8.21.0"
77
76
  },
78
77
  "files": [
79
78
  "dist",
@@ -1,366 +0,0 @@
1
- "use strict";
2
- /**
3
- * Main JMRI client class
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.JmriClient = void 0;
7
- const eventemitter3_1 = require("eventemitter3");
8
- const websocket_client_js_1 = require("./core/websocket-client.js");
9
- const power_manager_js_1 = require("./managers/power-manager.js");
10
- const roster_manager_js_1 = require("./managers/roster-manager.js");
11
- const throttle_manager_js_1 = require("./managers/throttle-manager.js");
12
- const turnout_manager_js_1 = require("./managers/turnout-manager.js");
13
- const light_manager_js_1 = require("./managers/light-manager.js");
14
- const system_connections_manager_js_1 = require("./managers/system-connections-manager.js");
15
- const client_options_js_1 = require("./types/client-options.js");
16
- /**
17
- * JMRI WebSocket Client
18
- * Provides event-driven interface to JMRI with throttle control
19
- */
20
- class JmriClient extends eventemitter3_1.EventEmitter {
21
- /**
22
- * Create a new JMRI client
23
- *
24
- * @param options - Client configuration options
25
- *
26
- * @example
27
- * ```typescript
28
- * const client = new JmriClient({
29
- * host: 'jmri.local',
30
- * port: 12080
31
- * });
32
- *
33
- * client.on('connected', () => console.log('Connected!'));
34
- * client.on('power:changed', (state) => console.log('Power:', state));
35
- * ```
36
- */
37
- constructor(options) {
38
- super();
39
- // Merge options with defaults
40
- this.options = (0, client_options_js_1.mergeOptions)(options);
41
- // Create WebSocket client
42
- this.wsClient = new websocket_client_js_1.WebSocketClient(this.options);
43
- // Create managers
44
- this.powerManager = new power_manager_js_1.PowerManager(this.wsClient);
45
- this.rosterManager = new roster_manager_js_1.RosterManager(this.wsClient);
46
- this.throttleManager = new throttle_manager_js_1.ThrottleManager(this.wsClient);
47
- this.turnoutManager = new turnout_manager_js_1.TurnoutManager(this.wsClient);
48
- this.lightManager = new light_manager_js_1.LightManager(this.wsClient);
49
- this.systemConnectionsManager = new system_connections_manager_js_1.SystemConnectionsManager(this.wsClient);
50
- // Forward events from WebSocket client
51
- this.wsClient.on('connected', () => this.emit('connected'));
52
- this.wsClient.on('disconnected', (reason) => this.emit('disconnected', reason));
53
- this.wsClient.on('reconnecting', (attempt, delay) => this.emit('reconnecting', attempt, delay));
54
- this.wsClient.on('reconnected', () => this.emit('reconnected'));
55
- this.wsClient.on('reconnectionFailed', (attempts) => this.emit('reconnectionFailed', attempts));
56
- this.wsClient.on('connectionStateChanged', (state) => this.emit('connectionStateChanged', state));
57
- this.wsClient.on('error', (error) => this.emit('error', error));
58
- this.wsClient.on('heartbeat:sent', () => this.emit('heartbeat:sent'));
59
- this.wsClient.on('heartbeat:timeout', () => this.emit('heartbeat:timeout'));
60
- this.wsClient.on('hello', (data) => this.emit('hello', data));
61
- // Forward events from managers
62
- this.powerManager.on('power:changed', (state) => this.emit('power:changed', state));
63
- this.turnoutManager.on('turnout:changed', (name, state) => this.emit('turnout:changed', name, state));
64
- this.lightManager.on('light:changed', (name, state) => this.emit('light:changed', name, state));
65
- this.throttleManager.on('throttle:acquired', (id) => this.emit('throttle:acquired', id));
66
- this.throttleManager.on('throttle:updated', (id, data) => this.emit('throttle:updated', id, data));
67
- this.throttleManager.on('throttle:released', (id) => this.emit('throttle:released', id));
68
- this.throttleManager.on('throttle:lost', (id) => this.emit('throttle:lost', id));
69
- // Auto-connect if enabled
70
- if (this.options.autoConnect) {
71
- this.connect().catch((error) => {
72
- this.emit('error', error);
73
- });
74
- }
75
- }
76
- /**
77
- * Connect to JMRI server
78
- */
79
- async connect() {
80
- return this.wsClient.connect();
81
- }
82
- /**
83
- * Disconnect from JMRI server
84
- * Releases all throttles and closes connection
85
- */
86
- async disconnect() {
87
- // Release all throttles first
88
- await this.throttleManager.releaseAllThrottles();
89
- // Disconnect WebSocket
90
- return this.wsClient.disconnect();
91
- }
92
- /**
93
- * Get current connection state
94
- */
95
- getConnectionState() {
96
- return this.wsClient.getState();
97
- }
98
- /**
99
- * Check if connected to JMRI
100
- */
101
- isConnected() {
102
- return this.wsClient.isConnected();
103
- }
104
- // ============================================================================
105
- // Power Control
106
- // ============================================================================
107
- /**
108
- * Get current track power state
109
- * @param prefix - Optional JMRI connection prefix to target a specific hardware connection
110
- */
111
- async getPower(prefix) {
112
- return this.powerManager.getPower(prefix);
113
- }
114
- /**
115
- * Set track power state
116
- * @param state - The desired power state
117
- * @param prefix - Optional JMRI connection prefix to target a specific hardware connection
118
- */
119
- async setPower(state, prefix) {
120
- return this.powerManager.setPower(state, prefix);
121
- }
122
- /**
123
- * Turn track power on
124
- * @param prefix - Optional JMRI connection prefix to target a specific hardware connection
125
- */
126
- async powerOn(prefix) {
127
- return this.powerManager.powerOn(prefix);
128
- }
129
- /**
130
- * Turn track power off
131
- * @param prefix - Optional JMRI connection prefix to target a specific hardware connection
132
- */
133
- async powerOff(prefix) {
134
- return this.powerManager.powerOff(prefix);
135
- }
136
- // ============================================================================
137
- // System Connections
138
- // ============================================================================
139
- /**
140
- * List all available JMRI system connections and their prefixes.
141
- * Use the returned prefix values with power and throttle commands to
142
- * target a specific hardware connection when multiple are configured.
143
- *
144
- * @example
145
- * ```typescript
146
- * const connections = await client.getSystemConnections();
147
- * // [{ name: 'LocoNet', prefix: 'L' }, { name: 'DCC++', prefix: 'D' }]
148
- * await client.powerOn('L'); // power on via LocoNet only
149
- * ```
150
- */
151
- async getSystemConnections() {
152
- return this.systemConnectionsManager.getSystemConnections();
153
- }
154
- // ============================================================================
155
- // Roster Management
156
- // ============================================================================
157
- /**
158
- * Get all roster entries
159
- */
160
- async getRoster() {
161
- return this.rosterManager.getRoster();
162
- }
163
- /**
164
- * Get roster entry by name
165
- */
166
- async getRosterEntryByName(name) {
167
- return this.rosterManager.getRosterEntryByName(name);
168
- }
169
- /**
170
- * Get roster entry by address
171
- */
172
- async getRosterEntryByAddress(address) {
173
- return this.rosterManager.getRosterEntryByAddress(address);
174
- }
175
- /**
176
- * Search roster by partial name match
177
- */
178
- async searchRoster(query) {
179
- return this.rosterManager.searchRoster(query);
180
- }
181
- // ============================================================================
182
- // Turnout Control
183
- // ============================================================================
184
- /**
185
- * Get the current state of a turnout
186
- */
187
- async getTurnout(name) {
188
- return this.turnoutManager.getTurnout(name);
189
- }
190
- /**
191
- * Set a turnout to the given state
192
- */
193
- async setTurnout(name, state) {
194
- return this.turnoutManager.setTurnout(name, state);
195
- }
196
- /**
197
- * Throw a turnout (diverging route)
198
- */
199
- async throwTurnout(name) {
200
- return this.turnoutManager.throwTurnout(name);
201
- }
202
- /**
203
- * Close a turnout (straight through / normal)
204
- */
205
- async closeTurnout(name) {
206
- return this.turnoutManager.closeTurnout(name);
207
- }
208
- /**
209
- * List all turnouts known to JMRI
210
- */
211
- async listTurnouts() {
212
- return this.turnoutManager.listTurnouts();
213
- }
214
- /**
215
- * Get cached turnout state without a network request
216
- */
217
- getTurnoutState(name) {
218
- return this.turnoutManager.getTurnoutState(name);
219
- }
220
- /**
221
- * Get all cached turnout states
222
- */
223
- getCachedTurnouts() {
224
- return this.turnoutManager.getCachedTurnouts();
225
- }
226
- // ============================================================================
227
- // Light Control
228
- // ============================================================================
229
- /**
230
- * Get the current state of a light
231
- */
232
- async getLight(name) {
233
- return this.lightManager.getLight(name);
234
- }
235
- /**
236
- * Set a light to the given state
237
- */
238
- async setLight(name, state) {
239
- return this.lightManager.setLight(name, state);
240
- }
241
- /**
242
- * Turn a light on
243
- */
244
- async turnOnLight(name) {
245
- return this.lightManager.turnOnLight(name);
246
- }
247
- /**
248
- * Turn a light off
249
- */
250
- async turnOffLight(name) {
251
- return this.lightManager.turnOffLight(name);
252
- }
253
- /**
254
- * List all lights known to JMRI
255
- */
256
- async listLights() {
257
- return this.lightManager.listLights();
258
- }
259
- /**
260
- * Get cached light state without a network request
261
- */
262
- getLightState(name) {
263
- return this.lightManager.getLightState(name);
264
- }
265
- /**
266
- * Get all cached light states
267
- */
268
- getCachedLights() {
269
- return this.lightManager.getCachedLights();
270
- }
271
- // ============================================================================
272
- // Throttle Control
273
- // ============================================================================
274
- /**
275
- * Acquire a throttle for a locomotive
276
- *
277
- * @param options - Throttle acquisition options
278
- * @returns Throttle ID for use in other throttle methods
279
- *
280
- * @example
281
- * ```typescript
282
- * const throttleId = await client.acquireThrottle({ address: 3 });
283
- * await client.setThrottleSpeed(throttleId, 0.5); // Half speed
284
- * ```
285
- */
286
- async acquireThrottle(options) {
287
- return this.throttleManager.acquireThrottle(options);
288
- }
289
- /**
290
- * Release a throttle
291
- */
292
- async releaseThrottle(throttleId) {
293
- return this.throttleManager.releaseThrottle(throttleId);
294
- }
295
- /**
296
- * Set throttle speed (0.0 to 1.0)
297
- *
298
- * @param throttleId - Throttle ID from acquireThrottle
299
- * @param speed - Speed value between 0.0 (stopped) and 1.0 (full speed)
300
- */
301
- async setThrottleSpeed(throttleId, speed) {
302
- return this.throttleManager.setSpeed(throttleId, speed);
303
- }
304
- /**
305
- * Set throttle direction
306
- *
307
- * @param throttleId - Throttle ID from acquireThrottle
308
- * @param forward - True for forward, false for reverse
309
- */
310
- async setThrottleDirection(throttleId, forward) {
311
- return this.throttleManager.setDirection(throttleId, forward);
312
- }
313
- /**
314
- * Set throttle function (F0-F28)
315
- *
316
- * @param throttleId - Throttle ID from acquireThrottle
317
- * @param functionKey - Function key (F0-F28)
318
- * @param value - True to activate, false to deactivate
319
- *
320
- * @example
321
- * ```typescript
322
- * await client.setThrottleFunction(throttleId, 'F0', true); // Headlight on
323
- * await client.setThrottleFunction(throttleId, 'F2', true); // Horn
324
- * ```
325
- */
326
- async setThrottleFunction(throttleId, functionKey, value) {
327
- return this.throttleManager.setFunction(throttleId, functionKey, value);
328
- }
329
- /**
330
- * Emergency stop for a throttle (speed to 0)
331
- */
332
- async emergencyStop(throttleId) {
333
- return this.throttleManager.emergencyStop(throttleId);
334
- }
335
- /**
336
- * Set throttle to idle (speed to 0, maintain direction)
337
- */
338
- async idleThrottle(throttleId) {
339
- return this.throttleManager.idle(throttleId);
340
- }
341
- /**
342
- * Get throttle state
343
- */
344
- getThrottleState(throttleId) {
345
- return this.throttleManager.getThrottleState(throttleId);
346
- }
347
- /**
348
- * Get all throttle IDs
349
- */
350
- getThrottleIds() {
351
- return this.throttleManager.getThrottleIds();
352
- }
353
- /**
354
- * Get all throttle states
355
- */
356
- getAllThrottles() {
357
- return this.throttleManager.getAllThrottles();
358
- }
359
- /**
360
- * Release all throttles
361
- */
362
- async releaseAllThrottles() {
363
- return this.throttleManager.releaseAllThrottles();
364
- }
365
- }
366
- exports.JmriClient = JmriClient;