@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.
- package/.gitea/workflows/default_nottags.yaml +66 -0
- package/.gitea/workflows/default_tags.yaml +124 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +26 -0
- package/changelog.md +91 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/device/device.classes.device.d.ts +205 -0
- package/dist_ts/device/device.classes.device.js +344 -0
- package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
- package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
- package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
- package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
- package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
- package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
- package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
- package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
- package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
- package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
- package/dist_ts/factories/index.d.ts +190 -0
- package/dist_ts/factories/index.js +350 -0
- package/dist_ts/features/feature.abstract.d.ts +118 -0
- package/dist_ts/features/feature.abstract.js +172 -0
- package/dist_ts/features/feature.camera.d.ts +95 -0
- package/dist_ts/features/feature.camera.js +162 -0
- package/dist_ts/features/feature.climate.d.ts +174 -0
- package/dist_ts/features/feature.climate.js +328 -0
- package/dist_ts/features/feature.cover.d.ts +121 -0
- package/dist_ts/features/feature.cover.js +217 -0
- package/dist_ts/features/feature.fan.d.ts +127 -0
- package/dist_ts/features/feature.fan.js +235 -0
- package/dist_ts/features/feature.light.d.ts +156 -0
- package/dist_ts/features/feature.light.js +296 -0
- package/dist_ts/features/feature.lock.d.ts +103 -0
- package/dist_ts/features/feature.lock.js +170 -0
- package/dist_ts/features/feature.playback.d.ts +98 -0
- package/dist_ts/features/feature.playback.js +191 -0
- package/dist_ts/features/feature.power.d.ts +79 -0
- package/dist_ts/features/feature.power.js +173 -0
- package/dist_ts/features/feature.print.d.ts +78 -0
- package/dist_ts/features/feature.print.js +226 -0
- package/dist_ts/features/feature.scan.d.ts +79 -0
- package/dist_ts/features/feature.scan.js +315 -0
- package/dist_ts/features/feature.sensor.d.ts +95 -0
- package/dist_ts/features/feature.sensor.js +147 -0
- package/dist_ts/features/feature.snmp.d.ts +88 -0
- package/dist_ts/features/feature.snmp.js +175 -0
- package/dist_ts/features/feature.switch.d.ts +75 -0
- package/dist_ts/features/feature.switch.js +126 -0
- package/dist_ts/features/feature.volume.d.ts +104 -0
- package/dist_ts/features/feature.volume.js +186 -0
- package/dist_ts/features/index.d.ts +19 -0
- package/dist_ts/features/index.js +23 -0
- package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
- package/dist_ts/helpers/helpers.iprange.js +151 -0
- package/dist_ts/helpers/helpers.retry.d.ts +25 -0
- package/dist_ts/helpers/helpers.retry.js +69 -0
- package/dist_ts/index.d.ts +15 -0
- package/dist_ts/index.js +40 -0
- package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
- package/dist_ts/interfaces/feature.interfaces.js +6 -0
- package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
- package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
- package/dist_ts/interfaces/index.d.ts +330 -0
- package/dist_ts/interfaces/index.js +16 -0
- package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
- package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
- package/dist_ts/paths.d.ts +1 -0
- package/dist_ts/paths.js +3 -0
- package/dist_ts/plugins.d.ts +23 -0
- package/dist_ts/plugins.js +30 -0
- package/dist_ts/protocols/index.d.ts +12 -0
- package/dist_ts/protocols/index.js +21 -0
- package/dist_ts/protocols/protocol.escl.d.ts +58 -0
- package/dist_ts/protocols/protocol.escl.js +364 -0
- package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
- package/dist_ts/protocols/protocol.homeassistant.js +611 -0
- package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
- package/dist_ts/protocols/protocol.ipp.js +284 -0
- package/dist_ts/protocols/protocol.nut.d.ts +160 -0
- package/dist_ts/protocols/protocol.nut.js +364 -0
- package/dist_ts/protocols/protocol.sane.d.ts +104 -0
- package/dist_ts/protocols/protocol.sane.js +597 -0
- package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
- package/dist_ts/protocols/protocol.snmp.js +333 -0
- package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
- package/dist_ts/protocols/protocol.upnp.js +442 -0
- package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
- package/dist_ts/protocols/protocol.upssnmp.js +263 -0
- package/npmextra.json +24 -0
- package/package.json +39 -0
- package/readme.hints.md +115 -0
- package/readme.md +624 -0
- package/test/test.ts +261 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/device/device.classes.device.ts +454 -0
- package/ts/devicemanager.classes.devicemanager.ts +1219 -0
- package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
- package/ts/discovery/discovery.classes.mdns.ts +347 -0
- package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
- package/ts/discovery/discovery.classes.ssdp.ts +357 -0
- package/ts/factories/index.ts +696 -0
- package/ts/features/feature.abstract.ts +251 -0
- package/ts/features/feature.camera.ts +214 -0
- package/ts/features/feature.climate.ts +407 -0
- package/ts/features/feature.cover.ts +278 -0
- package/ts/features/feature.fan.ts +296 -0
- package/ts/features/feature.light.ts +369 -0
- package/ts/features/feature.lock.ts +223 -0
- package/ts/features/feature.playback.ts +246 -0
- package/ts/features/feature.power.ts +231 -0
- package/ts/features/feature.print.ts +297 -0
- package/ts/features/feature.scan.ts +370 -0
- package/ts/features/feature.sensor.ts +202 -0
- package/ts/features/feature.snmp.ts +235 -0
- package/ts/features/feature.switch.ts +170 -0
- package/ts/features/feature.volume.ts +256 -0
- package/ts/features/index.ts +25 -0
- package/ts/helpers/helpers.iprange.ts +172 -0
- package/ts/helpers/helpers.retry.ts +100 -0
- package/ts/index.ts +165 -0
- package/ts/interfaces/feature.interfaces.ts +359 -0
- package/ts/interfaces/homeassistant.interfaces.ts +666 -0
- package/ts/interfaces/index.ts +421 -0
- package/ts/interfaces/smarthome.interfaces.ts +421 -0
- package/ts/paths.ts +5 -0
- package/ts/plugins.ts +41 -0
- package/ts/protocols/index.ts +59 -0
- package/ts/protocols/protocol.escl.ts +439 -0
- package/ts/protocols/protocol.homeassistant.ts +737 -0
- package/ts/protocols/protocol.ipp.ts +329 -0
- package/ts/protocols/protocol.nut.ts +471 -0
- package/ts/protocols/protocol.sane.ts +694 -0
- package/ts/protocols/protocol.snmp.ts +439 -0
- package/ts/protocols/protocol.upnp.ts +627 -0
- package/ts/protocols/protocol.upssnmp.ts +377 -0
- 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();
|