@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
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Factory Functions
|
|
3
|
+
* Create UniversalDevice instances with appropriate features
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { UniversalDevice, type IDeviceCreateOptions } from '../device/device.classes.device.js';
|
|
7
|
+
import { ScanFeature, type IScanFeatureOptions } from '../features/feature.scan.js';
|
|
8
|
+
import { PrintFeature, type IPrintFeatureOptions } from '../features/feature.print.js';
|
|
9
|
+
import { PlaybackFeature, type IPlaybackFeatureOptions } from '../features/feature.playback.js';
|
|
10
|
+
import { VolumeFeature, type IVolumeFeatureOptions } from '../features/feature.volume.js';
|
|
11
|
+
import { PowerFeature, type IPowerFeatureOptions } from '../features/feature.power.js';
|
|
12
|
+
import { SnmpFeature, type ISnmpFeatureOptions } from '../features/feature.snmp.js';
|
|
13
|
+
import type {
|
|
14
|
+
TScannerProtocol,
|
|
15
|
+
TScanFormat,
|
|
16
|
+
TColorMode,
|
|
17
|
+
TScanSource,
|
|
18
|
+
IRetryOptions,
|
|
19
|
+
} from '../interfaces/index.js';
|
|
20
|
+
import type { TPrintProtocol } from '../interfaces/feature.interfaces.js';
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Scanner Factory
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
export interface IScannerDiscoveryInfo {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
address: string;
|
|
30
|
+
port: number;
|
|
31
|
+
protocol: TScannerProtocol | 'ipp';
|
|
32
|
+
txtRecords: Record<string, string>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a scanner device (UniversalDevice with ScanFeature)
|
|
37
|
+
*/
|
|
38
|
+
export function createScanner(
|
|
39
|
+
info: IScannerDiscoveryInfo,
|
|
40
|
+
retryOptions?: IRetryOptions
|
|
41
|
+
): UniversalDevice {
|
|
42
|
+
const protocol = info.protocol === 'ipp' ? 'escl' : info.protocol;
|
|
43
|
+
const isSecure = info.txtRecords['TLS'] === '1' || (protocol === 'escl' && info.port === 443);
|
|
44
|
+
|
|
45
|
+
// Parse capabilities from TXT records
|
|
46
|
+
const formats = parseScanFormats(info.txtRecords);
|
|
47
|
+
const resolutions = parseScanResolutions(info.txtRecords);
|
|
48
|
+
const colorModes = parseScanColorModes(info.txtRecords);
|
|
49
|
+
const sources = parseScanSources(info.txtRecords);
|
|
50
|
+
|
|
51
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
52
|
+
name: info.name,
|
|
53
|
+
manufacturer: info.txtRecords['usb_MFG'] || info.txtRecords['mfg'],
|
|
54
|
+
model: info.txtRecords['usb_MDL'] || info.txtRecords['mdl'] || info.txtRecords['ty'],
|
|
55
|
+
retryOptions,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Override the generated ID with discovery ID
|
|
59
|
+
(device as { id: string }).id = info.id;
|
|
60
|
+
|
|
61
|
+
// Add scan feature
|
|
62
|
+
const scanFeature = new ScanFeature(device.getDeviceReference(), info.port, {
|
|
63
|
+
protocol: protocol as 'escl' | 'sane',
|
|
64
|
+
secure: isSecure,
|
|
65
|
+
supportedFormats: formats,
|
|
66
|
+
supportedResolutions: resolutions,
|
|
67
|
+
supportedColorModes: colorModes,
|
|
68
|
+
supportedSources: sources,
|
|
69
|
+
hasAdf: sources.includes('adf') || sources.includes('adf-duplex'),
|
|
70
|
+
hasDuplex: sources.includes('adf-duplex'),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
device.addFeature(scanFeature);
|
|
74
|
+
return device;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Printer Factory
|
|
79
|
+
// ============================================================================
|
|
80
|
+
|
|
81
|
+
export interface IPrinterDiscoveryInfo {
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
address: string;
|
|
85
|
+
port: number;
|
|
86
|
+
txtRecords: Record<string, string>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create a printer device (UniversalDevice with PrintFeature)
|
|
91
|
+
*/
|
|
92
|
+
export function createPrinter(
|
|
93
|
+
info: IPrinterDiscoveryInfo,
|
|
94
|
+
retryOptions?: IRetryOptions
|
|
95
|
+
): UniversalDevice {
|
|
96
|
+
const ippPath = info.txtRecords['rp'] || info.txtRecords['rfo'] || '/ipp/print';
|
|
97
|
+
const uri = `ipp://${info.address}:${info.port}${ippPath.startsWith('/') ? '' : '/'}${ippPath}`;
|
|
98
|
+
|
|
99
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
100
|
+
name: info.name,
|
|
101
|
+
manufacturer: info.txtRecords['usb_MFG'] || info.txtRecords['mfg'],
|
|
102
|
+
model: info.txtRecords['usb_MDL'] || info.txtRecords['mdl'] || info.txtRecords['ty'],
|
|
103
|
+
retryOptions,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Override the generated ID with discovery ID
|
|
107
|
+
(device as { id: string }).id = info.id;
|
|
108
|
+
|
|
109
|
+
// Add print feature
|
|
110
|
+
const printFeature = new PrintFeature(device.getDeviceReference(), info.port, {
|
|
111
|
+
protocol: 'ipp',
|
|
112
|
+
uri,
|
|
113
|
+
supportsColor: info.txtRecords['Color'] === 'T' || info.txtRecords['color'] === 'true',
|
|
114
|
+
supportsDuplex: info.txtRecords['Duplex'] === 'T' || info.txtRecords['duplex'] === 'true',
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
device.addFeature(printFeature);
|
|
118
|
+
return device;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// SNMP Device Factory
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
export interface ISnmpDiscoveryInfo {
|
|
126
|
+
id: string;
|
|
127
|
+
name: string;
|
|
128
|
+
address: string;
|
|
129
|
+
port: number;
|
|
130
|
+
community?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create an SNMP device (UniversalDevice with SnmpFeature)
|
|
135
|
+
*/
|
|
136
|
+
export function createSnmpDevice(
|
|
137
|
+
info: ISnmpDiscoveryInfo,
|
|
138
|
+
retryOptions?: IRetryOptions
|
|
139
|
+
): UniversalDevice {
|
|
140
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
141
|
+
name: info.name,
|
|
142
|
+
retryOptions,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Override the generated ID with discovery ID
|
|
146
|
+
(device as { id: string }).id = info.id;
|
|
147
|
+
|
|
148
|
+
// Add SNMP feature
|
|
149
|
+
const snmpFeature = new SnmpFeature(device.getDeviceReference(), info.port, {
|
|
150
|
+
community: info.community ?? 'public',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
device.addFeature(snmpFeature);
|
|
154
|
+
return device;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// UPS Device Factory
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
export interface IUpsDiscoveryInfo {
|
|
162
|
+
id: string;
|
|
163
|
+
name: string;
|
|
164
|
+
address: string;
|
|
165
|
+
port: number;
|
|
166
|
+
protocol: 'nut' | 'snmp';
|
|
167
|
+
upsName?: string;
|
|
168
|
+
community?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Create a UPS device (UniversalDevice with PowerFeature)
|
|
173
|
+
*/
|
|
174
|
+
export function createUpsDevice(
|
|
175
|
+
info: IUpsDiscoveryInfo,
|
|
176
|
+
retryOptions?: IRetryOptions
|
|
177
|
+
): UniversalDevice {
|
|
178
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
179
|
+
name: info.name,
|
|
180
|
+
retryOptions,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Override the generated ID with discovery ID
|
|
184
|
+
(device as { id: string }).id = info.id;
|
|
185
|
+
|
|
186
|
+
// Add power feature
|
|
187
|
+
const powerFeature = new PowerFeature(device.getDeviceReference(), info.port, {
|
|
188
|
+
protocol: info.protocol,
|
|
189
|
+
upsName: info.upsName,
|
|
190
|
+
community: info.community,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
device.addFeature(powerFeature);
|
|
194
|
+
return device;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Speaker Factory
|
|
199
|
+
// ============================================================================
|
|
200
|
+
|
|
201
|
+
export interface ISpeakerDiscoveryInfo {
|
|
202
|
+
id: string;
|
|
203
|
+
name: string;
|
|
204
|
+
address: string;
|
|
205
|
+
port: number;
|
|
206
|
+
protocol: 'sonos' | 'airplay' | 'chromecast' | 'dlna';
|
|
207
|
+
roomName?: string;
|
|
208
|
+
modelName?: string;
|
|
209
|
+
features?: number; // AirPlay feature flags
|
|
210
|
+
deviceId?: string;
|
|
211
|
+
friendlyName?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create a speaker device (UniversalDevice with PlaybackFeature and VolumeFeature)
|
|
216
|
+
*/
|
|
217
|
+
export function createSpeaker(
|
|
218
|
+
info: ISpeakerDiscoveryInfo,
|
|
219
|
+
retryOptions?: IRetryOptions
|
|
220
|
+
): UniversalDevice {
|
|
221
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
222
|
+
name: info.name,
|
|
223
|
+
model: info.modelName,
|
|
224
|
+
retryOptions,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Override the generated ID with discovery ID
|
|
228
|
+
(device as { id: string }).id = info.id;
|
|
229
|
+
|
|
230
|
+
// Add playback feature
|
|
231
|
+
const playbackFeature = new PlaybackFeature(device.getDeviceReference(), info.port, {
|
|
232
|
+
protocol: info.protocol,
|
|
233
|
+
supportsQueue: info.protocol === 'sonos',
|
|
234
|
+
supportsSeek: info.protocol !== 'airplay',
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
device.addFeature(playbackFeature);
|
|
238
|
+
|
|
239
|
+
// Add volume feature
|
|
240
|
+
const volumeFeature = new VolumeFeature(device.getDeviceReference(), info.port, {
|
|
241
|
+
volumeProtocol: info.protocol,
|
|
242
|
+
minVolume: 0,
|
|
243
|
+
maxVolume: 100,
|
|
244
|
+
supportsMute: true,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
device.addFeature(volumeFeature);
|
|
248
|
+
|
|
249
|
+
return device;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// DLNA Factory
|
|
254
|
+
// ============================================================================
|
|
255
|
+
|
|
256
|
+
export interface IDlnaRendererDiscoveryInfo {
|
|
257
|
+
id: string;
|
|
258
|
+
name: string;
|
|
259
|
+
address: string;
|
|
260
|
+
port: number;
|
|
261
|
+
controlUrl: string;
|
|
262
|
+
friendlyName: string;
|
|
263
|
+
modelName?: string;
|
|
264
|
+
manufacturer?: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Create a DLNA renderer device (UniversalDevice with PlaybackFeature)
|
|
269
|
+
*/
|
|
270
|
+
export function createDlnaRenderer(
|
|
271
|
+
info: IDlnaRendererDiscoveryInfo,
|
|
272
|
+
retryOptions?: IRetryOptions
|
|
273
|
+
): UniversalDevice {
|
|
274
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
275
|
+
name: info.friendlyName || info.name,
|
|
276
|
+
manufacturer: info.manufacturer,
|
|
277
|
+
model: info.modelName,
|
|
278
|
+
retryOptions,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Override the generated ID with discovery ID
|
|
282
|
+
(device as { id: string }).id = info.id;
|
|
283
|
+
|
|
284
|
+
// Add playback feature for DLNA
|
|
285
|
+
const playbackFeature = new PlaybackFeature(device.getDeviceReference(), info.port, {
|
|
286
|
+
protocol: 'dlna',
|
|
287
|
+
supportsQueue: false,
|
|
288
|
+
supportsSeek: true,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
device.addFeature(playbackFeature);
|
|
292
|
+
|
|
293
|
+
// Add volume feature
|
|
294
|
+
const volumeFeature = new VolumeFeature(device.getDeviceReference(), info.port, {
|
|
295
|
+
volumeProtocol: 'dlna',
|
|
296
|
+
minVolume: 0,
|
|
297
|
+
maxVolume: 100,
|
|
298
|
+
supportsMute: true,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
device.addFeature(volumeFeature);
|
|
302
|
+
|
|
303
|
+
return device;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ============================================================================
|
|
307
|
+
// Parsing Helpers
|
|
308
|
+
// ============================================================================
|
|
309
|
+
|
|
310
|
+
function parseScanFormats(txtRecords: Record<string, string>): TScanFormat[] {
|
|
311
|
+
const formats: TScanFormat[] = [];
|
|
312
|
+
const pdl = txtRecords['pdl'] || txtRecords['DocumentFormat'] || '';
|
|
313
|
+
|
|
314
|
+
if (pdl.includes('jpeg') || pdl.includes('jpg')) formats.push('jpeg');
|
|
315
|
+
if (pdl.includes('png')) formats.push('png');
|
|
316
|
+
if (pdl.includes('pdf')) formats.push('pdf');
|
|
317
|
+
if (pdl.includes('tiff')) formats.push('tiff');
|
|
318
|
+
|
|
319
|
+
return formats.length > 0 ? formats : ['jpeg', 'png'];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function parseScanResolutions(txtRecords: Record<string, string>): number[] {
|
|
323
|
+
const rs = txtRecords['rs'] || '';
|
|
324
|
+
const parts = rs.split(',').map((s) => parseInt(s.trim())).filter((n) => !isNaN(n) && n > 0);
|
|
325
|
+
return parts.length > 0 ? parts : [75, 150, 300, 600];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function parseScanColorModes(txtRecords: Record<string, string>): TColorMode[] {
|
|
329
|
+
const cs = txtRecords['cs'] || txtRecords['ColorSpace'] || '';
|
|
330
|
+
const modes: TColorMode[] = [];
|
|
331
|
+
|
|
332
|
+
if (cs.includes('color') || cs.includes('RGB')) modes.push('color');
|
|
333
|
+
if (cs.includes('gray') || cs.includes('grayscale')) modes.push('grayscale');
|
|
334
|
+
if (cs.includes('binary') || cs.includes('bw')) modes.push('blackwhite');
|
|
335
|
+
|
|
336
|
+
return modes.length > 0 ? modes : ['color', 'grayscale'];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function parseScanSources(txtRecords: Record<string, string>): TScanSource[] {
|
|
340
|
+
const is = txtRecords['is'] || txtRecords['InputSource'] || '';
|
|
341
|
+
const sources: TScanSource[] = [];
|
|
342
|
+
|
|
343
|
+
if (is.includes('platen') || is.includes('flatbed') || is === '') {
|
|
344
|
+
sources.push('flatbed');
|
|
345
|
+
}
|
|
346
|
+
if (is.includes('adf') || is.includes('feeder')) {
|
|
347
|
+
sources.push('adf');
|
|
348
|
+
}
|
|
349
|
+
if (is.includes('duplex')) {
|
|
350
|
+
sources.push('adf-duplex');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return sources.length > 0 ? sources : ['flatbed'];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ============================================================================
|
|
357
|
+
// Smart Home Factories
|
|
358
|
+
// ============================================================================
|
|
359
|
+
|
|
360
|
+
import { SwitchFeature, type ISwitchFeatureOptions } from '../features/feature.switch.js';
|
|
361
|
+
import { SensorFeature, type ISensorFeatureOptions } from '../features/feature.sensor.js';
|
|
362
|
+
import { LightFeature, type ILightFeatureOptions } from '../features/feature.light.js';
|
|
363
|
+
import { CoverFeature, type ICoverFeatureOptions } from '../features/feature.cover.js';
|
|
364
|
+
import { LockFeature, type ILockFeatureOptions } from '../features/feature.lock.js';
|
|
365
|
+
import { FanFeature, type IFanFeatureOptions } from '../features/feature.fan.js';
|
|
366
|
+
import { ClimateFeature, type IClimateFeatureOptions } from '../features/feature.climate.js';
|
|
367
|
+
import { CameraFeature, type ICameraFeatureOptions } from '../features/feature.camera.js';
|
|
368
|
+
|
|
369
|
+
import type {
|
|
370
|
+
TSwitchProtocol,
|
|
371
|
+
TSensorProtocol,
|
|
372
|
+
TLightProtocol,
|
|
373
|
+
TCoverProtocol,
|
|
374
|
+
TLockProtocol,
|
|
375
|
+
TFanProtocol,
|
|
376
|
+
TClimateProtocol,
|
|
377
|
+
TCameraProtocol,
|
|
378
|
+
ISwitchProtocolClient,
|
|
379
|
+
ISensorProtocolClient,
|
|
380
|
+
ILightProtocolClient,
|
|
381
|
+
ICoverProtocolClient,
|
|
382
|
+
ILockProtocolClient,
|
|
383
|
+
IFanProtocolClient,
|
|
384
|
+
IClimateProtocolClient,
|
|
385
|
+
ICameraProtocolClient,
|
|
386
|
+
ILightCapabilities,
|
|
387
|
+
ICoverCapabilities,
|
|
388
|
+
IFanCapabilities,
|
|
389
|
+
IClimateCapabilities,
|
|
390
|
+
ICameraCapabilities,
|
|
391
|
+
TSensorDeviceClass,
|
|
392
|
+
TSensorStateClass,
|
|
393
|
+
TCoverDeviceClass,
|
|
394
|
+
} from '../interfaces/smarthome.interfaces.js';
|
|
395
|
+
|
|
396
|
+
// Smart Switch Factory
|
|
397
|
+
export interface ISmartSwitchDiscoveryInfo {
|
|
398
|
+
id: string;
|
|
399
|
+
name: string;
|
|
400
|
+
address: string;
|
|
401
|
+
port: number;
|
|
402
|
+
entityId: string;
|
|
403
|
+
protocol: TSwitchProtocol;
|
|
404
|
+
protocolClient: ISwitchProtocolClient;
|
|
405
|
+
deviceClass?: 'outlet' | 'switch';
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function createSmartSwitch(
|
|
409
|
+
info: ISmartSwitchDiscoveryInfo,
|
|
410
|
+
retryOptions?: IRetryOptions
|
|
411
|
+
): UniversalDevice {
|
|
412
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
413
|
+
name: info.name,
|
|
414
|
+
retryOptions,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
(device as { id: string }).id = info.id;
|
|
418
|
+
|
|
419
|
+
const switchFeature = new SwitchFeature(device.getDeviceReference(), info.port, {
|
|
420
|
+
protocol: info.protocol,
|
|
421
|
+
entityId: info.entityId,
|
|
422
|
+
protocolClient: info.protocolClient,
|
|
423
|
+
deviceClass: info.deviceClass,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
device.addFeature(switchFeature);
|
|
427
|
+
return device;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Smart Sensor Factory
|
|
431
|
+
export interface ISmartSensorDiscoveryInfo {
|
|
432
|
+
id: string;
|
|
433
|
+
name: string;
|
|
434
|
+
address: string;
|
|
435
|
+
port: number;
|
|
436
|
+
entityId: string;
|
|
437
|
+
protocol: TSensorProtocol;
|
|
438
|
+
protocolClient: ISensorProtocolClient;
|
|
439
|
+
deviceClass?: TSensorDeviceClass;
|
|
440
|
+
stateClass?: TSensorStateClass;
|
|
441
|
+
unit?: string;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function createSmartSensor(
|
|
445
|
+
info: ISmartSensorDiscoveryInfo,
|
|
446
|
+
retryOptions?: IRetryOptions
|
|
447
|
+
): UniversalDevice {
|
|
448
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
449
|
+
name: info.name,
|
|
450
|
+
retryOptions,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
(device as { id: string }).id = info.id;
|
|
454
|
+
|
|
455
|
+
const sensorFeature = new SensorFeature(device.getDeviceReference(), info.port, {
|
|
456
|
+
protocol: info.protocol,
|
|
457
|
+
entityId: info.entityId,
|
|
458
|
+
protocolClient: info.protocolClient,
|
|
459
|
+
deviceClass: info.deviceClass,
|
|
460
|
+
stateClass: info.stateClass,
|
|
461
|
+
unit: info.unit,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
device.addFeature(sensorFeature);
|
|
465
|
+
return device;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Smart Light Factory
|
|
469
|
+
export interface ISmartLightDiscoveryInfo {
|
|
470
|
+
id: string;
|
|
471
|
+
name: string;
|
|
472
|
+
address: string;
|
|
473
|
+
port: number;
|
|
474
|
+
entityId: string;
|
|
475
|
+
protocol: TLightProtocol;
|
|
476
|
+
protocolClient: ILightProtocolClient;
|
|
477
|
+
capabilities?: Partial<ILightCapabilities>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export function createSmartLight(
|
|
481
|
+
info: ISmartLightDiscoveryInfo,
|
|
482
|
+
retryOptions?: IRetryOptions
|
|
483
|
+
): UniversalDevice {
|
|
484
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
485
|
+
name: info.name,
|
|
486
|
+
retryOptions,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
(device as { id: string }).id = info.id;
|
|
490
|
+
|
|
491
|
+
const lightFeature = new LightFeature(device.getDeviceReference(), info.port, {
|
|
492
|
+
protocol: info.protocol,
|
|
493
|
+
entityId: info.entityId,
|
|
494
|
+
protocolClient: info.protocolClient,
|
|
495
|
+
capabilities: info.capabilities,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
device.addFeature(lightFeature);
|
|
499
|
+
return device;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Smart Cover Factory
|
|
503
|
+
export interface ISmartCoverDiscoveryInfo {
|
|
504
|
+
id: string;
|
|
505
|
+
name: string;
|
|
506
|
+
address: string;
|
|
507
|
+
port: number;
|
|
508
|
+
entityId: string;
|
|
509
|
+
protocol: TCoverProtocol;
|
|
510
|
+
protocolClient: ICoverProtocolClient;
|
|
511
|
+
deviceClass?: TCoverDeviceClass;
|
|
512
|
+
capabilities?: Partial<ICoverCapabilities>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function createSmartCover(
|
|
516
|
+
info: ISmartCoverDiscoveryInfo,
|
|
517
|
+
retryOptions?: IRetryOptions
|
|
518
|
+
): UniversalDevice {
|
|
519
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
520
|
+
name: info.name,
|
|
521
|
+
retryOptions,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
(device as { id: string }).id = info.id;
|
|
525
|
+
|
|
526
|
+
const coverFeature = new CoverFeature(device.getDeviceReference(), info.port, {
|
|
527
|
+
protocol: info.protocol,
|
|
528
|
+
entityId: info.entityId,
|
|
529
|
+
protocolClient: info.protocolClient,
|
|
530
|
+
deviceClass: info.deviceClass,
|
|
531
|
+
capabilities: info.capabilities,
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
device.addFeature(coverFeature);
|
|
535
|
+
return device;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Smart Lock Factory
|
|
539
|
+
export interface ISmartLockDiscoveryInfo {
|
|
540
|
+
id: string;
|
|
541
|
+
name: string;
|
|
542
|
+
address: string;
|
|
543
|
+
port: number;
|
|
544
|
+
entityId: string;
|
|
545
|
+
protocol: TLockProtocol;
|
|
546
|
+
protocolClient: ILockProtocolClient;
|
|
547
|
+
supportsOpen?: boolean;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function createSmartLock(
|
|
551
|
+
info: ISmartLockDiscoveryInfo,
|
|
552
|
+
retryOptions?: IRetryOptions
|
|
553
|
+
): UniversalDevice {
|
|
554
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
555
|
+
name: info.name,
|
|
556
|
+
retryOptions,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
(device as { id: string }).id = info.id;
|
|
560
|
+
|
|
561
|
+
const lockFeature = new LockFeature(device.getDeviceReference(), info.port, {
|
|
562
|
+
protocol: info.protocol,
|
|
563
|
+
entityId: info.entityId,
|
|
564
|
+
protocolClient: info.protocolClient,
|
|
565
|
+
supportsOpen: info.supportsOpen,
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
device.addFeature(lockFeature);
|
|
569
|
+
return device;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Smart Fan Factory
|
|
573
|
+
export interface ISmartFanDiscoveryInfo {
|
|
574
|
+
id: string;
|
|
575
|
+
name: string;
|
|
576
|
+
address: string;
|
|
577
|
+
port: number;
|
|
578
|
+
entityId: string;
|
|
579
|
+
protocol: TFanProtocol;
|
|
580
|
+
protocolClient: IFanProtocolClient;
|
|
581
|
+
capabilities?: Partial<IFanCapabilities>;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function createSmartFan(
|
|
585
|
+
info: ISmartFanDiscoveryInfo,
|
|
586
|
+
retryOptions?: IRetryOptions
|
|
587
|
+
): UniversalDevice {
|
|
588
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
589
|
+
name: info.name,
|
|
590
|
+
retryOptions,
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
(device as { id: string }).id = info.id;
|
|
594
|
+
|
|
595
|
+
const fanFeature = new FanFeature(device.getDeviceReference(), info.port, {
|
|
596
|
+
protocol: info.protocol,
|
|
597
|
+
entityId: info.entityId,
|
|
598
|
+
protocolClient: info.protocolClient,
|
|
599
|
+
capabilities: info.capabilities,
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
device.addFeature(fanFeature);
|
|
603
|
+
return device;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Smart Climate Factory
|
|
607
|
+
export interface ISmartClimateDiscoveryInfo {
|
|
608
|
+
id: string;
|
|
609
|
+
name: string;
|
|
610
|
+
address: string;
|
|
611
|
+
port: number;
|
|
612
|
+
entityId: string;
|
|
613
|
+
protocol: TClimateProtocol;
|
|
614
|
+
protocolClient: IClimateProtocolClient;
|
|
615
|
+
capabilities?: Partial<IClimateCapabilities>;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export function createSmartClimate(
|
|
619
|
+
info: ISmartClimateDiscoveryInfo,
|
|
620
|
+
retryOptions?: IRetryOptions
|
|
621
|
+
): UniversalDevice {
|
|
622
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
623
|
+
name: info.name,
|
|
624
|
+
retryOptions,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
(device as { id: string }).id = info.id;
|
|
628
|
+
|
|
629
|
+
const climateFeature = new ClimateFeature(device.getDeviceReference(), info.port, {
|
|
630
|
+
protocol: info.protocol,
|
|
631
|
+
entityId: info.entityId,
|
|
632
|
+
protocolClient: info.protocolClient,
|
|
633
|
+
capabilities: info.capabilities,
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
device.addFeature(climateFeature);
|
|
637
|
+
return device;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Smart Camera Factory
|
|
641
|
+
export interface ISmartCameraDiscoveryInfo {
|
|
642
|
+
id: string;
|
|
643
|
+
name: string;
|
|
644
|
+
address: string;
|
|
645
|
+
port: number;
|
|
646
|
+
entityId: string;
|
|
647
|
+
protocol: TCameraProtocol;
|
|
648
|
+
protocolClient: ICameraProtocolClient;
|
|
649
|
+
capabilities?: Partial<ICameraCapabilities>;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
export function createSmartCamera(
|
|
653
|
+
info: ISmartCameraDiscoveryInfo,
|
|
654
|
+
retryOptions?: IRetryOptions
|
|
655
|
+
): UniversalDevice {
|
|
656
|
+
const device = new UniversalDevice(info.address, info.port, {
|
|
657
|
+
name: info.name,
|
|
658
|
+
retryOptions,
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
(device as { id: string }).id = info.id;
|
|
662
|
+
|
|
663
|
+
const cameraFeature = new CameraFeature(device.getDeviceReference(), info.port, {
|
|
664
|
+
protocol: info.protocol,
|
|
665
|
+
entityId: info.entityId,
|
|
666
|
+
protocolClient: info.protocolClient,
|
|
667
|
+
capabilities: info.capabilities,
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
device.addFeature(cameraFeature);
|
|
671
|
+
return device;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// ============================================================================
|
|
675
|
+
// Exports
|
|
676
|
+
// ============================================================================
|
|
677
|
+
|
|
678
|
+
export {
|
|
679
|
+
// Re-export device and feature types for convenience
|
|
680
|
+
UniversalDevice,
|
|
681
|
+
ScanFeature,
|
|
682
|
+
PrintFeature,
|
|
683
|
+
PlaybackFeature,
|
|
684
|
+
VolumeFeature,
|
|
685
|
+
PowerFeature,
|
|
686
|
+
SnmpFeature,
|
|
687
|
+
// Smart home features
|
|
688
|
+
SwitchFeature,
|
|
689
|
+
SensorFeature,
|
|
690
|
+
LightFeature,
|
|
691
|
+
CoverFeature,
|
|
692
|
+
LockFeature,
|
|
693
|
+
FanFeature,
|
|
694
|
+
ClimateFeature,
|
|
695
|
+
CameraFeature,
|
|
696
|
+
};
|