@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,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract Feature base class
|
|
3
|
+
* Features are composable capabilities that can be attached to devices
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as plugins from '../plugins.js';
|
|
7
|
+
import type {
|
|
8
|
+
TFeatureType,
|
|
9
|
+
TFeatureState,
|
|
10
|
+
IFeature,
|
|
11
|
+
IFeatureInfo,
|
|
12
|
+
IFeatureOptions,
|
|
13
|
+
} from '../interfaces/feature.interfaces.js';
|
|
14
|
+
import type { IRetryOptions } from '../interfaces/index.js';
|
|
15
|
+
import { withRetry } from '../helpers/helpers.retry.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Forward reference to Device to avoid circular dependency
|
|
19
|
+
* The actual Device class will set this reference
|
|
20
|
+
*/
|
|
21
|
+
export type TDeviceReference = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
address: string;
|
|
25
|
+
port: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Abstract base class for all features
|
|
30
|
+
* Provides common functionality for connection management, state tracking, and retry logic
|
|
31
|
+
*/
|
|
32
|
+
export abstract class Feature extends plugins.events.EventEmitter implements IFeature {
|
|
33
|
+
/**
|
|
34
|
+
* The feature type identifier
|
|
35
|
+
*/
|
|
36
|
+
public abstract readonly type: TFeatureType;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The protocol used by this feature
|
|
40
|
+
*/
|
|
41
|
+
public abstract readonly protocol: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The port used by this feature
|
|
45
|
+
*/
|
|
46
|
+
protected _port: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Current feature state
|
|
50
|
+
*/
|
|
51
|
+
protected _state: TFeatureState = 'disconnected';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Last error encountered
|
|
55
|
+
*/
|
|
56
|
+
protected _lastError?: Error;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reference to the parent device
|
|
60
|
+
*/
|
|
61
|
+
protected _device: TDeviceReference;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Retry configuration
|
|
65
|
+
*/
|
|
66
|
+
protected _retryConfig: Required<IRetryOptions>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Additional metadata from discovery
|
|
70
|
+
*/
|
|
71
|
+
protected _metadata: Record<string, unknown>;
|
|
72
|
+
|
|
73
|
+
constructor(
|
|
74
|
+
device: TDeviceReference,
|
|
75
|
+
port: number,
|
|
76
|
+
options?: IFeatureOptions
|
|
77
|
+
) {
|
|
78
|
+
super();
|
|
79
|
+
this._device = device;
|
|
80
|
+
this._port = port;
|
|
81
|
+
this._metadata = options?.metadata ?? {};
|
|
82
|
+
|
|
83
|
+
// Setup retry config
|
|
84
|
+
const retryOpts = options?.retryOptions ?? {};
|
|
85
|
+
this._retryConfig = {
|
|
86
|
+
maxRetries: retryOpts.maxRetries ?? 3,
|
|
87
|
+
baseDelay: retryOpts.baseDelay ?? 1000,
|
|
88
|
+
maxDelay: retryOpts.maxDelay ?? 30000,
|
|
89
|
+
multiplier: retryOpts.multiplier ?? 2,
|
|
90
|
+
jitter: retryOpts.jitter ?? true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Public Properties
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get the feature port
|
|
100
|
+
*/
|
|
101
|
+
public get port(): number {
|
|
102
|
+
return this._port;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the current feature state
|
|
107
|
+
*/
|
|
108
|
+
public get state(): TFeatureState {
|
|
109
|
+
return this._state;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if the feature is connected
|
|
114
|
+
*/
|
|
115
|
+
public get isConnected(): boolean {
|
|
116
|
+
return this._state === 'connected';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get the last error
|
|
121
|
+
*/
|
|
122
|
+
public get lastError(): Error | undefined {
|
|
123
|
+
return this._lastError;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get the device address
|
|
128
|
+
*/
|
|
129
|
+
public get address(): string {
|
|
130
|
+
return this._device.address;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get the device ID
|
|
135
|
+
*/
|
|
136
|
+
public get deviceId(): string {
|
|
137
|
+
return this._device.id;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Connection Management
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Connect to the feature endpoint
|
|
146
|
+
*/
|
|
147
|
+
public async connect(): Promise<void> {
|
|
148
|
+
if (this._state === 'connected') {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (this._state === 'connecting') {
|
|
153
|
+
throw new Error(`Feature ${this.type} is already connecting`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.setState('connecting');
|
|
157
|
+
this._lastError = undefined;
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
await this.withRetry(() => this.doConnect());
|
|
161
|
+
this.setState('connected');
|
|
162
|
+
this.emit('connected');
|
|
163
|
+
} catch (error) {
|
|
164
|
+
this._lastError = error instanceof Error ? error : new Error(String(error));
|
|
165
|
+
this.setState('error');
|
|
166
|
+
this.emit('error', this._lastError);
|
|
167
|
+
throw this._lastError;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Disconnect from the feature endpoint
|
|
173
|
+
*/
|
|
174
|
+
public async disconnect(): Promise<void> {
|
|
175
|
+
if (this._state === 'disconnected') {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await this.doDisconnect();
|
|
181
|
+
} finally {
|
|
182
|
+
this.setState('disconnected');
|
|
183
|
+
this.emit('disconnected');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// Abstract Methods (implemented by subclasses)
|
|
189
|
+
// ============================================================================
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Perform the actual connection logic
|
|
193
|
+
* Implemented by each feature subclass
|
|
194
|
+
*/
|
|
195
|
+
protected abstract doConnect(): Promise<void>;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Perform the actual disconnection logic
|
|
199
|
+
* Implemented by each feature subclass
|
|
200
|
+
*/
|
|
201
|
+
protected abstract doDisconnect(): Promise<void>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get feature-specific info for serialization
|
|
205
|
+
*/
|
|
206
|
+
public abstract getFeatureInfo(): IFeatureInfo;
|
|
207
|
+
|
|
208
|
+
// ============================================================================
|
|
209
|
+
// Helper Methods
|
|
210
|
+
// ============================================================================
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Set the feature state and emit change event
|
|
214
|
+
*/
|
|
215
|
+
protected setState(state: TFeatureState): void {
|
|
216
|
+
if (this._state !== state) {
|
|
217
|
+
const oldState = this._state;
|
|
218
|
+
this._state = state;
|
|
219
|
+
this.emit('state:changed', { oldState, newState: state });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Execute an operation with retry logic
|
|
225
|
+
*/
|
|
226
|
+
protected async withRetry<T>(operation: () => Promise<T>): Promise<T> {
|
|
227
|
+
return withRetry(operation, this._retryConfig);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get base feature info
|
|
232
|
+
*/
|
|
233
|
+
protected getBaseFeatureInfo(): IFeatureInfo {
|
|
234
|
+
return {
|
|
235
|
+
type: this.type,
|
|
236
|
+
protocol: this.protocol,
|
|
237
|
+
port: this._port,
|
|
238
|
+
state: this._state,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Clear error state
|
|
244
|
+
*/
|
|
245
|
+
public clearError(): void {
|
|
246
|
+
this._lastError = undefined;
|
|
247
|
+
if (this._state === 'error') {
|
|
248
|
+
this.setState('disconnected');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera Feature
|
|
3
|
+
* Provides control for smart cameras (snapshots, streams)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Feature, type TDeviceReference } from './feature.abstract.js';
|
|
7
|
+
import type { IFeatureOptions } from '../interfaces/feature.interfaces.js';
|
|
8
|
+
import type {
|
|
9
|
+
TCameraProtocol,
|
|
10
|
+
ICameraCapabilities,
|
|
11
|
+
ICameraState,
|
|
12
|
+
ICameraFeatureInfo,
|
|
13
|
+
ICameraProtocolClient,
|
|
14
|
+
} from '../interfaces/smarthome.interfaces.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating a CameraFeature
|
|
18
|
+
*/
|
|
19
|
+
export interface ICameraFeatureOptions extends IFeatureOptions {
|
|
20
|
+
/** Protocol type */
|
|
21
|
+
protocol: TCameraProtocol;
|
|
22
|
+
/** Entity ID (for Home Assistant) */
|
|
23
|
+
entityId: string;
|
|
24
|
+
/** Protocol client for the camera */
|
|
25
|
+
protocolClient: ICameraProtocolClient;
|
|
26
|
+
/** Camera capabilities */
|
|
27
|
+
capabilities?: Partial<ICameraCapabilities>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Camera Feature - snapshot and stream access
|
|
32
|
+
*
|
|
33
|
+
* Protocol-agnostic: works with Home Assistant, ONVIF, RTSP, etc.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const camera = device.getFeature<CameraFeature>('camera');
|
|
38
|
+
* if (camera) {
|
|
39
|
+
* const snapshot = await camera.getSnapshot();
|
|
40
|
+
* const streamUrl = await camera.getStreamUrl();
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export class CameraFeature extends Feature {
|
|
45
|
+
public readonly type = 'camera' as const;
|
|
46
|
+
public readonly protocol: TCameraProtocol;
|
|
47
|
+
|
|
48
|
+
/** Entity ID (e.g., "camera.front_door") */
|
|
49
|
+
public readonly entityId: string;
|
|
50
|
+
|
|
51
|
+
/** Capabilities */
|
|
52
|
+
public readonly capabilities: ICameraCapabilities;
|
|
53
|
+
|
|
54
|
+
/** Current state */
|
|
55
|
+
protected _isRecording: boolean = false;
|
|
56
|
+
protected _isStreaming: boolean = false;
|
|
57
|
+
protected _motionDetected: boolean = false;
|
|
58
|
+
|
|
59
|
+
/** Protocol client for the camera */
|
|
60
|
+
private protocolClient: ICameraProtocolClient;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
device: TDeviceReference,
|
|
64
|
+
port: number,
|
|
65
|
+
options: ICameraFeatureOptions
|
|
66
|
+
) {
|
|
67
|
+
super(device, port, options);
|
|
68
|
+
this.protocol = options.protocol;
|
|
69
|
+
this.entityId = options.entityId;
|
|
70
|
+
this.protocolClient = options.protocolClient;
|
|
71
|
+
|
|
72
|
+
this.capabilities = {
|
|
73
|
+
supportsStream: options.capabilities?.supportsStream ?? true,
|
|
74
|
+
supportsPtz: options.capabilities?.supportsPtz ?? false,
|
|
75
|
+
supportsSnapshot: options.capabilities?.supportsSnapshot ?? true,
|
|
76
|
+
supportsMotionDetection: options.capabilities?.supportsMotionDetection ?? false,
|
|
77
|
+
frontendStreamType: options.capabilities?.frontendStreamType,
|
|
78
|
+
streamUrl: options.capabilities?.streamUrl,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// Properties
|
|
84
|
+
// ============================================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if recording (cached)
|
|
88
|
+
*/
|
|
89
|
+
public get isRecording(): boolean {
|
|
90
|
+
return this._isRecording;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if streaming (cached)
|
|
95
|
+
*/
|
|
96
|
+
public get isStreaming(): boolean {
|
|
97
|
+
return this._isStreaming;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if motion detected (cached)
|
|
102
|
+
*/
|
|
103
|
+
public get motionDetected(): boolean {
|
|
104
|
+
return this._motionDetected;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Connection
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
protected async doConnect(): Promise<void> {
|
|
112
|
+
try {
|
|
113
|
+
const state = await this.protocolClient.getState(this.entityId);
|
|
114
|
+
this.updateStateInternal(state);
|
|
115
|
+
} catch {
|
|
116
|
+
// Ignore errors fetching initial state
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected async doDisconnect(): Promise<void> {
|
|
121
|
+
// Nothing to disconnect
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ============================================================================
|
|
125
|
+
// Camera Access
|
|
126
|
+
// ============================================================================
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get a snapshot image from the camera
|
|
130
|
+
* @returns Buffer containing image data
|
|
131
|
+
*/
|
|
132
|
+
public async getSnapshot(): Promise<Buffer> {
|
|
133
|
+
if (!this.capabilities.supportsSnapshot) {
|
|
134
|
+
throw new Error('Camera does not support snapshots');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return this.protocolClient.getSnapshot(this.entityId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get snapshot URL
|
|
142
|
+
* @returns URL for the snapshot image
|
|
143
|
+
*/
|
|
144
|
+
public async getSnapshotUrl(): Promise<string> {
|
|
145
|
+
if (!this.capabilities.supportsSnapshot) {
|
|
146
|
+
throw new Error('Camera does not support snapshots');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return this.protocolClient.getSnapshotUrl(this.entityId);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get stream URL
|
|
154
|
+
* @returns URL for the video stream
|
|
155
|
+
*/
|
|
156
|
+
public async getStreamUrl(): Promise<string> {
|
|
157
|
+
if (!this.capabilities.supportsStream) {
|
|
158
|
+
throw new Error('Camera does not support streaming');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this.protocolClient.getStreamUrl(this.entityId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get current state as object
|
|
166
|
+
*/
|
|
167
|
+
public getState(): ICameraState {
|
|
168
|
+
return {
|
|
169
|
+
isRecording: this._isRecording,
|
|
170
|
+
isStreaming: this._isStreaming,
|
|
171
|
+
motionDetected: this._motionDetected,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Refresh state from the device
|
|
177
|
+
*/
|
|
178
|
+
public async refreshState(): Promise<ICameraState> {
|
|
179
|
+
const state = await this.protocolClient.getState(this.entityId);
|
|
180
|
+
this.updateStateInternal(state);
|
|
181
|
+
return state;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Update state from external source
|
|
186
|
+
*/
|
|
187
|
+
public updateState(state: ICameraState): void {
|
|
188
|
+
this.updateStateInternal(state);
|
|
189
|
+
this.emit('state:changed', state);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Internal state update
|
|
194
|
+
*/
|
|
195
|
+
private updateStateInternal(state: ICameraState): void {
|
|
196
|
+
this._isRecording = state.isRecording;
|
|
197
|
+
this._isStreaming = state.isStreaming;
|
|
198
|
+
this._motionDetected = state.motionDetected;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// Feature Info
|
|
203
|
+
// ============================================================================
|
|
204
|
+
|
|
205
|
+
public getFeatureInfo(): ICameraFeatureInfo {
|
|
206
|
+
return {
|
|
207
|
+
...this.getBaseFeatureInfo(),
|
|
208
|
+
type: 'camera',
|
|
209
|
+
protocol: this.protocol,
|
|
210
|
+
capabilities: this.capabilities,
|
|
211
|
+
currentState: this.getState(),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|