@ecobridge.xyz/devicemanager 3.0.2

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 (137) hide show
  1. package/.gitea/workflows/default_nottags.yaml +66 -0
  2. package/.gitea/workflows/default_tags.yaml +124 -0
  3. package/.vscode/launch.json +11 -0
  4. package/.vscode/settings.json +26 -0
  5. package/changelog.md +91 -0
  6. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  7. package/dist_ts/00_commitinfo_data.js +9 -0
  8. package/dist_ts/device/device.classes.device.d.ts +205 -0
  9. package/dist_ts/device/device.classes.device.js +344 -0
  10. package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
  11. package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
  12. package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
  13. package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
  14. package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
  15. package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
  16. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
  17. package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
  18. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
  19. package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
  20. package/dist_ts/factories/index.d.ts +190 -0
  21. package/dist_ts/factories/index.js +350 -0
  22. package/dist_ts/features/feature.abstract.d.ts +118 -0
  23. package/dist_ts/features/feature.abstract.js +172 -0
  24. package/dist_ts/features/feature.camera.d.ts +95 -0
  25. package/dist_ts/features/feature.camera.js +162 -0
  26. package/dist_ts/features/feature.climate.d.ts +174 -0
  27. package/dist_ts/features/feature.climate.js +328 -0
  28. package/dist_ts/features/feature.cover.d.ts +121 -0
  29. package/dist_ts/features/feature.cover.js +217 -0
  30. package/dist_ts/features/feature.fan.d.ts +127 -0
  31. package/dist_ts/features/feature.fan.js +235 -0
  32. package/dist_ts/features/feature.light.d.ts +156 -0
  33. package/dist_ts/features/feature.light.js +296 -0
  34. package/dist_ts/features/feature.lock.d.ts +103 -0
  35. package/dist_ts/features/feature.lock.js +170 -0
  36. package/dist_ts/features/feature.playback.d.ts +98 -0
  37. package/dist_ts/features/feature.playback.js +191 -0
  38. package/dist_ts/features/feature.power.d.ts +79 -0
  39. package/dist_ts/features/feature.power.js +173 -0
  40. package/dist_ts/features/feature.print.d.ts +78 -0
  41. package/dist_ts/features/feature.print.js +226 -0
  42. package/dist_ts/features/feature.scan.d.ts +79 -0
  43. package/dist_ts/features/feature.scan.js +315 -0
  44. package/dist_ts/features/feature.sensor.d.ts +95 -0
  45. package/dist_ts/features/feature.sensor.js +147 -0
  46. package/dist_ts/features/feature.snmp.d.ts +88 -0
  47. package/dist_ts/features/feature.snmp.js +175 -0
  48. package/dist_ts/features/feature.switch.d.ts +75 -0
  49. package/dist_ts/features/feature.switch.js +126 -0
  50. package/dist_ts/features/feature.volume.d.ts +104 -0
  51. package/dist_ts/features/feature.volume.js +186 -0
  52. package/dist_ts/features/index.d.ts +19 -0
  53. package/dist_ts/features/index.js +23 -0
  54. package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
  55. package/dist_ts/helpers/helpers.iprange.js +151 -0
  56. package/dist_ts/helpers/helpers.retry.d.ts +25 -0
  57. package/dist_ts/helpers/helpers.retry.js +69 -0
  58. package/dist_ts/index.d.ts +15 -0
  59. package/dist_ts/index.js +40 -0
  60. package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
  61. package/dist_ts/interfaces/feature.interfaces.js +6 -0
  62. package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
  63. package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
  64. package/dist_ts/interfaces/index.d.ts +330 -0
  65. package/dist_ts/interfaces/index.js +16 -0
  66. package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
  67. package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
  68. package/dist_ts/paths.d.ts +1 -0
  69. package/dist_ts/paths.js +3 -0
  70. package/dist_ts/plugins.d.ts +23 -0
  71. package/dist_ts/plugins.js +30 -0
  72. package/dist_ts/protocols/index.d.ts +12 -0
  73. package/dist_ts/protocols/index.js +21 -0
  74. package/dist_ts/protocols/protocol.escl.d.ts +58 -0
  75. package/dist_ts/protocols/protocol.escl.js +364 -0
  76. package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
  77. package/dist_ts/protocols/protocol.homeassistant.js +611 -0
  78. package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
  79. package/dist_ts/protocols/protocol.ipp.js +284 -0
  80. package/dist_ts/protocols/protocol.nut.d.ts +160 -0
  81. package/dist_ts/protocols/protocol.nut.js +364 -0
  82. package/dist_ts/protocols/protocol.sane.d.ts +104 -0
  83. package/dist_ts/protocols/protocol.sane.js +597 -0
  84. package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
  85. package/dist_ts/protocols/protocol.snmp.js +333 -0
  86. package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
  87. package/dist_ts/protocols/protocol.upnp.js +442 -0
  88. package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
  89. package/dist_ts/protocols/protocol.upssnmp.js +263 -0
  90. package/npmextra.json +24 -0
  91. package/package.json +39 -0
  92. package/readme.hints.md +115 -0
  93. package/readme.md +624 -0
  94. package/test/test.ts +261 -0
  95. package/ts/00_commitinfo_data.ts +8 -0
  96. package/ts/device/device.classes.device.ts +454 -0
  97. package/ts/devicemanager.classes.devicemanager.ts +1219 -0
  98. package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
  99. package/ts/discovery/discovery.classes.mdns.ts +347 -0
  100. package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
  101. package/ts/discovery/discovery.classes.ssdp.ts +357 -0
  102. package/ts/factories/index.ts +696 -0
  103. package/ts/features/feature.abstract.ts +251 -0
  104. package/ts/features/feature.camera.ts +214 -0
  105. package/ts/features/feature.climate.ts +407 -0
  106. package/ts/features/feature.cover.ts +278 -0
  107. package/ts/features/feature.fan.ts +296 -0
  108. package/ts/features/feature.light.ts +369 -0
  109. package/ts/features/feature.lock.ts +223 -0
  110. package/ts/features/feature.playback.ts +246 -0
  111. package/ts/features/feature.power.ts +231 -0
  112. package/ts/features/feature.print.ts +297 -0
  113. package/ts/features/feature.scan.ts +370 -0
  114. package/ts/features/feature.sensor.ts +202 -0
  115. package/ts/features/feature.snmp.ts +235 -0
  116. package/ts/features/feature.switch.ts +170 -0
  117. package/ts/features/feature.volume.ts +256 -0
  118. package/ts/features/index.ts +25 -0
  119. package/ts/helpers/helpers.iprange.ts +172 -0
  120. package/ts/helpers/helpers.retry.ts +100 -0
  121. package/ts/index.ts +165 -0
  122. package/ts/interfaces/feature.interfaces.ts +359 -0
  123. package/ts/interfaces/homeassistant.interfaces.ts +666 -0
  124. package/ts/interfaces/index.ts +421 -0
  125. package/ts/interfaces/smarthome.interfaces.ts +421 -0
  126. package/ts/paths.ts +5 -0
  127. package/ts/plugins.ts +41 -0
  128. package/ts/protocols/index.ts +59 -0
  129. package/ts/protocols/protocol.escl.ts +439 -0
  130. package/ts/protocols/protocol.homeassistant.ts +737 -0
  131. package/ts/protocols/protocol.ipp.ts +329 -0
  132. package/ts/protocols/protocol.nut.ts +471 -0
  133. package/ts/protocols/protocol.sane.ts +694 -0
  134. package/ts/protocols/protocol.snmp.ts +439 -0
  135. package/ts/protocols/protocol.upnp.ts +627 -0
  136. package/ts/protocols/protocol.upssnmp.ts +377 -0
  137. package/tsconfig.json +12 -0
package/test/test.ts ADDED
@@ -0,0 +1,261 @@
1
+ import { expect, tap } from '@git.zone/tstest/tapbundle';
2
+ import * as devicemanager from '../ts/index.js';
3
+
4
+ // Test core exports
5
+ tap.test('should export DeviceManager', async () => {
6
+ expect(devicemanager.DeviceManager).toBeDefined();
7
+ expect(typeof devicemanager.DeviceManager).toEqual('function');
8
+ });
9
+
10
+ tap.test('should export UniversalDevice', async () => {
11
+ expect(devicemanager.UniversalDevice).toBeDefined();
12
+ expect(typeof devicemanager.UniversalDevice).toEqual('function');
13
+ });
14
+
15
+ tap.test('should export Features', async () => {
16
+ expect(devicemanager.ScanFeature).toBeDefined();
17
+ expect(devicemanager.PrintFeature).toBeDefined();
18
+ expect(devicemanager.PlaybackFeature).toBeDefined();
19
+ expect(devicemanager.VolumeFeature).toBeDefined();
20
+ expect(devicemanager.PowerFeature).toBeDefined();
21
+ expect(devicemanager.SnmpFeature).toBeDefined();
22
+ });
23
+
24
+ tap.test('should export device factories', async () => {
25
+ expect(devicemanager.createScanner).toBeDefined();
26
+ expect(devicemanager.createPrinter).toBeDefined();
27
+ expect(devicemanager.createSpeaker).toBeDefined();
28
+ expect(devicemanager.createUpsDevice).toBeDefined();
29
+ expect(typeof devicemanager.createScanner).toEqual('function');
30
+ expect(typeof devicemanager.createPrinter).toEqual('function');
31
+ });
32
+
33
+ tap.test('should export protocol implementations', async () => {
34
+ expect(devicemanager.EsclProtocol).toBeDefined();
35
+ expect(devicemanager.SaneProtocol).toBeDefined();
36
+ expect(devicemanager.IppProtocol).toBeDefined();
37
+ expect(devicemanager.SnmpProtocol).toBeDefined();
38
+ });
39
+
40
+ tap.test('should export retry helpers', async () => {
41
+ expect(devicemanager.withRetry).toBeDefined();
42
+ expect(devicemanager.createRetryable).toBeDefined();
43
+ expect(devicemanager.defaultRetryOptions).toBeDefined();
44
+ });
45
+
46
+ // Test DeviceManager creation
47
+ tap.test('should create DeviceManager instance', async () => {
48
+ const dm = new devicemanager.DeviceManager({
49
+ autoDiscovery: false,
50
+ });
51
+
52
+ expect(dm).toBeInstanceOf(devicemanager.DeviceManager);
53
+ expect(dm.isDiscovering).toEqual(false);
54
+ expect(dm.getScanners()).toEqual([]);
55
+ expect(dm.getPrinters()).toEqual([]);
56
+ expect(dm.getDevices()).toEqual([]);
57
+ });
58
+
59
+ // Test device selector
60
+ tap.test('should support device selection with IDeviceSelector', async () => {
61
+ const dm = new devicemanager.DeviceManager({
62
+ autoDiscovery: false,
63
+ });
64
+
65
+ // Empty manager should return empty array
66
+ expect(dm.getDevices({ hasFeature: 'scan' })).toEqual([]);
67
+ expect(dm.getDevices({ name: 'Test' })).toEqual([]);
68
+
69
+ // selectDevice should throw when no devices match
70
+ let error: Error | null = null;
71
+ try {
72
+ dm.selectDevice({ address: '192.168.1.100' });
73
+ } catch (e) {
74
+ error = e as Error;
75
+ }
76
+ expect(error).not.toBeNull();
77
+ expect(error?.message).toContain('No device found');
78
+ });
79
+
80
+ // Test retry helper
81
+ tap.test('withRetry should succeed on first try', async () => {
82
+ let callCount = 0;
83
+ const result = await devicemanager.withRetry(async () => {
84
+ callCount++;
85
+ return 'success';
86
+ });
87
+
88
+ expect(result).toEqual('success');
89
+ expect(callCount).toEqual(1);
90
+ });
91
+
92
+ tap.test('withRetry should retry on failure', async () => {
93
+ let callCount = 0;
94
+ const result = await devicemanager.withRetry(
95
+ async () => {
96
+ callCount++;
97
+ if (callCount < 3) {
98
+ throw new Error('Temporary failure');
99
+ }
100
+ return 'success after retries';
101
+ },
102
+ { maxRetries: 5, baseDelay: 10, maxDelay: 100 }
103
+ );
104
+
105
+ expect(result).toEqual('success after retries');
106
+ expect(callCount).toEqual(3);
107
+ });
108
+
109
+ tap.test('withRetry should throw after max retries', async () => {
110
+ let callCount = 0;
111
+ let error: Error | null = null;
112
+
113
+ try {
114
+ await devicemanager.withRetry(
115
+ async () => {
116
+ callCount++;
117
+ throw new Error('Persistent failure');
118
+ },
119
+ { maxRetries: 2, baseDelay: 10, maxDelay: 100 }
120
+ );
121
+ } catch (e) {
122
+ error = e as Error;
123
+ }
124
+
125
+ expect(error).not.toBeNull();
126
+ expect(error?.message).toEqual('Persistent failure');
127
+ expect(callCount).toEqual(3); // Initial + 2 retries
128
+ });
129
+
130
+ // Test discovery (non-blocking)
131
+ tap.test('should start and stop discovery', async () => {
132
+ const dm = new devicemanager.DeviceManager();
133
+
134
+ // Track events
135
+ let discoveryStarted = false;
136
+ let discoveryStopped = false;
137
+
138
+ dm.on('discovery:started', () => {
139
+ discoveryStarted = true;
140
+ });
141
+
142
+ dm.on('discovery:stopped', () => {
143
+ discoveryStopped = true;
144
+ });
145
+
146
+ await dm.startDiscovery();
147
+ expect(dm.isDiscovering).toEqual(true);
148
+ expect(discoveryStarted).toEqual(true);
149
+
150
+ // Wait a bit for potential device discovery
151
+ await new Promise((resolve) => setTimeout(resolve, 2000));
152
+
153
+ // Log discovered devices using new API
154
+ const scanners = dm.getDevices({ hasFeature: 'scan' });
155
+ const printers = dm.getDevices({ hasFeature: 'print' });
156
+ console.log(`Discovered ${scanners.length} scanner(s) and ${printers.length} printer(s)`);
157
+
158
+ for (const scanner of scanners) {
159
+ const scanFeature = scanner.getFeature<devicemanager.ScanFeature>('scan');
160
+ console.log(` Scanner: ${scanner.name} (${scanner.address}) - ${scanFeature?.protocol || 'unknown'}`);
161
+ }
162
+
163
+ for (const printer of printers) {
164
+ console.log(` Printer: ${printer.name} (${printer.address})`);
165
+ }
166
+
167
+ await dm.stopDiscovery();
168
+ expect(dm.isDiscovering).toEqual(false);
169
+ expect(discoveryStopped).toEqual(true);
170
+
171
+ await dm.shutdown();
172
+ });
173
+
174
+ // Test Scanner creation using factory
175
+ tap.test('should create Scanner device using factory', async () => {
176
+ const scanner = devicemanager.createScanner({
177
+ id: 'test:scanner:1',
178
+ name: 'Test Scanner',
179
+ address: '192.168.1.100',
180
+ port: 443,
181
+ protocol: 'escl',
182
+ txtRecords: {
183
+ 'ty': 'HP LaserJet MFP',
184
+ 'pdl': 'image/jpeg,application/pdf',
185
+ 'cs': 'color,grayscale',
186
+ 'is': 'platen,adf',
187
+ },
188
+ });
189
+
190
+ expect(scanner).toBeInstanceOf(devicemanager.UniversalDevice);
191
+ expect(scanner.name).toEqual('Test Scanner');
192
+ expect(scanner.address).toEqual('192.168.1.100');
193
+ expect(scanner.hasFeature('scan')).toEqual(true);
194
+
195
+ const scanFeature = scanner.selectFeature<devicemanager.ScanFeature>('scan');
196
+ expect(scanFeature).toBeInstanceOf(devicemanager.ScanFeature);
197
+ expect(scanFeature.protocol).toEqual('escl');
198
+ });
199
+
200
+ // Test Printer creation using factory
201
+ tap.test('should create Printer device using factory', async () => {
202
+ const printer = devicemanager.createPrinter({
203
+ id: 'test:printer:1',
204
+ name: 'Test Printer',
205
+ address: '192.168.1.101',
206
+ port: 631,
207
+ ippPath: '/ipp/print',
208
+ txtRecords: {
209
+ 'ty': 'Brother HL-L2350DW',
210
+ 'Color': 'T',
211
+ 'Duplex': 'T',
212
+ },
213
+ });
214
+
215
+ expect(printer).toBeInstanceOf(devicemanager.UniversalDevice);
216
+ expect(printer.name).toEqual('Test Printer');
217
+ expect(printer.address).toEqual('192.168.1.101');
218
+ expect(printer.hasFeature('print')).toEqual(true);
219
+
220
+ const printFeature = printer.selectFeature<devicemanager.PrintFeature>('print');
221
+ expect(printFeature).toBeInstanceOf(devicemanager.PrintFeature);
222
+ });
223
+
224
+ // Test UniversalDevice feature management
225
+ tap.test('should manage features on UniversalDevice', async () => {
226
+ const device = new devicemanager.UniversalDevice('192.168.1.50', 80, {
227
+ name: 'Test Device',
228
+ });
229
+
230
+ expect(device.name).toEqual('Test Device');
231
+ expect(device.address).toEqual('192.168.1.50');
232
+ expect(device.featureCount).toEqual(0);
233
+ expect(device.hasFeature('scan')).toEqual(false);
234
+
235
+ // selectFeature should throw when feature doesn't exist
236
+ let error: Error | null = null;
237
+ try {
238
+ device.selectFeature('scan');
239
+ } catch (e) {
240
+ error = e as Error;
241
+ }
242
+ expect(error).not.toBeNull();
243
+ expect(error?.message).toContain("does not have feature 'scan'");
244
+
245
+ // getFeature should return undefined
246
+ expect(device.getFeature('scan')).toBeUndefined();
247
+ });
248
+
249
+ // Test IP helpers
250
+ tap.test('should export IP helper utilities', async () => {
251
+ expect(devicemanager.isValidIp).toBeDefined();
252
+ expect(devicemanager.cidrToIps).toBeDefined();
253
+ expect(devicemanager.getLocalSubnet).toBeDefined();
254
+
255
+ // Test isValidIp
256
+ expect(devicemanager.isValidIp('192.168.1.1')).toEqual(true);
257
+ expect(devicemanager.isValidIp('invalid')).toEqual(false);
258
+ expect(devicemanager.isValidIp('256.1.1.1')).toEqual(false);
259
+ });
260
+
261
+ export default tap.start();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @push.rocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@ecobridge.xyz/devicemanager',
6
+ version: '3.0.2',
7
+ description: 'a device manager for talking to devices on network and over usb'
8
+ }