@ya-modbus/mqtt-bridge 0.4.1-refactor-scope-driver-packages.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 (65) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +674 -0
  3. package/README.md +190 -0
  4. package/dist/bin/ya-modbus-bridge.d.ts +9 -0
  5. package/dist/bin/ya-modbus-bridge.d.ts.map +1 -0
  6. package/dist/bin/ya-modbus-bridge.js +10 -0
  7. package/dist/bin/ya-modbus-bridge.js.map +1 -0
  8. package/dist/src/cli.d.ts +4 -0
  9. package/dist/src/cli.d.ts.map +1 -0
  10. package/dist/src/cli.js +109 -0
  11. package/dist/src/cli.js.map +1 -0
  12. package/dist/src/device-manager.d.ts +17 -0
  13. package/dist/src/device-manager.d.ts.map +1 -0
  14. package/dist/src/device-manager.js +79 -0
  15. package/dist/src/device-manager.js.map +1 -0
  16. package/dist/src/driver-loader.d.ts +53 -0
  17. package/dist/src/driver-loader.d.ts.map +1 -0
  18. package/dist/src/driver-loader.js +120 -0
  19. package/dist/src/driver-loader.js.map +1 -0
  20. package/dist/src/index.d.ts +13 -0
  21. package/dist/src/index.d.ts.map +1 -0
  22. package/dist/src/index.js +285 -0
  23. package/dist/src/index.js.map +1 -0
  24. package/dist/src/polling-scheduler.d.ts +48 -0
  25. package/dist/src/polling-scheduler.d.ts.map +1 -0
  26. package/dist/src/polling-scheduler.js +128 -0
  27. package/dist/src/polling-scheduler.js.map +1 -0
  28. package/dist/src/types.d.ts +86 -0
  29. package/dist/src/types.d.ts.map +1 -0
  30. package/dist/src/types.js +2 -0
  31. package/dist/src/types.js.map +1 -0
  32. package/dist/src/utils/__mocks__/package-info.d.ts +9 -0
  33. package/dist/src/utils/__mocks__/package-info.d.ts.map +1 -0
  34. package/dist/src/utils/__mocks__/package-info.js +11 -0
  35. package/dist/src/utils/__mocks__/package-info.js.map +1 -0
  36. package/dist/src/utils/__mocks__/process.d.ts +20 -0
  37. package/dist/src/utils/__mocks__/process.d.ts.map +1 -0
  38. package/dist/src/utils/__mocks__/process.js +37 -0
  39. package/dist/src/utils/__mocks__/process.js.map +1 -0
  40. package/dist/src/utils/config-validator.d.ts +3 -0
  41. package/dist/src/utils/config-validator.d.ts.map +1 -0
  42. package/dist/src/utils/config-validator.js +32 -0
  43. package/dist/src/utils/config-validator.js.map +1 -0
  44. package/dist/src/utils/config.d.ts +3 -0
  45. package/dist/src/utils/config.d.ts.map +1 -0
  46. package/dist/src/utils/config.js +52 -0
  47. package/dist/src/utils/config.js.map +1 -0
  48. package/dist/src/utils/device-validation.d.ts +31 -0
  49. package/dist/src/utils/device-validation.d.ts.map +1 -0
  50. package/dist/src/utils/device-validation.js +70 -0
  51. package/dist/src/utils/device-validation.js.map +1 -0
  52. package/dist/src/utils/package-info.d.ts +5 -0
  53. package/dist/src/utils/package-info.d.ts.map +1 -0
  54. package/dist/src/utils/package-info.js +11 -0
  55. package/dist/src/utils/package-info.js.map +1 -0
  56. package/dist/src/utils/process.d.ts +10 -0
  57. package/dist/src/utils/process.d.ts.map +1 -0
  58. package/dist/src/utils/process.js +13 -0
  59. package/dist/src/utils/process.js.map +1 -0
  60. package/dist/src/utils/test-utils.d.ts +313 -0
  61. package/dist/src/utils/test-utils.d.ts.map +1 -0
  62. package/dist/src/utils/test-utils.js +535 -0
  63. package/dist/src/utils/test-utils.js.map +1 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -0
  65. package/package.json +63 -0
@@ -0,0 +1,285 @@
1
+ import mqtt from 'mqtt';
2
+ import { DeviceManager } from './device-manager.js';
3
+ import { DriverLoader } from './driver-loader.js';
4
+ import { PollingScheduler } from './polling-scheduler.js';
5
+ export { loadConfig } from './utils/config.js';
6
+ export function createBridge(config, dependencies) {
7
+ let status = {
8
+ state: 'stopped',
9
+ timestamp: Date.now(),
10
+ };
11
+ let client = null;
12
+ const topicPrefix = config.topicPrefix ?? 'modbus';
13
+ // Subscriptions map needed because mqtt client doesn't expose handler lookup
14
+ const subscriptions = new Map();
15
+ // Use injected dependencies or create new instances
16
+ const driverLoader = dependencies?.driverLoader ?? new DriverLoader();
17
+ const deviceManager = dependencies?.deviceManager ?? new DeviceManager(driverLoader);
18
+ // Bridge reference to be set after bridge object is created
19
+ // This ensures publishData is always defined before PollingScheduler starts
20
+ let bridgeRef = null;
21
+ // Publish function that will be called from polling
22
+ const publishData = (deviceId, data) => {
23
+ if (!bridgeRef) {
24
+ // Bridge not fully initialized yet - skip publishing
25
+ return;
26
+ }
27
+ const payload = JSON.stringify({
28
+ deviceId,
29
+ timestamp: Date.now(),
30
+ data,
31
+ });
32
+ // Publish to device-specific topic
33
+ void bridgeRef.publish(`${deviceId}/data`, payload, { qos: 0 }).catch((error) => {
34
+ console.error(`Failed to publish data for device ${deviceId}:`, error);
35
+ });
36
+ };
37
+ // Handle data from polling
38
+ const handlePollingData = (deviceId, data) => {
39
+ publishData(deviceId, data);
40
+ // Update device status and reset failure count on success
41
+ deviceManager.updateDeviceState(deviceId, {
42
+ lastPoll: Date.now(),
43
+ lastUpdate: Date.now(),
44
+ consecutiveFailures: 0,
45
+ });
46
+ };
47
+ // Handle polling errors
48
+ const handlePollingError = (deviceId, error, failureCount) => {
49
+ console.error(`Polling error for device ${deviceId}:`, error);
50
+ deviceManager.updateDeviceState(deviceId, {
51
+ consecutiveFailures: failureCount,
52
+ errors: [error.message],
53
+ });
54
+ };
55
+ const pollingScheduler = dependencies?.pollingScheduler ?? new PollingScheduler(handlePollingData, handlePollingError);
56
+ const bridge = {
57
+ start() {
58
+ return new Promise((resolve, reject) => {
59
+ status = {
60
+ state: 'starting',
61
+ timestamp: Date.now(),
62
+ };
63
+ const mqttOptions = {
64
+ clean: true,
65
+ reconnectPeriod: config.mqtt.reconnectPeriod ?? 5000,
66
+ resubscribe: true, // Automatic resubscription on reconnect (default: true)
67
+ };
68
+ if (config.mqtt.clientId) {
69
+ mqttOptions.clientId = config.mqtt.clientId;
70
+ }
71
+ if (config.mqtt.username) {
72
+ mqttOptions.username = config.mqtt.username;
73
+ }
74
+ if (config.mqtt.password) {
75
+ mqttOptions.password = config.mqtt.password;
76
+ }
77
+ client = mqtt.connect(config.mqtt.url, mqttOptions);
78
+ let isInitialConnection = true;
79
+ client.on('connect', () => {
80
+ status = {
81
+ state: 'running',
82
+ timestamp: Date.now(),
83
+ };
84
+ // Automatic resubscription is handled by mqtt.js (resubscribe: true)
85
+ // Start polling scheduler
86
+ pollingScheduler.start();
87
+ if (isInitialConnection) {
88
+ isInitialConnection = false;
89
+ resolve();
90
+ }
91
+ });
92
+ client.on('error', (error) => {
93
+ status = {
94
+ state: 'error',
95
+ timestamp: Date.now(),
96
+ errors: [error.message],
97
+ };
98
+ reject(error);
99
+ });
100
+ // No need to track disconnect/offline - client.connected property handles this
101
+ client.on('reconnect', () => {
102
+ // Note: reconnect event fires when attempting to reconnect
103
+ // mqttConnected status is updated by 'connect' event
104
+ });
105
+ client.on('message', (topic, payload, packet) => {
106
+ const handler = subscriptions.get(topic);
107
+ if (handler) {
108
+ const message = {
109
+ topic,
110
+ payload,
111
+ qos: packet.qos,
112
+ retain: packet.retain,
113
+ };
114
+ try {
115
+ handler(message);
116
+ }
117
+ catch (error) {
118
+ // Prevent handler errors from crashing the bridge
119
+ console.error(`Error in message handler for topic ${topic}:`, error);
120
+ status = {
121
+ ...status,
122
+ errors: [
123
+ ...(status.errors ?? []),
124
+ /* istanbul ignore next - defensive: handlers always throw Error */
125
+ `Handler error for ${topic}: ${error instanceof Error ? error.message : String(error)}`,
126
+ ],
127
+ };
128
+ }
129
+ }
130
+ });
131
+ });
132
+ },
133
+ async stop() {
134
+ status = {
135
+ state: 'stopping',
136
+ timestamp: Date.now(),
137
+ };
138
+ // Stop polling first
139
+ pollingScheduler.stop();
140
+ // Cleanup device manager (unloads drivers)
141
+ await deviceManager.clear();
142
+ // Close all transports managed by the transport manager
143
+ await driverLoader.closeAllTransports();
144
+ if (client) {
145
+ // Cleanup event listeners and subscriptions to prevent memory leaks
146
+ client.removeAllListeners();
147
+ subscriptions.clear();
148
+ await new Promise((resolve) => {
149
+ if (!client) {
150
+ resolve();
151
+ return;
152
+ }
153
+ client.end(false, {}, () => {
154
+ status = {
155
+ state: 'stopped',
156
+ timestamp: Date.now(),
157
+ };
158
+ client = null;
159
+ resolve();
160
+ });
161
+ });
162
+ }
163
+ else {
164
+ status = {
165
+ state: 'stopped',
166
+ timestamp: Date.now(),
167
+ };
168
+ }
169
+ },
170
+ getStatus() {
171
+ return {
172
+ ...status,
173
+ deviceCount: deviceManager.getDeviceCount(),
174
+ // Use client.connected property directly
175
+ mqttConnected: client?.connected ?? false,
176
+ };
177
+ },
178
+ publish(topic, payload, options) {
179
+ return new Promise((resolve, reject) => {
180
+ if (!client) {
181
+ reject(new Error('MQTT client not initialized'));
182
+ return;
183
+ }
184
+ if (!client.connected) {
185
+ reject(new Error('MQTT client not connected'));
186
+ return;
187
+ }
188
+ const fullTopic = `${topicPrefix}/${topic}`;
189
+ const publishOptions = {};
190
+ if (options?.qos !== undefined) {
191
+ publishOptions.qos = options.qos;
192
+ }
193
+ if (options?.retain !== undefined) {
194
+ publishOptions.retain = options.retain;
195
+ }
196
+ client.publish(fullTopic, payload, publishOptions, (error) => {
197
+ if (error) {
198
+ reject(error);
199
+ }
200
+ else {
201
+ resolve();
202
+ }
203
+ });
204
+ });
205
+ },
206
+ subscribe(topic, handler, options) {
207
+ return new Promise((resolve, reject) => {
208
+ if (!client) {
209
+ reject(new Error('MQTT client not initialized'));
210
+ return;
211
+ }
212
+ if (!client.connected) {
213
+ reject(new Error('MQTT client not connected'));
214
+ return;
215
+ }
216
+ const fullTopic = `${topicPrefix}/${topic}`;
217
+ const subscribeOptions = {
218
+ qos: options?.qos ?? 0,
219
+ };
220
+ client.subscribe(fullTopic, subscribeOptions, (error) => {
221
+ if (error) {
222
+ reject(error);
223
+ }
224
+ else {
225
+ subscriptions.set(fullTopic, handler);
226
+ resolve();
227
+ }
228
+ });
229
+ });
230
+ },
231
+ unsubscribe(topic) {
232
+ return new Promise((resolve, reject) => {
233
+ if (!client) {
234
+ reject(new Error('MQTT client not initialized'));
235
+ return;
236
+ }
237
+ if (!client.connected) {
238
+ reject(new Error('MQTT client not connected'));
239
+ return;
240
+ }
241
+ const fullTopic = `${topicPrefix}/${topic}`;
242
+ client.unsubscribe(fullTopic, (error) => {
243
+ if (error) {
244
+ reject(error);
245
+ }
246
+ else {
247
+ subscriptions.delete(fullTopic);
248
+ resolve();
249
+ }
250
+ });
251
+ });
252
+ },
253
+ async addDevice(deviceConfig) {
254
+ await deviceManager.addDevice(deviceConfig);
255
+ // Schedule device for polling if enabled
256
+ if (deviceConfig.enabled !== false) {
257
+ const driver = driverLoader.getDriver(deviceConfig.deviceId);
258
+ if (!driver) {
259
+ throw new Error(`Driver for ${deviceConfig.deviceId} not found after loading. ` +
260
+ `This may indicate the device was removed during initialization.`);
261
+ }
262
+ pollingScheduler.scheduleDevice(deviceConfig.deviceId, deviceConfig, driver);
263
+ }
264
+ },
265
+ async removeDevice(deviceId) {
266
+ // Unschedule polling first
267
+ pollingScheduler.unscheduleDevice(deviceId);
268
+ // Remove device (unloads driver)
269
+ await deviceManager.removeDevice(deviceId);
270
+ },
271
+ getDevice(deviceId) {
272
+ return deviceManager.getDevice(deviceId);
273
+ },
274
+ listDevices() {
275
+ return deviceManager.listDevices();
276
+ },
277
+ getDeviceConfig(deviceId) {
278
+ return deviceManager.getDeviceConfig(deviceId);
279
+ },
280
+ };
281
+ // Set bridge reference now that bridge is defined
282
+ bridgeRef = bridge;
283
+ return bridge;
284
+ }
285
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { DeviceDriver } from '@ya-modbus/driver-types';
2
+ import type { DeviceConfig } from './types.js';
3
+ type DataCallback = (deviceId: string, data: Record<string, unknown>) => void;
4
+ type ErrorCallback = (deviceId: string, error: Error, failureCount: number) => void;
5
+ /**
6
+ * Coordinates polling across multiple devices
7
+ */
8
+ export declare class PollingScheduler {
9
+ private onData;
10
+ private onError;
11
+ private devices;
12
+ private running;
13
+ constructor(onData: DataCallback, onError: ErrorCallback);
14
+ /**
15
+ * Schedule a device for polling
16
+ */
17
+ scheduleDevice(deviceId: string, config: DeviceConfig, driver: DeviceDriver): void;
18
+ /**
19
+ * Stop polling a device and remove it from the schedule
20
+ */
21
+ unscheduleDevice(deviceId: string): void;
22
+ /**
23
+ * Start polling all scheduled devices
24
+ */
25
+ start(): void;
26
+ /**
27
+ * Stop polling all devices
28
+ */
29
+ stop(): void;
30
+ /**
31
+ * Check if a device is scheduled for polling
32
+ */
33
+ isScheduled(deviceId: string): boolean;
34
+ /**
35
+ * Start polling for a specific device
36
+ */
37
+ private startDevicePolling;
38
+ /**
39
+ * Schedule the next poll for a device, with backoff if needed
40
+ */
41
+ private scheduleNextPoll;
42
+ /**
43
+ * Poll a device once
44
+ */
45
+ private pollDevice;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=polling-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling-scheduler.d.ts","sourceRoot":"","sources":["../../src/polling-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAU9C,KAAK,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;AAC7E,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;AAInF;;GAEG;AACH,qBAAa,gBAAgB;IAKzB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO;IALjB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAQ;gBAGb,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa;IAGhC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAelF;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQxC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;YACW,UAAU;CAyCzB"}
@@ -0,0 +1,128 @@
1
+ const DEFAULT_POLLING_INTERVAL = 5000; // 5 seconds
2
+ /**
3
+ * Coordinates polling across multiple devices
4
+ */
5
+ export class PollingScheduler {
6
+ onData;
7
+ onError;
8
+ devices = new Map();
9
+ running = false;
10
+ constructor(onData, onError) {
11
+ this.onData = onData;
12
+ this.onError = onError;
13
+ }
14
+ /**
15
+ * Schedule a device for polling
16
+ */
17
+ scheduleDevice(deviceId, config, driver) {
18
+ const interval = config.polling?.interval ?? DEFAULT_POLLING_INTERVAL;
19
+ this.devices.set(deviceId, {
20
+ config,
21
+ driver,
22
+ interval,
23
+ lastFailureCount: 0,
24
+ });
25
+ if (this.running) {
26
+ this.startDevicePolling(deviceId);
27
+ }
28
+ }
29
+ /**
30
+ * Stop polling a device and remove it from the schedule
31
+ */
32
+ unscheduleDevice(deviceId) {
33
+ const device = this.devices.get(deviceId);
34
+ if (device?.timer) {
35
+ clearTimeout(device.timer);
36
+ }
37
+ this.devices.delete(deviceId);
38
+ }
39
+ /**
40
+ * Start polling all scheduled devices
41
+ */
42
+ start() {
43
+ this.running = true;
44
+ for (const deviceId of this.devices.keys()) {
45
+ this.startDevicePolling(deviceId);
46
+ }
47
+ }
48
+ /**
49
+ * Stop polling all devices
50
+ */
51
+ stop() {
52
+ this.running = false;
53
+ for (const device of this.devices.values()) {
54
+ if (device.timer) {
55
+ clearTimeout(device.timer);
56
+ delete device.timer;
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Check if a device is scheduled for polling
62
+ */
63
+ isScheduled(deviceId) {
64
+ return this.devices.has(deviceId);
65
+ }
66
+ /**
67
+ * Start polling for a specific device
68
+ */
69
+ startDevicePolling(deviceId) {
70
+ const device = this.devices.get(deviceId);
71
+ if (!device)
72
+ return;
73
+ // Schedule first poll after the interval
74
+ this.scheduleNextPoll(deviceId);
75
+ }
76
+ /**
77
+ * Schedule the next poll for a device, with backoff if needed
78
+ */
79
+ scheduleNextPoll(deviceId) {
80
+ const device = this.devices.get(deviceId);
81
+ if (!device || !this.running)
82
+ return;
83
+ const maxRetries = device.config.polling?.maxRetries ?? 3;
84
+ const retryBackoff = device.config.polling?.retryBackoff ?? device.interval * 2;
85
+ // Use backoff if we've exceeded max retries
86
+ const delay = device.lastFailureCount >= maxRetries ? retryBackoff : device.interval;
87
+ device.timer = setTimeout(() => {
88
+ const currentDevice = this.devices.get(deviceId);
89
+ if (!currentDevice || !this.running)
90
+ return;
91
+ void this.pollDevice(deviceId);
92
+ }, delay);
93
+ }
94
+ /**
95
+ * Poll a device once
96
+ */
97
+ async pollDevice(deviceId) {
98
+ const device = this.devices.get(deviceId);
99
+ if (!device || !this.running)
100
+ return;
101
+ try {
102
+ const dataPointIds = device.driver.dataPoints.map((dp) => dp.id);
103
+ const data = await device.driver.readDataPoints(dataPointIds);
104
+ // Reset failure count BEFORE callback - polling succeeded regardless of callback result
105
+ device.lastFailureCount = 0;
106
+ try {
107
+ this.onData(deviceId, data);
108
+ }
109
+ catch (callbackError) {
110
+ // Data callback threw - log but don't count as polling failure
111
+ console.error(`Error in data callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
112
+ }
113
+ }
114
+ catch (error) {
115
+ // Increment failure count - scheduler owns this state
116
+ device.lastFailureCount++;
117
+ try {
118
+ this.onError(deviceId, error instanceof Error ? error : new Error(String(error)), device.lastFailureCount);
119
+ }
120
+ catch (callbackError) {
121
+ // Error callback threw - log but state already updated
122
+ console.error(`Error in error callback for device ${deviceId}:`, callbackError instanceof Error ? callbackError : new Error(String(callbackError)));
123
+ }
124
+ }
125
+ this.scheduleNextPoll(deviceId);
126
+ }
127
+ }
128
+ //# sourceMappingURL=polling-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polling-scheduler.js","sourceRoot":"","sources":["../../src/polling-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ export interface MqttBridgeConfig {
2
+ mqtt: {
3
+ url: string;
4
+ clientId?: string;
5
+ username?: string;
6
+ password?: string;
7
+ reconnectPeriod?: number;
8
+ };
9
+ stateDir?: string;
10
+ topicPrefix?: string;
11
+ }
12
+ export interface BridgeStatus {
13
+ state: 'starting' | 'running' | 'stopping' | 'stopped' | 'error';
14
+ timestamp: number;
15
+ deviceCount: number;
16
+ mqttConnected?: boolean;
17
+ errors?: string[];
18
+ }
19
+ export interface PublishOptions {
20
+ qos?: 0 | 1 | 2;
21
+ retain?: boolean;
22
+ }
23
+ export interface SubscribeOptions {
24
+ qos?: 0 | 1 | 2;
25
+ }
26
+ export interface MqttMessage {
27
+ topic: string;
28
+ payload: Buffer;
29
+ qos: 0 | 1 | 2;
30
+ retain: boolean;
31
+ }
32
+ export type MessageHandler = (message: MqttMessage) => void;
33
+ export interface PollingConfig {
34
+ interval: number;
35
+ maxRetries?: number;
36
+ retryBackoff?: number;
37
+ }
38
+ export interface DeviceConfig {
39
+ deviceId: string;
40
+ driver: string;
41
+ connection: DeviceConnection;
42
+ enabled?: boolean;
43
+ polling?: PollingConfig;
44
+ }
45
+ export type DeviceConnection = RTUConnection | TCPConnection;
46
+ export interface RTUConnection {
47
+ type: 'rtu';
48
+ port: string;
49
+ baudRate: number;
50
+ slaveId: number;
51
+ parity: 'none' | 'even' | 'odd';
52
+ dataBits: 7 | 8;
53
+ stopBits: 1 | 2;
54
+ timeout?: number;
55
+ }
56
+ export interface TCPConnection {
57
+ type: 'tcp';
58
+ host: string;
59
+ port?: number;
60
+ slaveId: number;
61
+ timeout?: number;
62
+ }
63
+ export interface DeviceStatus {
64
+ deviceId: string;
65
+ state: 'connecting' | 'connected' | 'disconnected' | 'error';
66
+ enabled: boolean;
67
+ connected: boolean;
68
+ lastUpdate?: number;
69
+ lastPoll?: number;
70
+ consecutiveFailures?: number;
71
+ errors?: string[];
72
+ }
73
+ export interface MqttBridge {
74
+ start(): Promise<void>;
75
+ stop(): Promise<void>;
76
+ getStatus(): BridgeStatus;
77
+ publish(topic: string, payload: string | Buffer, options?: PublishOptions): Promise<void>;
78
+ subscribe(topic: string, handler: MessageHandler, options?: SubscribeOptions): Promise<void>;
79
+ unsubscribe(topic: string): Promise<void>;
80
+ addDevice(config: DeviceConfig): Promise<void>;
81
+ removeDevice(deviceId: string): Promise<void>;
82
+ getDevice(deviceId: string): DeviceStatus | undefined;
83
+ listDevices(): DeviceStatus[];
84
+ getDeviceConfig(deviceId: string): DeviceConfig | undefined;
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Auto-mock for package info utilities
3
+ * Used in tests via jest.mock('./utils/package-info.js')
4
+ */
5
+ export declare function getPackageInfo(): {
6
+ version: string;
7
+ description: string;
8
+ };
9
+ //# sourceMappingURL=package-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-info.d.ts","sourceRoot":"","sources":["../../../../src/utils/__mocks__/package-info.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAMzE"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Auto-mock for package info utilities
3
+ * Used in tests via jest.mock('./utils/package-info.js')
4
+ */
5
+ export function getPackageInfo() {
6
+ return {
7
+ version: '0.0.0',
8
+ description: 'MQTT bridge for ya-modbus - orchestrates device management, polling, and MQTT publishing',
9
+ };
10
+ }
11
+ //# sourceMappingURL=package-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-info.js","sourceRoot":"","sources":["../../../../src/utils/__mocks__/package-info.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,WAAW,EACT,0FAA0F;KAC7F,CAAA;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Auto-mock for process utilities
3
+ * Used in tests via jest.mock('./utils/process.js')
4
+ */
5
+ import type { ProcessUtils } from '../process.js';
6
+ /**
7
+ * Map of registered signal handlers
8
+ * Populated by onSignal mock implementation
9
+ * Automatically cleared before each test
10
+ */
11
+ export declare const signalHandlers: Map<string, () => void>;
12
+ /**
13
+ * Trigger a signal handler that was registered via onSignal
14
+ *
15
+ * @param signal - The signal to trigger (e.g., 'SIGINT', 'SIGTERM')
16
+ * @returns true if handler was found and called, false otherwise
17
+ */
18
+ export declare function triggerSignal(signal: string): boolean;
19
+ export declare const processUtils: ProcessUtils;
20
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../../src/utils/__mocks__/process.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,oBAAyB,IAAI,CAAG,CAAA;AAE3D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOrD;AAQD,eAAO,MAAM,YAAY,EAAE,YAK1B,CAAA"}