@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/readme.md ADDED
@@ -0,0 +1,624 @@
1
+ # @ecobridge.xyz/devicemanager
2
+
3
+ A comprehensive, TypeScript-first device manager for discovering and communicating with network devices. 🔌
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@ecobridge.xyz/devicemanager.svg)](https://www.npmjs.com/package/@ecobridge.xyz/devicemanager)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## 🎯 Overview
9
+
10
+ `@ecobridge.xyz/devicemanager` provides a unified, object-oriented API for discovering and controlling network devices. Whether you're building a document scanning workflow, managing printers, controlling smart home devices, or monitoring UPS systems — this library has you covered.
11
+
12
+ **Supported Device Types:**
13
+ - 🖨️ **Scanners** — eSCL (AirScan), SANE protocols
14
+ - 📄 **Printers** — IPP, JetDirect protocols
15
+ - 🔊 **Speakers** — Sonos, AirPlay, Chromecast, DLNA
16
+ - 🔋 **UPS Systems** — NUT, SNMP protocols
17
+ - 📡 **SNMP Devices** — Generic SNMP v1/v2c/v3 support
18
+ - 🏠 **Smart Home** — Home Assistant integration (lights, switches, sensors, climate, locks, fans, cameras, covers)
19
+
20
+ ## Issue Reporting and Security
21
+
22
+ For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
23
+
24
+ ## 📦 Installation
25
+
26
+ ```bash
27
+ # Using pnpm (recommended)
28
+ pnpm add @ecobridge.xyz/devicemanager
29
+
30
+ # Using npm
31
+ npm install @ecobridge.xyz/devicemanager
32
+
33
+ # Using yarn
34
+ yarn add @ecobridge.xyz/devicemanager
35
+ ```
36
+
37
+ ## 🚀 Quick Start
38
+
39
+ ### The OOP Pattern: Discovery → Selection → Feature → Operation
40
+
41
+ ```typescript
42
+ import { DeviceManager, ScanFeature } from '@ecobridge.xyz/devicemanager';
43
+ import * as fs from 'fs/promises';
44
+
45
+ async function scanDocument() {
46
+ const manager = new DeviceManager();
47
+
48
+ // 1️⃣ DISCOVERY - Find scanners in your network
49
+ await manager.discoverScanners('192.168.1.0/24');
50
+
51
+ // 2️⃣ INSPECTION - See what's available
52
+ const scanners = manager.getDevices({ hasFeature: 'scan' });
53
+ console.log('Found scanners:', scanners.map(s => `${s.name} at ${s.address}`));
54
+
55
+ // 3️⃣ SELECTION - Choose your device (explicit, no magic!)
56
+ const device = manager.selectDevice({ address: '192.168.1.100' });
57
+
58
+ // 4️⃣ FEATURE ACCESS - Get the capability you need
59
+ const scanFeature = device.selectFeature<ScanFeature>('scan');
60
+
61
+ // 5️⃣ OPERATION - Do the thing!
62
+ await scanFeature.connect();
63
+ const result = await scanFeature.scan({
64
+ source: 'flatbed',
65
+ resolution: 300,
66
+ colorMode: 'color',
67
+ format: 'jpeg',
68
+ });
69
+
70
+ await fs.writeFile('scan.jpg', result.data);
71
+ console.log(`Saved: scan.jpg (${result.data.length} bytes)`);
72
+
73
+ await manager.shutdown();
74
+ }
75
+
76
+ scanDocument();
77
+ ```
78
+
79
+ ## 🏗️ Architecture
80
+
81
+ The library follows a clean, composable architecture:
82
+
83
+ ```
84
+ ┌─────────────────────────────────────────────────────────────┐
85
+ │ DeviceManager │
86
+ │ • Discovery (mDNS, SSDP, Network Scanning) │
87
+ │ • Device Registry (by IP, deduplication) │
88
+ │ • Device Selection (query & assert patterns) │
89
+ └─────────────────────────────────────────────────────────────┘
90
+
91
+
92
+ ┌─────────────────────────────────────────────────────────────┐
93
+ │ UniversalDevice │
94
+ │ • Represents any network device │
95
+ │ • Composable features (scan, print, volume, etc.) │
96
+ │ • Connection lifecycle management │
97
+ └─────────────────────────────────────────────────────────────┘
98
+
99
+
100
+ ┌─────────────────────────────────────────────────────────────┐
101
+ │ Features │
102
+ │ ScanFeature │ PrintFeature │ PlaybackFeature │ VolumeFeature
103
+ │ PowerFeature │ SnmpFeature │ LightFeature │ SwitchFeature │
104
+ │ SensorFeature │ ClimateFeature │ CameraFeature │ ... │
105
+ └─────────────────────────────────────────────────────────────┘
106
+
107
+
108
+ ┌─────────────────────────────────────────────────────────────┐
109
+ │ Protocols │
110
+ │ eSCL │ SANE │ IPP │ SNMP │ NUT │ UPnP/SOAP │ Home Assistant│
111
+ └─────────────────────────────────────────────────────────────┘
112
+ ```
113
+
114
+ ## 📖 API Reference
115
+
116
+ ### DeviceManager
117
+
118
+ The central orchestrator for device discovery and management.
119
+
120
+ ```typescript
121
+ const manager = new DeviceManager({
122
+ autoDiscovery: true, // Enable mDNS/SSDP auto-discovery
123
+ discoveryTimeout: 10000, // Discovery timeout in ms
124
+ enableRetry: true, // Enable retry with exponential backoff
125
+ maxRetries: 5, // Maximum retry attempts
126
+ });
127
+ ```
128
+
129
+ #### Discovery Methods
130
+
131
+ ```typescript
132
+ // Focused scanner discovery
133
+ const scanners = await manager.discoverScanners('192.168.1.0/24', {
134
+ timeout: 3000,
135
+ concurrency: 50,
136
+ });
137
+
138
+ // Focused printer discovery
139
+ const printers = await manager.discoverPrinters('192.168.1.0/24');
140
+
141
+ // General network scan (all device types)
142
+ const devices = await manager.scanNetwork({
143
+ ipRange: '192.168.1.0/24',
144
+ probeEscl: true,
145
+ probeIpp: true,
146
+ probeSane: true,
147
+ probeAirplay: true,
148
+ probeSonos: true,
149
+ probeChromecast: true,
150
+ });
151
+
152
+ // mDNS/SSDP continuous discovery
153
+ await manager.startDiscovery();
154
+ manager.on('device:found', ({ device, featureType }) => {
155
+ console.log(`Found: ${device.name}`);
156
+ });
157
+ await manager.stopDiscovery();
158
+ ```
159
+
160
+ #### Device Selection
161
+
162
+ ```typescript
163
+ // Query pattern (returns array, may be empty)
164
+ const allDevices = manager.getDevices();
165
+ const scanners = manager.getDevices({ hasFeature: 'scan' });
166
+ const brotherDevices = manager.getDevices({ name: 'Brother' });
167
+
168
+ // Assert pattern (returns single device, throws if not found)
169
+ const device = manager.selectDevice({ address: '192.168.1.100' });
170
+ const scanner = manager.selectDevice({ name: 'Brother', hasFeature: 'scan' });
171
+ ```
172
+
173
+ #### IDeviceSelector Interface
174
+
175
+ ```typescript
176
+ interface IDeviceSelector {
177
+ id?: string; // Exact match on device ID
178
+ address?: string; // Exact match on IP address
179
+ name?: string; // Partial match (case-insensitive)
180
+ model?: string; // Partial match (case-insensitive)
181
+ manufacturer?: string; // Partial match (case-insensitive)
182
+ hasFeature?: TFeatureType; // Must have this feature
183
+ hasFeatures?: TFeatureType[]; // Must have ALL features
184
+ hasAnyFeature?: TFeatureType[]; // Must have ANY feature
185
+ }
186
+ ```
187
+
188
+ ### UniversalDevice
189
+
190
+ Represents any network device with composable features.
191
+
192
+ ```typescript
193
+ const device = manager.selectDevice({ address: '192.168.1.100' });
194
+
195
+ // Device properties
196
+ console.log(device.name); // "Brother MFC-J5730DW"
197
+ console.log(device.address); // "192.168.1.100"
198
+ console.log(device.manufacturer); // "Brother"
199
+ console.log(device.model); // "MFC-J5730DW"
200
+ console.log(device.status); // 'online' | 'offline' | 'busy' | 'error'
201
+
202
+ // Feature access (safe query - returns undefined)
203
+ const maybeScan = device.getFeature<ScanFeature>('scan');
204
+
205
+ // Feature access (assert - throws if not available)
206
+ const scanFeature = device.selectFeature<ScanFeature>('scan');
207
+
208
+ // Check capabilities
209
+ device.hasFeature('scan'); // true/false
210
+ device.hasFeatures(['scan', 'print']); // must have ALL
211
+ device.hasAnyFeature(['scan', 'print']); // must have ANY
212
+ device.getFeatureTypes(); // ['scan', 'print', ...]
213
+ ```
214
+
215
+ ### Features
216
+
217
+ #### 🖨️ ScanFeature
218
+
219
+ ```typescript
220
+ const scanFeature = device.selectFeature<ScanFeature>('scan');
221
+ await scanFeature.connect();
222
+
223
+ // Get capabilities
224
+ const caps = await scanFeature.getCapabilities();
225
+ // { resolutions: [100, 200, 300, 600], formats: ['jpeg', 'png', 'pdf'], ... }
226
+
227
+ // Scan a document
228
+ const result = await scanFeature.scan({
229
+ source: 'flatbed', // 'flatbed' | 'adf' | 'adf-duplex'
230
+ resolution: 300, // DPI
231
+ colorMode: 'color', // 'color' | 'grayscale' | 'blackwhite'
232
+ format: 'jpeg', // 'jpeg' | 'png' | 'pdf' | 'tiff'
233
+ quality: 85, // JPEG quality (1-100)
234
+ area: { x: 0, y: 0, width: 210, height: 297 }, // mm
235
+ });
236
+
237
+ // result.data is a Buffer containing the scanned image
238
+ await fs.writeFile('scan.jpg', result.data);
239
+ ```
240
+
241
+ #### 📄 PrintFeature
242
+
243
+ ```typescript
244
+ const printFeature = device.selectFeature<PrintFeature>('print');
245
+ await printFeature.connect();
246
+
247
+ // Get printer capabilities
248
+ const caps = await printFeature.getCapabilities();
249
+
250
+ // Print a document
251
+ const job = await printFeature.print(pdfBuffer, {
252
+ copies: 2,
253
+ mediaSize: 'iso_a4_210x297mm',
254
+ sides: 'two-sided-long-edge',
255
+ quality: 'high',
256
+ colorMode: 'color',
257
+ jobName: 'My Document',
258
+ });
259
+
260
+ // Check job status
261
+ const status = await printFeature.getJobStatus(job.id);
262
+ ```
263
+
264
+ #### 🔊 PlaybackFeature & VolumeFeature
265
+
266
+ ```typescript
267
+ const playback = device.selectFeature<PlaybackFeature>('playback');
268
+ const volume = device.selectFeature<VolumeFeature>('volume');
269
+ await playback.connect();
270
+
271
+ // Control playback
272
+ await playback.play('http://example.com/audio.mp3');
273
+ await playback.pause();
274
+ await playback.stop();
275
+ await playback.seek(120); // seconds
276
+
277
+ // Get playback status
278
+ const status = await playback.getStatus();
279
+ // { state: 'playing', position: 45, duration: 180, track: { title: '...' } }
280
+
281
+ // Control volume
282
+ await volume.setVolume(50); // 0-100
283
+ await volume.mute();
284
+ await volume.unmute();
285
+ const level = await volume.getVolume();
286
+ ```
287
+
288
+ #### 🔋 PowerFeature (UPS)
289
+
290
+ ```typescript
291
+ const power = device.selectFeature<PowerFeature>('power');
292
+ await power.connect();
293
+
294
+ // Get UPS status
295
+ const status = await power.getStatus();
296
+ // { status: 'online', battery: { charge: 100, runtime: 1800, voltage: 13.8 }, ... }
297
+
298
+ // Get battery info
299
+ const battery = await power.getBatteryInfo();
300
+ // { charge: 95, runtime: 1500, health: 'good' }
301
+
302
+ // Run self-test
303
+ await power.runTest();
304
+ ```
305
+
306
+ #### 🏠 Smart Home Features
307
+
308
+ ```typescript
309
+ // Light control
310
+ const light = device.selectFeature<LightFeature>('light');
311
+ await light.turnOn();
312
+ await light.setBrightness(80);
313
+ await light.setColor({ r: 255, g: 100, b: 50 });
314
+ await light.setColorTemperature(4000); // Kelvin
315
+
316
+ // Switch control
317
+ const switch_ = device.selectFeature<SwitchFeature>('switch');
318
+ await switch_.turnOn();
319
+ await switch_.turnOff();
320
+ await switch_.toggle();
321
+
322
+ // Climate control
323
+ const climate = device.selectFeature<ClimateFeature>('climate');
324
+ await climate.setTargetTemperature(22);
325
+ await climate.setMode('heat'); // 'heat' | 'cool' | 'auto' | 'off'
326
+
327
+ // Sensor reading
328
+ const sensor = device.selectFeature<SensorFeature>('sensor');
329
+ const reading = await sensor.getState();
330
+ // { temperature: 22.5, humidity: 45, battery: 85 }
331
+ ```
332
+
333
+ ### Protocol Direct Access
334
+
335
+ For advanced use cases, you can access protocols directly:
336
+
337
+ ```typescript
338
+ import { EsclProtocol, IppProtocol, SnmpProtocol } from '@ecobridge.xyz/devicemanager';
339
+
340
+ // Direct eSCL (AirScan) access
341
+ const escl = new EsclProtocol('192.168.1.100', 80, false);
342
+ const caps = await escl.getCapabilities();
343
+ const result = await escl.scan({ source: 'flatbed', resolution: 300 });
344
+
345
+ // Direct IPP access
346
+ const ipp = new IppProtocol('ipp://192.168.1.100:631/ipp/print');
347
+ const printerAttrs = await ipp.getPrinterAttributes();
348
+
349
+ // Direct SNMP access
350
+ const snmp = new SnmpProtocol('192.168.1.100', { community: 'public' });
351
+ const sysDescr = await snmp.get('1.3.6.1.2.1.1.1.0');
352
+ ```
353
+
354
+ ### Home Assistant Integration
355
+
356
+ ```typescript
357
+ import { HomeAssistantProtocol, HomeAssistantDiscovery } from '@ecobridge.xyz/devicemanager';
358
+
359
+ // Connect to Home Assistant
360
+ const ha = new HomeAssistantProtocol({
361
+ host: 'homeassistant.local',
362
+ port: 8123,
363
+ accessToken: 'your_long_lived_access_token',
364
+ });
365
+
366
+ await ha.connect();
367
+
368
+ // Get all entities
369
+ const entities = await ha.getStates();
370
+
371
+ // Control a light
372
+ await ha.callService('light', 'turn_on', {
373
+ entity_id: 'light.living_room',
374
+ brightness: 200,
375
+ });
376
+
377
+ // Subscribe to state changes
378
+ ha.on('state_changed', (event) => {
379
+ console.log(`${event.entity_id}: ${event.new_state.state}`);
380
+ });
381
+
382
+ // Auto-discover Home Assistant instances via mDNS
383
+ const discovery = new HomeAssistantDiscovery();
384
+ discovery.on('found', (instance) => {
385
+ console.log(`Found HA at ${instance.address}:${instance.port}`);
386
+ });
387
+ await discovery.start();
388
+ ```
389
+
390
+ ### Helper Utilities
391
+
392
+ ```typescript
393
+ import {
394
+ withRetry,
395
+ isValidIp,
396
+ cidrToIps,
397
+ getLocalSubnet,
398
+ } from '@ecobridge.xyz/devicemanager';
399
+
400
+ // Retry with exponential backoff
401
+ const result = await withRetry(
402
+ () => someFlakeyOperation(),
403
+ { maxRetries: 3, baseDelay: 1000, multiplier: 2 }
404
+ );
405
+
406
+ // IP utilities
407
+ isValidIp('192.168.1.1'); // true
408
+ cidrToIps('192.168.1.0/30'); // ['192.168.1.0', '192.168.1.1', ...]
409
+ getLocalSubnet(); // '192.168.1.0/24'
410
+ ```
411
+
412
+ ## 🔍 Discovery Methods
413
+
414
+ The library supports multiple discovery mechanisms:
415
+
416
+ | Method | Protocol | Use Case |
417
+ |--------|----------|----------|
418
+ | `discoverScanners()` | eSCL, SANE | Find network scanners |
419
+ | `discoverPrinters()` | IPP | Find network printers |
420
+ | `scanNetwork()` | All | Comprehensive subnet scan |
421
+ | `startDiscovery()` | mDNS, SSDP | Continuous auto-discovery |
422
+
423
+ ### mDNS Service Types
424
+
425
+ ```typescript
426
+ import { SERVICE_TYPES } from '@ecobridge.xyz/devicemanager';
427
+
428
+ // Available: escl, ipp, ipp-tls, airplay, raop,
429
+ // googlecast, sonos, sane, http, https, printer
430
+ ```
431
+
432
+ ### SSDP Service Types
433
+
434
+ ```typescript
435
+ import { SSDP_SERVICE_TYPES } from '@ecobridge.xyz/devicemanager';
436
+
437
+ // Available: all, rootdevice, mediaRenderer, mediaServer,
438
+ // contentDirectory, avtransport, renderingControl,
439
+ // connectionManager, zonePlayer
440
+ ```
441
+
442
+ ## 🎯 Feature Types
443
+
444
+ ```typescript
445
+ type TFeatureType =
446
+ | 'scan' // Document scanning
447
+ | 'print' // Document printing
448
+ | 'fax' // Fax send/receive
449
+ | 'copy' // Copy (scan + print)
450
+ | 'playback' // Media playback
451
+ | 'volume' // Volume control
452
+ | 'power' // Power/UPS status
453
+ | 'snmp' // SNMP queries
454
+ | 'dlna-render'// DLNA renderer
455
+ | 'dlna-serve' // DLNA server
456
+ | 'light' // Smart lights
457
+ | 'climate' // HVAC/thermostats
458
+ | 'sensor' // Sensors
459
+ | 'camera' // Cameras
460
+ | 'cover' // Blinds, garage doors
461
+ | 'switch' // Smart switches
462
+ | 'lock' // Smart locks
463
+ | 'fan' // Fans
464
+ ;
465
+ ```
466
+
467
+ ## 🔧 Advanced Usage
468
+
469
+ ### Custom Device Creation
470
+
471
+ Use the factory functions for creating devices with specific features:
472
+
473
+ ```typescript
474
+ import { createScanner, createPrinter, createSpeaker } from '@ecobridge.xyz/devicemanager';
475
+
476
+ // Create a scanner device manually
477
+ const scanner = createScanner({
478
+ id: 'my-scanner',
479
+ name: 'Office Scanner',
480
+ address: '192.168.1.50',
481
+ port: 80,
482
+ protocol: 'escl',
483
+ txtRecords: {},
484
+ });
485
+
486
+ // Create a printer device
487
+ const printer = createPrinter({
488
+ id: 'my-printer',
489
+ name: 'Office Printer',
490
+ address: '192.168.1.51',
491
+ port: 631,
492
+ txtRecords: { rp: '/ipp/print' },
493
+ });
494
+
495
+ // Create a Sonos speaker
496
+ const speaker = createSpeaker({
497
+ id: 'living-room-sonos',
498
+ name: 'Living Room',
499
+ address: '192.168.1.52',
500
+ port: 1400,
501
+ protocol: 'sonos',
502
+ });
503
+ ```
504
+
505
+ ### Smart Home Factory Functions
506
+
507
+ ```typescript
508
+ import {
509
+ createSmartLight,
510
+ createSmartSwitch,
511
+ createSmartSensor,
512
+ createSmartClimate,
513
+ createSmartCover,
514
+ createSmartLock,
515
+ createSmartFan,
516
+ createSmartCamera,
517
+ } from '@ecobridge.xyz/devicemanager';
518
+
519
+ // Create devices with Home Assistant integration
520
+ const light = createSmartLight({
521
+ id: 'living-room-light',
522
+ name: 'Living Room Light',
523
+ address: 'homeassistant.local',
524
+ port: 8123,
525
+ entityId: 'light.living_room',
526
+ protocol: 'home-assistant',
527
+ protocolClient: haClient, // Your HomeAssistantProtocol instance
528
+ capabilities: {
529
+ supportsBrightness: true,
530
+ supportsColorTemp: true,
531
+ supportsRgb: true,
532
+ },
533
+ });
534
+ ```
535
+
536
+ ### Event Handling
537
+
538
+ ```typescript
539
+ const manager = new DeviceManager();
540
+
541
+ // Discovery events
542
+ manager.on('device:found', ({ device, featureType }) => {
543
+ console.log(`Found ${device.name} with ${featureType} capability`);
544
+ });
545
+
546
+ manager.on('device:lost', (address) => {
547
+ console.log(`Device at ${address} went offline`);
548
+ });
549
+
550
+ // Network scan progress
551
+ manager.on('network:progress', (progress) => {
552
+ console.log(`Scanning: ${progress.percentage}% - Found ${progress.devicesFound} devices`);
553
+ });
554
+
555
+ // Device events
556
+ const device = manager.selectDevice({ address: '192.168.1.100' });
557
+ device.on('status:changed', ({ oldStatus, newStatus }) => {
558
+ console.log(`Status: ${oldStatus} → ${newStatus}`);
559
+ });
560
+ device.on('feature:connected', (featureType) => {
561
+ console.log(`Feature ${featureType} connected`);
562
+ });
563
+ ```
564
+
565
+ ### Error Handling
566
+
567
+ The library uses a fail-fast approach with clear error messages:
568
+
569
+ ```typescript
570
+ try {
571
+ // Throws if no device matches
572
+ const device = manager.selectDevice({ address: '192.168.1.999' });
573
+ } catch (err) {
574
+ // "No device found matching: {\"address\":\"192.168.1.999\"}"
575
+ }
576
+
577
+ try {
578
+ // Throws if device doesn't have the feature
579
+ const printFeature = device.selectFeature<PrintFeature>('print');
580
+ } catch (err) {
581
+ // "Device 'Brother Scanner' does not have feature 'print'"
582
+ }
583
+
584
+ // Safe alternatives that don't throw
585
+ const devices = manager.getDevices({ address: '192.168.1.999' }); // []
586
+ const maybePrint = device.getFeature<PrintFeature>('print'); // undefined
587
+ ```
588
+
589
+ ## 📋 Requirements
590
+
591
+ - **Node.js** 18+ (native `fetch` support required)
592
+ - **TypeScript** 5.0+ (recommended)
593
+ - **Network access** to target devices
594
+
595
+ ## 🙏 Credits
596
+
597
+ Built with ❤️ using:
598
+ - [bonjour-service](https://github.com/onlxltd/bonjour-service) - mDNS discovery
599
+ - [node-ssdp](https://github.com/diversario/node-ssdp) - SSDP/UPnP discovery
600
+ - [net-snmp](https://github.com/markabrahams/node-net-snmp) - SNMP protocol
601
+ - [ipp](https://github.com/niclaslindstedt/ipp) - IPP printing protocol
602
+ - [sonos](https://github.com/bencevans/node-sonos) - Sonos control
603
+ - [castv2-client](https://github.com/thibauts/node-castv2-client) - Chromecast protocol
604
+
605
+ ## License and Legal Information
606
+
607
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
608
+
609
+ **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
610
+
611
+ ### Trademarks
612
+
613
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
614
+
615
+ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
616
+
617
+ ### Company Information
618
+
619
+ Task Venture Capital GmbH
620
+ Registered at District Court Bremen HRB 35230 HB, Germany
621
+
622
+ For any legal inquiries or further information, please contact us via email at hello@task.vc.
623
+
624
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.