1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.CameraController = exports.CameraControllerEvents = exports.ResourceRequestReason = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
6 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
7 | const events_1 = require("events");
|
8 | const camera_1 = require("../camera");
|
9 | const Characteristic_1 = require("../Characteristic");
|
10 | const datastream_1 = require("../datastream");
|
11 | const Service_1 = require("../Service");
|
12 | const hapStatusError_1 = require("../util/hapStatusError");
|
13 | const debug = (0, debug_1.default)("HAP-NodeJS:Camera:Controller");
|
14 |
|
15 |
|
16 |
|
17 | var ResourceRequestReason;
|
18 | (function (ResourceRequestReason) {
|
19 | |
20 |
|
21 |
|
22 |
|
23 | ResourceRequestReason[ResourceRequestReason["PERIODIC"] = 0] = "PERIODIC";
|
24 | |
25 |
|
26 |
|
27 |
|
28 | ResourceRequestReason[ResourceRequestReason["EVENT"] = 1] = "EVENT";
|
29 | })(ResourceRequestReason || (exports.ResourceRequestReason = ResourceRequestReason = {}));
|
30 |
|
31 |
|
32 |
|
33 | var CameraControllerEvents;
|
34 | (function (CameraControllerEvents) {
|
35 | |
36 |
|
37 |
|
38 |
|
39 |
|
40 | CameraControllerEvents["MICROPHONE_PROPERTIES_CHANGED"] = "microphone-change";
|
41 | |
42 |
|
43 |
|
44 |
|
45 | CameraControllerEvents["SPEAKER_PROPERTIES_CHANGED"] = "speaker-change";
|
46 | })(CameraControllerEvents || (exports.CameraControllerEvents = CameraControllerEvents = {}));
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | class CameraController extends events_1.EventEmitter {
|
54 | static STREAM_MANAGEMENT = "streamManagement";
|
55 | stateChangeDelegate;
|
56 | streamCount;
|
57 | delegate;
|
58 | streamingOptions;
|
59 | |
60 |
|
61 |
|
62 |
|
63 |
|
64 | recording;
|
65 | |
66 |
|
67 |
|
68 | sensorOptions;
|
69 | legacyMode = false;
|
70 | |
71 |
|
72 |
|
73 | streamManagements = [];
|
74 | |
75 |
|
76 |
|
77 |
|
78 | recordingManagement;
|
79 | microphoneService;
|
80 | speakerService;
|
81 | microphoneMuted = false;
|
82 | microphoneVolume = 100;
|
83 | speakerMuted = false;
|
84 | speakerVolume = 100;
|
85 | motionService;
|
86 | motionServiceExternallySupplied = false;
|
87 | occupancyService;
|
88 | occupancyServiceExternallySupplied = false;
|
89 | constructor(options, legacyMode = false) {
|
90 | super();
|
91 | this.streamCount = Math.max(1, options.cameraStreamCount || 1);
|
92 | this.delegate = options.delegate;
|
93 | this.streamingOptions = options.streamingOptions;
|
94 | this.recording = options.recording;
|
95 | this.sensorOptions = options.sensors;
|
96 | this.legacyMode = legacyMode;
|
97 | }
|
98 | |
99 |
|
100 |
|
101 | controllerId() {
|
102 | return "camera" ;
|
103 | }
|
104 |
|
105 | |
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | forceStopStreamingSession(sessionId) {
|
112 | this.streamManagements.forEach(management => {
|
113 | if (management.sessionIdentifier === sessionId) {
|
114 | management.forceStop();
|
115 | }
|
116 | });
|
117 | }
|
118 | static generateSynchronisationSource() {
|
119 | const ssrc = crypto_1.default.randomBytes(4);
|
120 | ssrc[0] = 0;
|
121 | return ssrc.readInt32BE(0);
|
122 | }
|
123 |
|
124 | setMicrophoneMuted(muted = true) {
|
125 | if (!this.microphoneService) {
|
126 | return;
|
127 | }
|
128 | this.microphoneMuted = muted;
|
129 | this.microphoneService.updateCharacteristic(Characteristic_1.Characteristic.Mute, muted);
|
130 | }
|
131 | setMicrophoneVolume(volume) {
|
132 | if (!this.microphoneService) {
|
133 | return;
|
134 | }
|
135 | this.microphoneVolume = volume;
|
136 | this.microphoneService.updateCharacteristic(Characteristic_1.Characteristic.Volume, volume);
|
137 | }
|
138 | setSpeakerMuted(muted = true) {
|
139 | if (!this.speakerService) {
|
140 | return;
|
141 | }
|
142 | this.speakerMuted = muted;
|
143 | this.speakerService.updateCharacteristic(Characteristic_1.Characteristic.Mute, muted);
|
144 | }
|
145 | setSpeakerVolume(volume) {
|
146 | if (!this.speakerService) {
|
147 | return;
|
148 | }
|
149 | this.speakerVolume = volume;
|
150 | this.speakerService.updateCharacteristic(Characteristic_1.Characteristic.Volume, volume);
|
151 | }
|
152 | emitMicrophoneChange() {
|
153 | this.emit("microphone-change" , this.microphoneMuted, this.microphoneVolume);
|
154 | }
|
155 | emitSpeakerChange() {
|
156 | this.emit("speaker-change" , this.speakerMuted, this.speakerVolume);
|
157 | }
|
158 |
|
159 | |
160 |
|
161 |
|
162 | constructServices() {
|
163 | for (let i = 0; i < this.streamCount; i++) {
|
164 | const rtp = new camera_1.RTPStreamManagement(i, this.streamingOptions, this.delegate, undefined, this.rtpStreamManagementDisabledThroughOperatingMode.bind(this));
|
165 | this.streamManagements.push(rtp);
|
166 | }
|
167 | if (!this.legacyMode && this.streamingOptions.audio) {
|
168 |
|
169 |
|
170 | this.microphoneService = new Service_1.Service.Microphone("", "");
|
171 | this.microphoneService.setCharacteristic(Characteristic_1.Characteristic.Volume, this.microphoneVolume);
|
172 | if (this.streamingOptions.audio.twoWayAudio) {
|
173 | this.speakerService = new Service_1.Service.Speaker("", "");
|
174 | this.speakerService.setCharacteristic(Characteristic_1.Characteristic.Volume, this.speakerVolume);
|
175 | }
|
176 | }
|
177 | if (this.recording) {
|
178 | this.recordingManagement = new camera_1.RecordingManagement(this.recording.options, this.recording.delegate, this.retrieveEventTriggerOptions());
|
179 | }
|
180 | if (this.sensorOptions?.motion) {
|
181 | if (typeof this.sensorOptions.motion === "boolean") {
|
182 | this.motionService = new Service_1.Service.MotionSensor("", "");
|
183 | }
|
184 | else {
|
185 | this.motionService = this.sensorOptions.motion;
|
186 | this.motionServiceExternallySupplied = true;
|
187 | }
|
188 | this.motionService.setCharacteristic(Characteristic_1.Characteristic.StatusActive, true);
|
189 | this.recordingManagement?.recordingManagementService.addLinkedService(this.motionService);
|
190 | }
|
191 | if (this.sensorOptions?.occupancy) {
|
192 | if (typeof this.sensorOptions.occupancy === "boolean") {
|
193 | this.occupancyService = new Service_1.Service.OccupancySensor("", "");
|
194 | }
|
195 | else {
|
196 | this.occupancyService = this.sensorOptions.occupancy;
|
197 | this.occupancyServiceExternallySupplied = true;
|
198 | }
|
199 | this.occupancyService.setCharacteristic(Characteristic_1.Characteristic.StatusActive, true);
|
200 | this.recordingManagement?.recordingManagementService.addLinkedService(this.occupancyService);
|
201 | }
|
202 | const serviceMap = {
|
203 | microphone: this.microphoneService,
|
204 | speaker: this.speakerService,
|
205 | motionService: !this.motionServiceExternallySupplied ? this.motionService : undefined,
|
206 | occupancyService: !this.occupancyServiceExternallySupplied ? this.occupancyService : undefined,
|
207 | };
|
208 | if (this.recordingManagement) {
|
209 | serviceMap.cameraEventRecordingManagement = this.recordingManagement.recordingManagementService;
|
210 | serviceMap.cameraOperatingMode = this.recordingManagement.operatingModeService;
|
211 | serviceMap.dataStreamTransportManagement = this.recordingManagement.dataStreamManagement.getService();
|
212 | }
|
213 | this.streamManagements.forEach((management, index) => {
|
214 | serviceMap[CameraController.STREAM_MANAGEMENT + index] = management.getService();
|
215 | });
|
216 | this.recording = undefined;
|
217 | this.sensorOptions = undefined;
|
218 | return serviceMap;
|
219 | }
|
220 | |
221 |
|
222 |
|
223 | initWithServices(serviceMap) {
|
224 | const result = this._initWithServices(serviceMap);
|
225 | if (result.updated) {
|
226 | return result.serviceMap;
|
227 | }
|
228 | }
|
229 | _initWithServices(serviceMap) {
|
230 | let modifiedServiceMap = false;
|
231 |
|
232 | for (let i = 0; true; i++) {
|
233 | const streamManagementService = serviceMap[CameraController.STREAM_MANAGEMENT + i];
|
234 | if (i < this.streamCount) {
|
235 | const operatingModeClosure = this.rtpStreamManagementDisabledThroughOperatingMode.bind(this);
|
236 | if (streamManagementService) {
|
237 | this.streamManagements.push(new camera_1.RTPStreamManagement(i, this.streamingOptions, this.delegate, streamManagementService, operatingModeClosure));
|
238 | }
|
239 | else {
|
240 | const management = new camera_1.RTPStreamManagement(i, this.streamingOptions, this.delegate, undefined, operatingModeClosure);
|
241 | this.streamManagements.push(management);
|
242 | serviceMap[CameraController.STREAM_MANAGEMENT + i] = management.getService();
|
243 | modifiedServiceMap = true;
|
244 | }
|
245 | }
|
246 | else {
|
247 | if (streamManagementService) {
|
248 | delete serviceMap[CameraController.STREAM_MANAGEMENT + i];
|
249 | modifiedServiceMap = true;
|
250 | }
|
251 | else {
|
252 | break;
|
253 | }
|
254 | }
|
255 | }
|
256 |
|
257 | if (!this.legacyMode && this.streamingOptions.audio) {
|
258 | if (serviceMap.microphone) {
|
259 | this.microphoneService = serviceMap.microphone;
|
260 | }
|
261 | else {
|
262 |
|
263 | this.microphoneService = new Service_1.Service.Microphone("", "");
|
264 | this.microphoneService.setCharacteristic(Characteristic_1.Characteristic.Volume, this.microphoneVolume);
|
265 | serviceMap.microphone = this.microphoneService;
|
266 | modifiedServiceMap = true;
|
267 | }
|
268 | }
|
269 | else if (serviceMap.microphone) {
|
270 |
|
271 | delete serviceMap.microphone;
|
272 | modifiedServiceMap = true;
|
273 | }
|
274 |
|
275 | if (!this.legacyMode && this.streamingOptions.audio?.twoWayAudio) {
|
276 | if (serviceMap.speaker) {
|
277 | this.speakerService = serviceMap.speaker;
|
278 | }
|
279 | else {
|
280 |
|
281 | this.speakerService = new Service_1.Service.Speaker("", "");
|
282 | this.speakerService.setCharacteristic(Characteristic_1.Characteristic.Volume, this.speakerVolume);
|
283 | serviceMap.speaker = this.speakerService;
|
284 | modifiedServiceMap = true;
|
285 | }
|
286 | }
|
287 | else if (serviceMap.speaker) {
|
288 |
|
289 | delete serviceMap.speaker;
|
290 | modifiedServiceMap = true;
|
291 | }
|
292 |
|
293 | if (this.recording) {
|
294 | const eventTriggers = this.retrieveEventTriggerOptions();
|
295 |
|
296 | if (serviceMap.cameraEventRecordingManagement && serviceMap.cameraOperatingMode && serviceMap.dataStreamTransportManagement) {
|
297 | this.recordingManagement = new camera_1.RecordingManagement(this.recording.options, this.recording.delegate, eventTriggers, {
|
298 | recordingManagement: serviceMap.cameraEventRecordingManagement,
|
299 | operatingMode: serviceMap.cameraOperatingMode,
|
300 | dataStreamManagement: new datastream_1.DataStreamManagement(serviceMap.dataStreamTransportManagement),
|
301 | });
|
302 | }
|
303 | else {
|
304 | this.recordingManagement = new camera_1.RecordingManagement(this.recording.options, this.recording.delegate, eventTriggers);
|
305 | serviceMap.cameraEventRecordingManagement = this.recordingManagement.recordingManagementService;
|
306 | serviceMap.cameraOperatingMode = this.recordingManagement.operatingModeService;
|
307 | serviceMap.dataStreamTransportManagement = this.recordingManagement.dataStreamManagement.getService();
|
308 | modifiedServiceMap = true;
|
309 | }
|
310 | }
|
311 | else {
|
312 | if (serviceMap.cameraEventRecordingManagement) {
|
313 | delete serviceMap.cameraEventRecordingManagement;
|
314 | modifiedServiceMap = true;
|
315 | }
|
316 | if (serviceMap.cameraOperatingMode) {
|
317 | delete serviceMap.cameraOperatingMode;
|
318 | modifiedServiceMap = true;
|
319 | }
|
320 | if (serviceMap.dataStreamTransportManagement) {
|
321 | delete serviceMap.dataStreamTransportManagement;
|
322 | modifiedServiceMap = true;
|
323 | }
|
324 | }
|
325 |
|
326 | if (this.sensorOptions?.motion) {
|
327 | if (typeof this.sensorOptions.motion === "boolean") {
|
328 | if (serviceMap.motionService) {
|
329 | this.motionService = serviceMap.motionService;
|
330 | }
|
331 | else {
|
332 |
|
333 |
|
334 | this.motionService = new Service_1.Service.MotionSensor("", "");
|
335 | }
|
336 | }
|
337 | else {
|
338 | this.motionService = this.sensorOptions.motion;
|
339 | this.motionServiceExternallySupplied = true;
|
340 | if (serviceMap.motionService) {
|
341 | this.recordingManagement?.recordingManagementService.removeLinkedService(serviceMap.motionService);
|
342 | delete serviceMap.motionService;
|
343 | modifiedServiceMap = true;
|
344 | }
|
345 | }
|
346 | this.motionService.setCharacteristic(Characteristic_1.Characteristic.StatusActive, true);
|
347 | this.recordingManagement?.recordingManagementService.addLinkedService(this.motionService);
|
348 | }
|
349 | else {
|
350 | if (serviceMap.motionService) {
|
351 | this.recordingManagement?.recordingManagementService.removeLinkedService(serviceMap.motionService);
|
352 | delete serviceMap.motionService;
|
353 | modifiedServiceMap = true;
|
354 | }
|
355 | }
|
356 |
|
357 | if (this.sensorOptions?.occupancy) {
|
358 | if (typeof this.sensorOptions.occupancy === "boolean") {
|
359 | if (serviceMap.occupancyService) {
|
360 | this.occupancyService = serviceMap.occupancyService;
|
361 | }
|
362 | else {
|
363 |
|
364 |
|
365 | this.occupancyService = new Service_1.Service.OccupancySensor("", "");
|
366 | }
|
367 | }
|
368 | else {
|
369 | this.occupancyService = this.sensorOptions.occupancy;
|
370 | this.occupancyServiceExternallySupplied = true;
|
371 | if (serviceMap.occupancyService) {
|
372 | this.recordingManagement?.recordingManagementService.removeLinkedService(serviceMap.occupancyService);
|
373 | delete serviceMap.occupancyService;
|
374 | modifiedServiceMap = true;
|
375 | }
|
376 | }
|
377 | this.occupancyService.setCharacteristic(Characteristic_1.Characteristic.StatusActive, true);
|
378 | this.recordingManagement?.recordingManagementService.addLinkedService(this.occupancyService);
|
379 | }
|
380 | else {
|
381 | if (serviceMap.occupancyService) {
|
382 | this.recordingManagement?.recordingManagementService.removeLinkedService(serviceMap.occupancyService);
|
383 | delete serviceMap.occupancyService;
|
384 | modifiedServiceMap = true;
|
385 | }
|
386 | }
|
387 | if (this.migrateFromDoorbell(serviceMap)) {
|
388 | modifiedServiceMap = true;
|
389 | }
|
390 | this.recording = undefined;
|
391 | this.sensorOptions = undefined;
|
392 | return {
|
393 | serviceMap: serviceMap,
|
394 | updated: modifiedServiceMap,
|
395 | };
|
396 | }
|
397 |
|
398 | migrateFromDoorbell(serviceMap) {
|
399 | if (serviceMap.doorbell) {
|
400 | delete serviceMap.doorbell;
|
401 | return true;
|
402 | }
|
403 | return false;
|
404 | }
|
405 | retrieveEventTriggerOptions() {
|
406 | if (!this.recording) {
|
407 | return new Set();
|
408 | }
|
409 | const triggerOptions = new Set();
|
410 | if (this.recording.options.overrideEventTriggerOptions) {
|
411 | for (const option of this.recording.options.overrideEventTriggerOptions) {
|
412 | triggerOptions.add(option);
|
413 | }
|
414 | }
|
415 | if (this.sensorOptions?.motion) {
|
416 | triggerOptions.add(1 );
|
417 | }
|
418 |
|
419 | return triggerOptions;
|
420 | }
|
421 | |
422 |
|
423 |
|
424 | configureServices() {
|
425 | if (this.microphoneService) {
|
426 | this.microphoneService.getCharacteristic(Characteristic_1.Characteristic.Mute)
|
427 | .on("get" , (callback) => {
|
428 | callback(undefined, this.microphoneMuted);
|
429 | })
|
430 | .on("set" , (value, callback) => {
|
431 | this.microphoneMuted = value;
|
432 | callback();
|
433 | this.emitMicrophoneChange();
|
434 | });
|
435 | this.microphoneService.getCharacteristic(Characteristic_1.Characteristic.Volume)
|
436 | .on("get" , (callback) => {
|
437 | callback(undefined, this.microphoneVolume);
|
438 | })
|
439 | .on("set" , (value, callback) => {
|
440 | this.microphoneVolume = value;
|
441 | callback();
|
442 | this.emitMicrophoneChange();
|
443 | });
|
444 | }
|
445 | if (this.speakerService) {
|
446 | this.speakerService.getCharacteristic(Characteristic_1.Characteristic.Mute)
|
447 | .on("get" , (callback) => {
|
448 | callback(undefined, this.speakerMuted);
|
449 | })
|
450 | .on("set" , (value, callback) => {
|
451 | this.speakerMuted = value;
|
452 | callback();
|
453 | this.emitSpeakerChange();
|
454 | });
|
455 | this.speakerService.getCharacteristic(Characteristic_1.Characteristic.Volume)
|
456 | .on("get" , (callback) => {
|
457 | callback(undefined, this.speakerVolume);
|
458 | })
|
459 | .on("set" , (value, callback) => {
|
460 | this.speakerVolume = value;
|
461 | callback();
|
462 | this.emitSpeakerChange();
|
463 | });
|
464 | }
|
465 |
|
466 | if (this.motionService) {
|
467 | this.recordingManagement?.sensorServices.push(this.motionService);
|
468 | }
|
469 | if (this.occupancyService) {
|
470 | this.recordingManagement?.sensorServices.push(this.occupancyService);
|
471 | }
|
472 | }
|
473 | rtpStreamManagementDisabledThroughOperatingMode() {
|
474 | return this.recordingManagement
|
475 | ? !this.recordingManagement.operatingModeService.getCharacteristic(Characteristic_1.Characteristic.HomeKitCameraActive).value
|
476 | : false;
|
477 | }
|
478 | |
479 |
|
480 |
|
481 | handleControllerRemoved() {
|
482 | this.handleFactoryReset();
|
483 | for (const management of this.streamManagements) {
|
484 | management.destroy();
|
485 | }
|
486 | this.streamManagements.splice(0, this.streamManagements.length);
|
487 | this.microphoneService = undefined;
|
488 | this.speakerService = undefined;
|
489 | this.recordingManagement?.destroy();
|
490 | this.recordingManagement = undefined;
|
491 | this.removeAllListeners();
|
492 | }
|
493 | |
494 |
|
495 |
|
496 | handleFactoryReset() {
|
497 | this.streamManagements.forEach(management => management.handleFactoryReset());
|
498 | this.recordingManagement?.handleFactoryReset();
|
499 | this.microphoneMuted = false;
|
500 | this.microphoneVolume = 100;
|
501 | this.speakerMuted = false;
|
502 | this.speakerVolume = 100;
|
503 | }
|
504 | |
505 |
|
506 |
|
507 | serialize() {
|
508 | const streamManagementStates = [];
|
509 | for (const management of this.streamManagements) {
|
510 | const serializedState = management.serialize();
|
511 | if (serializedState) {
|
512 | streamManagementStates.push(serializedState);
|
513 | }
|
514 | }
|
515 | return {
|
516 | streamManagements: streamManagementStates,
|
517 | recordingManagement: this.recordingManagement?.serialize(),
|
518 | };
|
519 | }
|
520 | |
521 |
|
522 |
|
523 | deserialize(serialized) {
|
524 | for (const streamManagementState of serialized.streamManagements) {
|
525 | const streamManagement = this.streamManagements[streamManagementState.id];
|
526 | if (streamManagement) {
|
527 | streamManagement.deserialize(streamManagementState);
|
528 | }
|
529 | }
|
530 | if (serialized.recordingManagement) {
|
531 | if (this.recordingManagement) {
|
532 | this.recordingManagement.deserialize(serialized.recordingManagement);
|
533 | }
|
534 | else {
|
535 |
|
536 | for (const streamManagement of this.streamManagements) {
|
537 | streamManagement.service.updateCharacteristic(Characteristic_1.Characteristic.Active, true);
|
538 | }
|
539 | this.stateChangeDelegate?.();
|
540 | }
|
541 | }
|
542 | }
|
543 | |
544 |
|
545 |
|
546 | setupStateChangeDelegate(delegate) {
|
547 | this.stateChangeDelegate = delegate;
|
548 | for (const streamManagement of this.streamManagements) {
|
549 | streamManagement.setupStateChangeDelegate(delegate);
|
550 | }
|
551 | this.recordingManagement?.setupStateChangeDelegate(delegate);
|
552 | }
|
553 | |
554 |
|
555 |
|
556 | handleSnapshotRequest(height, width, accessoryName, reason) {
|
557 |
|
558 | const streamingDisabled = this.streamManagements
|
559 | .map(management => !management.getService().getCharacteristic(Characteristic_1.Characteristic.Active).value)
|
560 | .reduce((previousValue, currentValue) => previousValue && currentValue);
|
561 | if (streamingDisabled) {
|
562 | debug("[%s] Rejecting snapshot as streaming is disabled.", accessoryName);
|
563 | return Promise.reject(-70412 );
|
564 | }
|
565 | if (this.recordingManagement) {
|
566 | const operatingModeService = this.recordingManagement.operatingModeService;
|
567 | if (!operatingModeService.getCharacteristic(Characteristic_1.Characteristic.HomeKitCameraActive).value) {
|
568 | debug("[%s] Rejecting snapshot as HomeKit camera is disabled.", accessoryName);
|
569 | return Promise.reject(-70412 );
|
570 | }
|
571 | const eventSnapshotsActive = operatingModeService
|
572 | .getCharacteristic(Characteristic_1.Characteristic.EventSnapshotsActive)
|
573 | .value;
|
574 | if (!eventSnapshotsActive) {
|
575 | if (reason == null) {
|
576 | debug("[%s] Rejecting snapshot as reason is required due to disabled event snapshots.", accessoryName);
|
577 | return Promise.reject(-70401 );
|
578 | }
|
579 | else if (reason === 1 ) {
|
580 | debug("[%s] Rejecting snapshot as even snapshots are disabled.", accessoryName);
|
581 | return Promise.reject(-70412 );
|
582 | }
|
583 | }
|
584 | const periodicSnapshotsActive = operatingModeService
|
585 | .getCharacteristic(Characteristic_1.Characteristic.PeriodicSnapshotsActive)
|
586 | .value;
|
587 | if (!periodicSnapshotsActive) {
|
588 | if (reason == null) {
|
589 | debug("[%s] Rejecting snapshot as reason is required due to disabled periodic snapshots.", accessoryName);
|
590 | return Promise.reject(-70401 );
|
591 | }
|
592 | else if (reason === 0 ) {
|
593 | debug("[%s] Rejecting snapshot as periodic snapshots are disabled.", accessoryName);
|
594 | return Promise.reject(-70412 );
|
595 | }
|
596 | }
|
597 | }
|
598 |
|
599 | return new Promise((resolve, reject) => {
|
600 |
|
601 | let timeout = setTimeout(() => {
|
602 | console.warn(`[${accessoryName}] The image snapshot handler for the given accessory is slow to respond! See https://homebridge.io/w/JtMGR for more info.`);
|
603 | timeout = setTimeout(() => {
|
604 | timeout = undefined;
|
605 | console.warn(`[${accessoryName}] The image snapshot handler for the given accessory didn't respond at all! See https://homebridge.io/w/JtMGR for more info.`);
|
606 | reject(-70408 );
|
607 | }, 17000);
|
608 | timeout.unref();
|
609 | }, 8000);
|
610 | timeout.unref();
|
611 | try {
|
612 | this.delegate.handleSnapshotRequest({
|
613 | height: height,
|
614 | width: width,
|
615 | reason: reason,
|
616 | }, (error, buffer) => {
|
617 | if (!timeout) {
|
618 | return;
|
619 | }
|
620 | else {
|
621 | clearTimeout(timeout);
|
622 | timeout = undefined;
|
623 | }
|
624 | if (error) {
|
625 | if (typeof error === "number") {
|
626 | reject(error);
|
627 | }
|
628 | else {
|
629 | debug("[%s] Error getting snapshot: %s", accessoryName, error.stack);
|
630 | reject(-70402 );
|
631 | }
|
632 | return;
|
633 | }
|
634 | if (!buffer || buffer.length === 0) {
|
635 | console.warn(`[${accessoryName}] Snapshot request handler provided empty image buffer!`);
|
636 | reject(-70402 );
|
637 | }
|
638 | else {
|
639 | resolve(buffer);
|
640 | }
|
641 | });
|
642 | }
|
643 | catch (error) {
|
644 | if (!timeout) {
|
645 | return;
|
646 | }
|
647 | else {
|
648 | clearTimeout(timeout);
|
649 | timeout = undefined;
|
650 | }
|
651 | console.warn(`[${accessoryName}] Unhandled error thrown inside snapshot request handler: ${error.stack}`);
|
652 | reject(error instanceof hapStatusError_1.HapStatusError ? error.hapStatus : -70402 );
|
653 | }
|
654 | });
|
655 | }
|
656 | }
|
657 | exports.CameraController = CameraController;
|
658 |
|
\ | No newline at end of file |