UNPKG

29 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Service = exports.ServiceEventTypes = void 0;
4const tslib_1 = require("tslib");
5const assert_1 = tslib_1.__importDefault(require("assert"));
6const debug_1 = tslib_1.__importDefault(require("debug"));
7const events_1 = require("events");
8const Characteristic_1 = require("./Characteristic");
9const uuid_1 = require("./util/uuid");
10const checkName_1 = require("./util/checkName");
11const debug = (0, debug_1.default)("HAP-NodeJS:Service");
12/**
13 * HAP spec allows a maximum of 100 characteristics per service!
14 */
15const MAX_CHARACTERISTICS = 100;
16/**
17 * @group Service
18 */
19var ServiceEventTypes;
20(function (ServiceEventTypes) {
21 ServiceEventTypes["CHARACTERISTIC_CHANGE"] = "characteristic-change";
22 ServiceEventTypes["SERVICE_CONFIGURATION_CHANGE"] = "service-configurationChange";
23 ServiceEventTypes["CHARACTERISTIC_WARNING"] = "characteristic-warning";
24})(ServiceEventTypes || (exports.ServiceEventTypes = ServiceEventTypes = {}));
25/**
26 * Service represents a set of grouped values necessary to provide a logical function. For instance, a
27 * "Door Lock Mechanism" service might contain two values, one for the "desired lock state" and one for the
28 * "current lock state". A particular Service is distinguished from others by its "type", which is a UUID.
29 * HomeKit provides a set of known Service UUIDs defined in HomeKit.ts along with a corresponding
30 * concrete subclass that you can instantiate directly to set up the necessary values. These natively-supported
31 * Services are expected to contain a particular set of Characteristics.
32 *
33 * Unlike Characteristics, where you cannot have two Characteristics with the same UUID in the same Service,
34 * you can actually have multiple Services with the same UUID in a single Accessory. For instance, imagine
35 * a Garage Door Opener with both a "security light" and a "backlight" for the display. Each light could be
36 * a "Lightbulb" Service with the same UUID. To account for this situation, we define an extra "subtype"
37 * property on Service, that can be a string or other string-convertible object that uniquely identifies the
38 * Service among its peers in an Accessory. For instance, you might have `service1.subtype = 'security_light'`
39 * for one and `service2.subtype = 'backlight'` for the other.
40 *
41 * You can also define custom Services by providing your own UUID for the type that you generate yourself.
42 * Custom Services can contain an arbitrary set of Characteristics, but Siri will likely not be able to
43 * work with these.
44 *
45 * @group Service
46 */
47// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
48class Service extends events_1.EventEmitter {
49 // Service MUST NOT have any other static variables
50 // Pattern below is for automatic detection of the section of defined services. Used by the generator
51 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-
52 /**
53 * @group Service Definitions
54 */
55 static AccessCode;
56 /**
57 * @group Service Definitions
58 */
59 static AccessControl;
60 /**
61 * @group Service Definitions
62 */
63 static AccessoryInformation;
64 /**
65 * @group Service Definitions
66 */
67 static AccessoryMetrics;
68 /**
69 * @group Service Definitions
70 */
71 static AccessoryRuntimeInformation;
72 /**
73 * @group Service Definitions
74 */
75 static AirPurifier;
76 /**
77 * @group Service Definitions
78 */
79 static AirQualitySensor;
80 /**
81 * @group Service Definitions
82 */
83 static AssetUpdate;
84 /**
85 * @group Service Definitions
86 */
87 static Assistant;
88 /**
89 * @group Service Definitions
90 */
91 static AudioStreamManagement;
92 /**
93 * @group Service Definitions
94 */
95 static Battery;
96 /**
97 * @group Service Definitions
98 */
99 static CameraOperatingMode;
100 /**
101 * @group Service Definitions
102 */
103 static CameraRecordingManagement;
104 /**
105 * @group Service Definitions
106 */
107 static CameraRTPStreamManagement;
108 /**
109 * @group Service Definitions
110 */
111 static CarbonDioxideSensor;
112 /**
113 * @group Service Definitions
114 */
115 static CarbonMonoxideSensor;
116 /**
117 * @group Service Definitions
118 */
119 static CloudRelay;
120 /**
121 * @group Service Definitions
122 */
123 static ContactSensor;
124 /**
125 * @group Service Definitions
126 */
127 static DataStreamTransportManagement;
128 /**
129 * @group Service Definitions
130 */
131 static Diagnostics;
132 /**
133 * @group Service Definitions
134 */
135 static Door;
136 /**
137 * @group Service Definitions
138 */
139 static Doorbell;
140 /**
141 * @group Service Definitions
142 */
143 static Fan;
144 /**
145 * @group Service Definitions
146 */
147 static Fanv2;
148 /**
149 * @group Service Definitions
150 */
151 static Faucet;
152 /**
153 * @group Service Definitions
154 */
155 static FilterMaintenance;
156 /**
157 * @group Service Definitions
158 */
159 static FirmwareUpdate;
160 /**
161 * @group Service Definitions
162 */
163 static GarageDoorOpener;
164 /**
165 * @group Service Definitions
166 */
167 static HeaterCooler;
168 /**
169 * @group Service Definitions
170 */
171 static HumidifierDehumidifier;
172 /**
173 * @group Service Definitions
174 */
175 static HumiditySensor;
176 /**
177 * @group Service Definitions
178 */
179 static InputSource;
180 /**
181 * @group Service Definitions
182 */
183 static IrrigationSystem;
184 /**
185 * @group Service Definitions
186 */
187 static LeakSensor;
188 /**
189 * @group Service Definitions
190 */
191 static Lightbulb;
192 /**
193 * @group Service Definitions
194 */
195 static LightSensor;
196 /**
197 * @group Service Definitions
198 */
199 static LockManagement;
200 /**
201 * @group Service Definitions
202 */
203 static LockMechanism;
204 /**
205 * @group Service Definitions
206 */
207 static Microphone;
208 /**
209 * @group Service Definitions
210 */
211 static MotionSensor;
212 /**
213 * @group Service Definitions
214 */
215 static NFCAccess;
216 /**
217 * @group Service Definitions
218 */
219 static OccupancySensor;
220 /**
221 * @group Service Definitions
222 */
223 static Outlet;
224 /**
225 * @group Service Definitions
226 */
227 static Pairing;
228 /**
229 * @group Service Definitions
230 */
231 static PowerManagement;
232 /**
233 * @group Service Definitions
234 */
235 static ProtocolInformation;
236 /**
237 * @group Service Definitions
238 */
239 static SecuritySystem;
240 /**
241 * @group Service Definitions
242 */
243 static ServiceLabel;
244 /**
245 * @group Service Definitions
246 */
247 static Siri;
248 /**
249 * @group Service Definitions
250 */
251 static SiriEndpoint;
252 /**
253 * @group Service Definitions
254 */
255 static Slats;
256 /**
257 * @group Service Definitions
258 */
259 static SmartSpeaker;
260 /**
261 * @group Service Definitions
262 */
263 static SmokeSensor;
264 /**
265 * @group Service Definitions
266 */
267 static Speaker;
268 /**
269 * @group Service Definitions
270 */
271 static StatefulProgrammableSwitch;
272 /**
273 * @group Service Definitions
274 */
275 static StatelessProgrammableSwitch;
276 /**
277 * @group Service Definitions
278 */
279 static Switch;
280 /**
281 * @group Service Definitions
282 */
283 static TapManagement;
284 /**
285 * @group Service Definitions
286 */
287 static TargetControl;
288 /**
289 * @group Service Definitions
290 */
291 static TargetControlManagement;
292 /**
293 * @group Service Definitions
294 */
295 static Television;
296 /**
297 * @group Service Definitions
298 */
299 static TelevisionSpeaker;
300 /**
301 * @group Service Definitions
302 */
303 static TemperatureSensor;
304 /**
305 * @group Service Definitions
306 */
307 static Thermostat;
308 /**
309 * @group Service Definitions
310 */
311 static ThreadTransport;
312 /**
313 * @group Service Definitions
314 */
315 static TransferTransportManagement;
316 /**
317 * @group Service Definitions
318 */
319 static Tunnel;
320 /**
321 * @group Service Definitions
322 */
323 static Valve;
324 /**
325 * @group Service Definitions
326 */
327 static WiFiRouter;
328 /**
329 * @group Service Definitions
330 */
331 static WiFiSatellite;
332 /**
333 * @group Service Definitions
334 */
335 static WiFiTransport;
336 /**
337 * @group Service Definitions
338 */
339 static Window;
340 /**
341 * @group Service Definitions
342 */
343 static WindowCovering;
344 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=
345 // NOTICE: when adding/changing properties, remember to possibly adjust the serialize/deserialize functions
346 displayName;
347 UUID;
348 subtype;
349 iid = null; // assigned later by our containing Accessory
350 name = null;
351 characteristics = [];
352 optionalCharacteristics = [];
353 /**
354 * @private
355 */
356 isHiddenService = false;
357 /**
358 * @private
359 */
360 isPrimaryService = false; // do not write to this directly
361 /**
362 * @private
363 */
364 linkedServices = [];
365 constructor(displayName = "", UUID, subtype) {
366 super();
367 (0, assert_1.default)(UUID, "Services must be created with a valid UUID.");
368 this.displayName = displayName;
369 this.UUID = UUID;
370 this.subtype = subtype;
371 // every service has an optional Characteristic.Name property - we'll set it to our displayName
372 // if one was given
373 // if you don't provide a display name, some HomeKit apps may choose to hide the device.
374 if (displayName) {
375 // create the characteristic if necessary
376 (0, checkName_1.checkName)(this.displayName, "Name", displayName);
377 const nameCharacteristic = this.getCharacteristic(Characteristic_1.Characteristic.Name) ||
378 this.addCharacteristic(Characteristic_1.Characteristic.Name);
379 nameCharacteristic.updateValue(displayName);
380 }
381 }
382 /**
383 * Returns an id which uniquely identifies a service on the associated accessory.
384 * The serviceId is a concatenation of the UUID for the service (defined by HAP) and the subtype (could be empty)
385 * which is programmatically defined by the programmer.
386 *
387 * @returns the serviceId
388 */
389 getServiceId() {
390 return this.UUID + (this.subtype || "");
391 }
392 // eslint-disable-next-line @typescript-eslint/no-explicit-any
393 addCharacteristic(input, ...constructorArgs) {
394 // characteristic might be a constructor like `Characteristic.Brightness` instead of an instance of Characteristic. Coerce if necessary.
395 const characteristic = typeof input === "function" ? new input(...constructorArgs) : input;
396 // check for UUID conflict
397 for (const existing of this.characteristics) {
398 if (existing.UUID === characteristic.UUID) {
399 if (characteristic.UUID === "00000052-0000-1000-8000-0026BB765291") {
400 //This is a special workaround for the Firmware Revision characteristic.
401 return existing;
402 }
403 throw new Error("Cannot add a Characteristic with the same UUID as another Characteristic in this Service: " + existing.UUID);
404 }
405 }
406 if (this.characteristics.length >= MAX_CHARACTERISTICS) {
407 throw new Error("Cannot add more than " + MAX_CHARACTERISTICS + " characteristics to a single service!");
408 }
409 this.setupCharacteristicEventHandlers(characteristic);
410 this.characteristics.push(characteristic);
411 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
412 return characteristic;
413 }
414 /**
415 * Sets this service as the new primary service.
416 * Any currently active primary service will be reset to be not primary.
417 * This will happen immediately, if the service was already added to an accessory, or later
418 * when the service gets added to an accessory.
419 *
420 * @param isPrimary - optional boolean (default true) if the service should be the primary service
421 */
422 setPrimaryService(isPrimary = true) {
423 this.isPrimaryService = isPrimary;
424 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
425 }
426 /**
427 * Marks the service as hidden
428 *
429 * @param isHidden - optional boolean (default true) if the service should be marked hidden
430 */
431 setHiddenService(isHidden = true) {
432 this.isHiddenService = isHidden;
433 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
434 }
435 /**
436 * Adds a new link to the specified service. The service MUST be already added to
437 * the SAME accessory.
438 *
439 * @param service - The service this service should link to
440 */
441 addLinkedService(service) {
442 //TODO: Add a check if the service is on the same accessory.
443 if (!this.linkedServices.includes(service)) {
444 this.linkedServices.push(service);
445 }
446 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
447 }
448 /**
449 * Removes a link to the specified service which was previously added with {@link addLinkedService}
450 *
451 * @param service - Previously linked service
452 */
453 removeLinkedService(service) {
454 //TODO: Add a check if the service is on the same accessory.
455 const index = this.linkedServices.indexOf(service);
456 if (index !== -1) {
457 this.linkedServices.splice(index, 1);
458 }
459 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
460 }
461 removeCharacteristic(characteristic) {
462 const index = this.characteristics.indexOf(characteristic);
463 if (index !== -1) {
464 this.characteristics.splice(index, 1);
465 characteristic.removeAllListeners();
466 this.emit("service-configurationChange" /* ServiceEventTypes.SERVICE_CONFIGURATION_CHANGE */);
467 }
468 }
469 getCharacteristic(name) {
470 // returns a characteristic object from the service
471 // If Service.prototype.getCharacteristic(Characteristic.Type) does not find the characteristic,
472 // but the type is in optionalCharacteristics, it adds the characteristic.type to the service and returns it.
473 for (const characteristic of this.characteristics) {
474 if (typeof name === "string" && characteristic.displayName === name) {
475 return characteristic;
476 }
477 else {
478 // @ts-expect-error ('UUID' does not exist on type 'never')
479 if (typeof name === "function" && ((characteristic instanceof name) || (name.UUID === characteristic.UUID))) {
480 return characteristic;
481 }
482 }
483 }
484 if (typeof name === "function") {
485 for (const characteristic of this.optionalCharacteristics) {
486 // @ts-expect-error ('UUID' does not exist on type 'never')
487 if ((characteristic instanceof name) || (name.UUID === characteristic.UUID)) {
488 return this.addCharacteristic(name);
489 }
490 }
491 const instance = this.addCharacteristic(name);
492 // Not found in optional Characteristics. Adding anyway, but warning about it if it isn't the Name.
493 if (name.UUID !== Characteristic_1.Characteristic.Name.UUID) {
494 this.emitCharacteristicWarningEvent(instance, "warn-message" /* CharacteristicWarningType.WARN_MESSAGE */, "Characteristic not in required or optional characteristic section for service " + this.constructor.name + ". Adding anyway.");
495 }
496 return instance;
497 }
498 }
499 testCharacteristic(name) {
500 // checks for the existence of a characteristic object in the service
501 for (const characteristic of this.characteristics) {
502 if (typeof name === "string" && characteristic.displayName === name) {
503 return true;
504 }
505 else {
506 // @ts-expect-error ('UUID' does not exist on type 'never')
507 if (typeof name === "function" && ((characteristic instanceof name) || (name.UUID === characteristic.UUID))) {
508 return true;
509 }
510 }
511 }
512 return false;
513 }
514 setCharacteristic(name, value) {
515 // @ts-expect-error: We know that both overloads exists individually. There is just no publicly exposed type for that!
516 this.getCharacteristic(name).setValue(value);
517 return this; // for chaining
518 }
519 updateCharacteristic(name, value) {
520 this.getCharacteristic(name).updateValue(value);
521 return this;
522 }
523 addOptionalCharacteristic(characteristic) {
524 // characteristic might be a constructor like `Characteristic.Brightness` instead of an instance
525 // of Characteristic. Coerce if necessary.
526 if (typeof characteristic === "function") {
527 characteristic = new characteristic();
528 }
529 this.optionalCharacteristics.push(characteristic);
530 }
531 // noinspection JSUnusedGlobalSymbols
532 /**
533 * This method was created to copy all characteristics from another service to this.
534 * It's only adopting is currently in homebridge to merge the AccessoryInformation service. So some things
535 * may be explicitly tailored towards this use case.
536 *
537 * It will not remove characteristics which are present currently but not added on the other characteristic.
538 * It will not replace the characteristic if the value is falsy (except of '0' or 'false')
539 * @param service
540 * @private used by homebridge
541 */
542 replaceCharacteristicsFromService(service) {
543 if (this.UUID !== service.UUID) {
544 throw new Error(`Incompatible services. Tried replacing characteristics of ${this.UUID} with characteristics from ${service.UUID}`);
545 }
546 const foreignCharacteristics = {}; // index foreign characteristics by UUID
547 service.characteristics.forEach(characteristic => foreignCharacteristics[characteristic.UUID] = characteristic);
548 this.characteristics.forEach(characteristic => {
549 const foreignCharacteristic = foreignCharacteristics[characteristic.UUID];
550 if (foreignCharacteristic) {
551 delete foreignCharacteristics[characteristic.UUID];
552 if (!foreignCharacteristic.value && foreignCharacteristic.value !== 0 && foreignCharacteristic.value !== false) {
553 return; // ignore falsy values except if it's the number zero or literally false
554 }
555 characteristic.replaceBy(foreignCharacteristic);
556 }
557 });
558 // add all additional characteristics which where not present already
559 Object.values(foreignCharacteristics).forEach(characteristic => this.addCharacteristic(characteristic));
560 }
561 /**
562 * @private
563 */
564 getCharacteristicByIID(iid) {
565 for (const characteristic of this.characteristics) {
566 if (characteristic.iid === iid) {
567 return characteristic;
568 }
569 }
570 }
571 /**
572 * @private
573 */
574 _assignIDs(identifierCache, accessoryName, baseIID = 0) {
575 // the Accessory Information service must have a (reserved by IdentifierCache) ID of 1
576 if (this.UUID === "0000003E-0000-1000-8000-0026BB765291") {
577 this.iid = 1;
578 }
579 else {
580 // assign our own ID based on our UUID
581 this.iid = baseIID + identifierCache.getIID(accessoryName, this.UUID, this.subtype);
582 }
583 // assign IIDs to our Characteristics
584 for (const characteristic of this.characteristics) {
585 characteristic._assignID(identifierCache, accessoryName, this.UUID, this.subtype);
586 }
587 }
588 /**
589 * Returns a JSON representation of this service suitable for delivering to HAP clients.
590 * @private used to generate response to /accessories query
591 */
592 toHAP(connection, contactGetHandlers = true) {
593 return new Promise(resolve => {
594 (0, assert_1.default)(this.iid, "iid cannot be undefined for service '" + this.displayName + "'");
595 (0, assert_1.default)(this.characteristics.length, "service '" + this.displayName + "' does not have any characteristics!");
596 const service = {
597 type: (0, uuid_1.toShortForm)(this.UUID),
598 iid: this.iid,
599 characteristics: [],
600 hidden: this.isHiddenService ? true : undefined,
601 primary: this.isPrimaryService ? true : undefined,
602 };
603 if (this.linkedServices.length) {
604 service.linked = [];
605 for (const linked of this.linkedServices) {
606 if (!linked.iid) {
607 // we got a linked service which is not added to the accessory
608 // as it doesn't "exists" we just ignore it.
609 // we have some (at least one) plugins on homebridge which link to the AccessoryInformation service.
610 // homebridge always creates its own AccessoryInformation service and ignores the user supplied one
611 // thus the link is automatically broken.
612 debug(`iid of linked service '${linked.displayName}' ${linked.UUID} is undefined on service '${this.displayName}'`);
613 continue;
614 }
615 service.linked.push(linked.iid);
616 }
617 }
618 const missingCharacteristics = new Set();
619 let timeout = setTimeout(() => {
620 for (const characteristic of missingCharacteristics) {
621 this.emitCharacteristicWarningEvent(characteristic, "slow-read" /* CharacteristicWarningType.SLOW_READ */, `The read handler for the characteristic '${characteristic.displayName}' was slow to respond!`);
622 }
623 timeout = setTimeout(() => {
624 timeout = undefined;
625 for (const characteristic of missingCharacteristics) {
626 this.emitCharacteristicWarningEvent(characteristic, "timeout-read" /* CharacteristicWarningType.TIMEOUT_READ */, "The read handler for the characteristic '" + characteristic?.displayName +
627 "' didn't respond at all!. Please check that you properly call the callback!");
628 service.characteristics.push(characteristic.internalHAPRepresentation()); // value is set to null
629 }
630 missingCharacteristics.clear();
631 resolve(service);
632 }, 6000);
633 }, 3000);
634 for (const characteristic of this.characteristics) {
635 missingCharacteristics.add(characteristic);
636 characteristic.toHAP(connection, contactGetHandlers).then(value => {
637 if (!timeout) {
638 return; // if timeout is undefined, response was already sent out
639 }
640 missingCharacteristics.delete(characteristic);
641 service.characteristics.push(value);
642 if (missingCharacteristics.size === 0) {
643 if (timeout) {
644 clearTimeout(timeout);
645 timeout = undefined;
646 }
647 resolve(service);
648 }
649 });
650 }
651 });
652 }
653 /**
654 * Returns a JSON representation of this service without characteristic values.
655 * @private used to generate the config hash
656 */
657 internalHAPRepresentation() {
658 (0, assert_1.default)(this.iid, "iid cannot be undefined for service '" + this.displayName + "'");
659 (0, assert_1.default)(this.characteristics.length, "service '" + this.displayName + "' does not have any characteristics!");
660 const service = {
661 type: (0, uuid_1.toShortForm)(this.UUID),
662 iid: this.iid,
663 characteristics: this.characteristics.map(characteristic => characteristic.internalHAPRepresentation()),
664 hidden: this.isHiddenService ? true : undefined,
665 primary: this.isPrimaryService ? true : undefined,
666 };
667 if (this.linkedServices.length) {
668 service.linked = [];
669 for (const linked of this.linkedServices) {
670 if (!linked.iid) {
671 // we got a linked service which is not added to the accessory
672 // as it doesn't "exists" we just ignore it.
673 // we have some (at least one) plugins on homebridge which link to the AccessoryInformation service.
674 // homebridge always creates its own AccessoryInformation service and ignores the user supplied one
675 // thus the link is automatically broken.
676 debug(`iid of linked service '${linked.displayName}' ${linked.UUID} is undefined on service '${this.displayName}'`);
677 continue;
678 }
679 service.linked.push(linked.iid);
680 }
681 }
682 return service;
683 }
684 /**
685 * @private
686 */
687 setupCharacteristicEventHandlers(characteristic) {
688 // listen for changes in characteristics and bubble them up
689 characteristic.on("change" /* CharacteristicEventTypes.CHANGE */, (change) => {
690 this.emit("characteristic-change" /* ServiceEventTypes.CHARACTERISTIC_CHANGE */, { ...change, characteristic: characteristic });
691 });
692 characteristic.on("characteristic-warning" /* CharacteristicEventTypes.CHARACTERISTIC_WARNING */, this.emitCharacteristicWarningEvent.bind(this, characteristic));
693 }
694 /**
695 * @private
696 */
697 emitCharacteristicWarningEvent(characteristic, type, message, stack) {
698 this.emit("characteristic-warning" /* ServiceEventTypes.CHARACTERISTIC_WARNING */, {
699 characteristic: characteristic,
700 type: type,
701 message: message,
702 originatorChain: [this.displayName, characteristic.displayName],
703 stack: stack,
704 });
705 }
706 /**
707 * @private
708 */
709 _sideloadCharacteristics(targetCharacteristics) {
710 for (const target of targetCharacteristics) {
711 this.setupCharacteristicEventHandlers(target);
712 }
713 this.characteristics = targetCharacteristics.slice();
714 }
715 /**
716 * @private
717 */
718 static serialize(service) {
719 let constructorName;
720 if (service.constructor.name !== "Service") {
721 constructorName = service.constructor.name;
722 }
723 return {
724 displayName: service.displayName,
725 UUID: service.UUID,
726 subtype: service.subtype,
727 constructorName: constructorName,
728 hiddenService: service.isHiddenService,
729 primaryService: service.isPrimaryService,
730 characteristics: service.characteristics.map(characteristic => Characteristic_1.Characteristic.serialize(characteristic)),
731 optionalCharacteristics: service.optionalCharacteristics.map(characteristic => Characteristic_1.Characteristic.serialize(characteristic)),
732 };
733 }
734 /**
735 * @private
736 */
737 static deserialize(json) {
738 let service;
739 if (json.constructorName && json.constructorName.charAt(0).toUpperCase() === json.constructorName.charAt(0)
740 && Service[json.constructorName]) { // MUST start with uppercase character and must exist on Service object
741 const constructor = Service[json.constructorName];
742 service = new constructor(json.displayName, json.subtype);
743 }
744 else {
745 service = new Service(json.displayName, json.UUID, json.subtype);
746 }
747 service.isHiddenService = !!json.hiddenService;
748 service.isPrimaryService = !!json.primaryService;
749 const characteristics = json.characteristics.map(serialized => Characteristic_1.Characteristic.deserialize(serialized));
750 service._sideloadCharacteristics(characteristics);
751 if (json.optionalCharacteristics) {
752 service.optionalCharacteristics = json.optionalCharacteristics.map(serialized => Characteristic_1.Characteristic.deserialize(serialized));
753 }
754 return service;
755 }
756}
757exports.Service = Service;
758// We have a cyclic dependency problem. Within this file we have the definitions of "./definitions" as
759// type imports only (in order to define the static properties). Setting those properties is done outside
760// this file, within the definition files. Therefore, we import it at the end of this file. Seems weird, but is important.
761require("./definitions/ServiceDefinitions");
762//# sourceMappingURL=Service.js.map
\No newline at end of file