UNPKG

87.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Characteristic = exports.CharacteristicEventTypes = exports.ChangeReason = exports.Access = exports.Perms = exports.Units = exports.Formats = 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 HAPServer_1 = require("./HAPServer");
9const clone_1 = require("./util/clone");
10const hapStatusError_1 = require("./util/hapStatusError");
11const once_1 = require("./util/once");
12const request_util_1 = require("./util/request-util");
13const uuid_1 = require("./util/uuid");
14const checkName_1 = require("./util/checkName");
15const debug = (0, debug_1.default)("HAP-NodeJS:Characteristic");
16/**
17 * @group Characteristic
18 */
19var Formats;
20(function (Formats) {
21 Formats["BOOL"] = "bool";
22 /**
23 * Signed 32-bit integer
24 */
25 Formats["INT"] = "int";
26 /**
27 * Signed 64-bit floating point
28 */
29 Formats["FLOAT"] = "float";
30 /**
31 * String encoded in utf8
32 */
33 Formats["STRING"] = "string";
34 /**
35 * Unsigned 8-bit integer.
36 */
37 Formats["UINT8"] = "uint8";
38 /**
39 * Unsigned 16-bit integer.
40 */
41 Formats["UINT16"] = "uint16";
42 /**
43 * Unsigned 32-bit integer.
44 */
45 Formats["UINT32"] = "uint32";
46 /**
47 * Unsigned 64-bit integer.
48 */
49 Formats["UINT64"] = "uint64";
50 /**
51 * Data is base64 encoded string.
52 */
53 Formats["DATA"] = "data";
54 /**
55 * Base64 encoded tlv8 string.
56 */
57 Formats["TLV8"] = "tlv8";
58})(Formats || (exports.Formats = Formats = {}));
59/**
60 * @group Characteristic
61 */
62var Units;
63(function (Units) {
64 /**
65 * Celsius is the only temperature unit in the HomeKit Accessory Protocol.
66 * Unit conversion is always done on the client side e.g. on the iPhone in the Home App depending on
67 * the configured unit on the device itself.
68 */
69 Units["CELSIUS"] = "celsius";
70 Units["PERCENTAGE"] = "percentage";
71 Units["ARC_DEGREE"] = "arcdegrees";
72 Units["LUX"] = "lux";
73 Units["SECONDS"] = "seconds";
74})(Units || (exports.Units = Units = {}));
75/**
76 * @group Characteristic
77 */
78var Perms;
79(function (Perms) {
80 // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
81 Perms["PAIRED_READ"] = "pr";
82 // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
83 Perms["PAIRED_WRITE"] = "pw";
84 Perms["NOTIFY"] = "ev";
85 // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
86 Perms["EVENTS"] = "ev";
87 Perms["ADDITIONAL_AUTHORIZATION"] = "aa";
88 Perms["TIMED_WRITE"] = "tw";
89 Perms["HIDDEN"] = "hd";
90 Perms["WRITE_RESPONSE"] = "wr";
91})(Perms || (exports.Perms = Perms = {}));
92/**
93 * Describes the abstract access to a {@link Characteristic}.
94 * It abstracts the more granular access described by {@link Perms}.
95 *
96 * It is used in {@link CharacteristicProps.adminOnlyAccess}.
97 *
98 * @group Characteristic
99 */
100var Access;
101(function (Access) {
102 Access[Access["READ"] = 0] = "READ";
103 Access[Access["WRITE"] = 1] = "WRITE";
104 Access[Access["NOTIFY"] = 2] = "NOTIFY";
105})(Access || (exports.Access = Access = {}));
106/**
107 * @group Characteristic
108 */
109var ChangeReason;
110(function (ChangeReason) {
111 /**
112 * Reason used when HomeKit writes a value or the API user calls {@link Characteristic.setValue}.
113 */
114 ChangeReason["WRITE"] = "write";
115 /**
116 * Reason used when the API user calls the method {@link Characteristic.updateValue}.
117 */
118 ChangeReason["UPDATE"] = "update";
119 /**
120 * Used when HomeKit reads a value or the API user calls the deprecated method `Characteristic.getValue`.
121 */
122 ChangeReason["READ"] = "read";
123 /**
124 * Used when call to {@link Characteristic.sendEventNotification} was made.
125 */
126 ChangeReason["EVENT"] = "event";
127})(ChangeReason || (exports.ChangeReason = ChangeReason = {}));
128/**
129 * @group Characteristic
130 */
131var CharacteristicEventTypes;
132(function (CharacteristicEventTypes) {
133 /**
134 * This event is thrown when a HomeKit controller wants to read the current value of the characteristic.
135 * The event handler should call the supplied callback as fast as possible.
136 *
137 * HAP-NodeJS will complain about slow running get handlers after 3 seconds and terminate the request after 10 seconds.
138 */
139 CharacteristicEventTypes["GET"] = "get";
140 /**
141 * This event is thrown when a HomeKit controller wants to write a new value to the characteristic.
142 * The event handler should call the supplied callback as fast as possible.
143 *
144 * HAP-NodeJS will complain about slow running set handlers after 3 seconds and terminate the request after 10 seconds.
145 */
146 CharacteristicEventTypes["SET"] = "set";
147 /**
148 * Emitted after a new value is set for the characteristic.
149 * The new value can be set via a request by a HomeKit controller or via an API call.
150 */
151 CharacteristicEventTypes["CHANGE"] = "change";
152 /**
153 * @private
154 */
155 CharacteristicEventTypes["SUBSCRIBE"] = "subscribe";
156 /**
157 * @private
158 */
159 CharacteristicEventTypes["UNSUBSCRIBE"] = "unsubscribe";
160 /**
161 * @private
162 */
163 CharacteristicEventTypes["CHARACTERISTIC_WARNING"] = "characteristic-warning";
164})(CharacteristicEventTypes || (exports.CharacteristicEventTypes = CharacteristicEventTypes = {}));
165/**
166 * @group Characteristic
167 */
168class ValidValuesIterable {
169 props;
170 constructor(props) {
171 (0, assert_1.default)((0, request_util_1.isNumericFormat)(props.format), "Cannot instantiate valid values iterable when format is not numeric. Found " + props.format);
172 this.props = props;
173 }
174 *[Symbol.iterator]() {
175 if (this.props.validValues) {
176 for (const value of this.props.validValues) {
177 yield value;
178 }
179 }
180 else {
181 let min = 0; // default is zero for all the uint types
182 let max;
183 let stepValue = 1;
184 if (this.props.validValueRanges) {
185 min = this.props.validValueRanges[0];
186 max = this.props.validValueRanges[1];
187 }
188 else if (this.props.minValue != null && this.props.maxValue != null) {
189 min = this.props.minValue;
190 max = this.props.maxValue;
191 if (this.props.minStep != null) {
192 stepValue = this.props.minStep;
193 }
194 }
195 else if ((0, request_util_1.isUnsignedNumericFormat)(this.props.format)) {
196 max = (0, request_util_1.numericUpperBound)(this.props.format);
197 }
198 else {
199 throw new Error("Could not find valid iterator strategy for props: " + JSON.stringify(this.props));
200 }
201 for (let i = min; i <= max; i += stepValue) {
202 yield i;
203 }
204 }
205 }
206}
207const numberPattern = /^-?\d+$/;
208function extractHAPStatusFromError(error) {
209 let errorValue = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
210 if (numberPattern.test(error.message)) {
211 const value = parseInt(error.message, 10);
212 if ((0, HAPServer_1.IsKnownHAPStatusError)(value)) {
213 errorValue = value;
214 }
215 }
216 return errorValue;
217}
218function maxWithUndefined(a, b) {
219 if (a == null) {
220 return b;
221 }
222 else if (b == null) {
223 return a;
224 }
225 else {
226 return Math.max(a, b);
227 }
228}
229function minWithUndefined(a, b) {
230 if (a == null) {
231 return b;
232 }
233 else if (b == null) {
234 return a;
235 }
236 else {
237 return Math.min(a, b);
238 }
239}
240/**
241 * Characteristic represents a particular typed variable that can be assigned to a Service. For instance, a
242 * "Hue" Characteristic might store a 'float' value of type 'arcdegrees'. You could add the Hue Characteristic
243 * to a {@link Service} in order to store that value. A particular Characteristic is distinguished from others by its
244 * UUID. HomeKit provides a set of known Characteristic UUIDs defined in HomeKit.ts along with a
245 * corresponding concrete subclass.
246 *
247 * You can also define custom Characteristics by providing your own UUID. Custom Characteristics can be added
248 * to any native or custom Services, but Siri will likely not be able to work with these.
249 *
250 * @group Characteristic
251 */
252// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
253class Characteristic extends events_1.EventEmitter {
254 // Pattern below is for automatic detection of the section of defined characteristics. Used by the generator
255 // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-
256 /**
257 * @group Characteristic Definitions
258 */
259 static AccessCodeControlPoint;
260 /**
261 * @group Characteristic Definitions
262 */
263 static AccessCodeSupportedConfiguration;
264 /**
265 * @group Characteristic Definitions
266 */
267 static AccessControlLevel;
268 /**
269 * @group Characteristic Definitions
270 */
271 static AccessoryFlags;
272 /**
273 * @group Characteristic Definitions
274 */
275 static AccessoryIdentifier;
276 /**
277 * @group Characteristic Definitions
278 */
279 static Active;
280 /**
281 * @group Characteristic Definitions
282 */
283 static ActiveIdentifier;
284 /**
285 * @group Characteristic Definitions
286 */
287 static ActivityInterval;
288 /**
289 * @group Characteristic Definitions
290 */
291 static AdministratorOnlyAccess;
292 /**
293 * @group Characteristic Definitions
294 */
295 static AirParticulateDensity;
296 /**
297 * @group Characteristic Definitions
298 */
299 static AirParticulateSize;
300 /**
301 * @group Characteristic Definitions
302 */
303 static AirPlayEnable;
304 /**
305 * @group Characteristic Definitions
306 */
307 static AirQuality;
308 /**
309 * @group Characteristic Definitions
310 */
311 static AppMatchingIdentifier;
312 /**
313 * @group Characteristic Definitions
314 */
315 static AssetUpdateReadiness;
316 /**
317 * @group Characteristic Definitions
318 */
319 static AudioFeedback;
320 /**
321 * @group Characteristic Definitions
322 */
323 static BatteryLevel;
324 /**
325 * @group Characteristic Definitions
326 */
327 static Brightness;
328 /**
329 * @group Characteristic Definitions
330 */
331 static ButtonEvent;
332 /**
333 * @group Characteristic Definitions
334 */
335 static CameraOperatingModeIndicator;
336 /**
337 * @group Characteristic Definitions
338 */
339 static CarbonDioxideDetected;
340 /**
341 * @group Characteristic Definitions
342 */
343 static CarbonDioxideLevel;
344 /**
345 * @group Characteristic Definitions
346 */
347 static CarbonDioxidePeakLevel;
348 /**
349 * @group Characteristic Definitions
350 */
351 static CarbonMonoxideDetected;
352 /**
353 * @group Characteristic Definitions
354 */
355 static CarbonMonoxideLevel;
356 /**
357 * @group Characteristic Definitions
358 */
359 static CarbonMonoxidePeakLevel;
360 /**
361 * @group Characteristic Definitions
362 */
363 static CCAEnergyDetectThreshold;
364 /**
365 * @group Characteristic Definitions
366 */
367 static CCASignalDetectThreshold;
368 /**
369 * @group Characteristic Definitions
370 */
371 static CharacteristicValueActiveTransitionCount;
372 /**
373 * @group Characteristic Definitions
374 */
375 static CharacteristicValueTransitionControl;
376 /**
377 * @group Characteristic Definitions
378 */
379 static ChargingState;
380 /**
381 * @group Characteristic Definitions
382 */
383 static ClosedCaptions;
384 /**
385 * @group Characteristic Definitions
386 */
387 static ColorTemperature;
388 /**
389 * @group Characteristic Definitions
390 */
391 static ConfigurationState;
392 /**
393 * @group Characteristic Definitions
394 */
395 static ConfiguredName;
396 /**
397 * @group Characteristic Definitions
398 */
399 static ContactSensorState;
400 /**
401 * @group Characteristic Definitions
402 */
403 static CoolingThresholdTemperature;
404 /**
405 * @group Characteristic Definitions
406 */
407 static CryptoHash;
408 /**
409 * @group Characteristic Definitions
410 */
411 static CurrentAirPurifierState;
412 /**
413 * @group Characteristic Definitions
414 */
415 static CurrentAmbientLightLevel;
416 /**
417 * @group Characteristic Definitions
418 */
419 static CurrentDoorState;
420 /**
421 * @group Characteristic Definitions
422 */
423 static CurrentFanState;
424 /**
425 * @group Characteristic Definitions
426 */
427 static CurrentHeaterCoolerState;
428 /**
429 * @group Characteristic Definitions
430 */
431 static CurrentHeatingCoolingState;
432 /**
433 * @group Characteristic Definitions
434 */
435 static CurrentHorizontalTiltAngle;
436 /**
437 * @group Characteristic Definitions
438 */
439 static CurrentHumidifierDehumidifierState;
440 /**
441 * @group Characteristic Definitions
442 */
443 static CurrentMediaState;
444 /**
445 * @group Characteristic Definitions
446 */
447 static CurrentPosition;
448 /**
449 * @group Characteristic Definitions
450 */
451 static CurrentRelativeHumidity;
452 /**
453 * @group Characteristic Definitions
454 */
455 static CurrentSlatState;
456 /**
457 * @group Characteristic Definitions
458 */
459 static CurrentTemperature;
460 /**
461 * @group Characteristic Definitions
462 */
463 static CurrentTiltAngle;
464 /**
465 * @group Characteristic Definitions
466 */
467 static CurrentTransport;
468 /**
469 * @group Characteristic Definitions
470 */
471 static CurrentVerticalTiltAngle;
472 /**
473 * @group Characteristic Definitions
474 */
475 static CurrentVisibilityState;
476 /**
477 * @group Characteristic Definitions
478 */
479 static DataStreamHAPTransport;
480 /**
481 * @group Characteristic Definitions
482 */
483 static DataStreamHAPTransportInterrupt;
484 /**
485 * @group Characteristic Definitions
486 */
487 static DiagonalFieldOfView;
488 /**
489 * @group Characteristic Definitions
490 */
491 static DigitalZoom;
492 /**
493 * @group Characteristic Definitions
494 */
495 static DisplayOrder;
496 /**
497 * @group Characteristic Definitions
498 */
499 static EventRetransmissionMaximum;
500 /**
501 * @group Characteristic Definitions
502 */
503 static EventSnapshotsActive;
504 /**
505 * @group Characteristic Definitions
506 */
507 static EventTransmissionCounters;
508 /**
509 * @group Characteristic Definitions
510 */
511 static FilterChangeIndication;
512 /**
513 * @group Characteristic Definitions
514 */
515 static FilterLifeLevel;
516 /**
517 * @group Characteristic Definitions
518 */
519 static FirmwareRevision;
520 /**
521 * @group Characteristic Definitions
522 */
523 static FirmwareUpdateReadiness;
524 /**
525 * @group Characteristic Definitions
526 */
527 static FirmwareUpdateStatus;
528 /**
529 * @group Characteristic Definitions
530 */
531 static HardwareFinish;
532 /**
533 * @group Characteristic Definitions
534 */
535 static HardwareRevision;
536 /**
537 * @group Characteristic Definitions
538 */
539 static HeartBeat;
540 /**
541 * @group Characteristic Definitions
542 */
543 static HeatingThresholdTemperature;
544 /**
545 * @group Characteristic Definitions
546 */
547 static HoldPosition;
548 /**
549 * @group Characteristic Definitions
550 */
551 static HomeKitCameraActive;
552 /**
553 * @group Characteristic Definitions
554 */
555 static Hue;
556 /**
557 * @group Characteristic Definitions
558 */
559 static Identifier;
560 /**
561 * @group Characteristic Definitions
562 */
563 static Identify;
564 /**
565 * @group Characteristic Definitions
566 */
567 static ImageMirroring;
568 /**
569 * @group Characteristic Definitions
570 */
571 static ImageRotation;
572 /**
573 * @group Characteristic Definitions
574 */
575 static InputDeviceType;
576 /**
577 * @group Characteristic Definitions
578 */
579 static InputSourceType;
580 /**
581 * @group Characteristic Definitions
582 */
583 static InUse;
584 /**
585 * @group Characteristic Definitions
586 */
587 static IsConfigured;
588 /**
589 * @group Characteristic Definitions
590 */
591 static LeakDetected;
592 /**
593 * @group Characteristic Definitions
594 */
595 static ListPairings;
596 /**
597 * @group Characteristic Definitions
598 */
599 static LockControlPoint;
600 /**
601 * @group Characteristic Definitions
602 */
603 static LockCurrentState;
604 /**
605 * @group Characteristic Definitions
606 */
607 static LockLastKnownAction;
608 /**
609 * @group Characteristic Definitions
610 */
611 static LockManagementAutoSecurityTimeout;
612 /**
613 * @group Characteristic Definitions
614 */
615 static LockPhysicalControls;
616 /**
617 * @group Characteristic Definitions
618 */
619 static LockTargetState;
620 /**
621 * @group Characteristic Definitions
622 */
623 static Logs;
624 /**
625 * @group Characteristic Definitions
626 */
627 static MACRetransmissionMaximum;
628 /**
629 * @group Characteristic Definitions
630 */
631 static MACTransmissionCounters;
632 /**
633 * @group Characteristic Definitions
634 */
635 static ManagedNetworkEnable;
636 /**
637 * @group Characteristic Definitions
638 */
639 static ManuallyDisabled;
640 /**
641 * @group Characteristic Definitions
642 */
643 static Manufacturer;
644 /**
645 * @group Characteristic Definitions
646 */
647 static MaximumTransmitPower;
648 /**
649 * @group Characteristic Definitions
650 */
651 static MetricsBufferFullState;
652 /**
653 * @group Characteristic Definitions
654 */
655 static Model;
656 /**
657 * @group Characteristic Definitions
658 */
659 static MotionDetected;
660 /**
661 * @group Characteristic Definitions
662 */
663 static MultifunctionButton;
664 /**
665 * @group Characteristic Definitions
666 */
667 static Mute;
668 /**
669 * @group Characteristic Definitions
670 */
671 static Name;
672 /**
673 * @group Characteristic Definitions
674 */
675 static NetworkAccessViolationControl;
676 /**
677 * @group Characteristic Definitions
678 */
679 static NetworkClientProfileControl;
680 /**
681 * @group Characteristic Definitions
682 */
683 static NetworkClientStatusControl;
684 /**
685 * @group Characteristic Definitions
686 */
687 static NFCAccessControlPoint;
688 /**
689 * @group Characteristic Definitions
690 */
691 static NFCAccessSupportedConfiguration;
692 /**
693 * @group Characteristic Definitions
694 */
695 static NightVision;
696 /**
697 * @group Characteristic Definitions
698 */
699 static NitrogenDioxideDensity;
700 /**
701 * @group Characteristic Definitions
702 */
703 static ObstructionDetected;
704 /**
705 * @group Characteristic Definitions
706 */
707 static OccupancyDetected;
708 /**
709 * @group Characteristic Definitions
710 */
711 static On;
712 /**
713 * @group Characteristic Definitions
714 */
715 static OperatingStateResponse;
716 /**
717 * @group Characteristic Definitions
718 */
719 static OpticalZoom;
720 /**
721 * @group Characteristic Definitions
722 */
723 static OutletInUse;
724 /**
725 * @group Characteristic Definitions
726 */
727 static OzoneDensity;
728 /**
729 * @group Characteristic Definitions
730 */
731 static PairingFeatures;
732 /**
733 * @group Characteristic Definitions
734 */
735 static PairSetup;
736 /**
737 * @group Characteristic Definitions
738 */
739 static PairVerify;
740 /**
741 * @group Characteristic Definitions
742 */
743 static PasswordSetting;
744 /**
745 * @group Characteristic Definitions
746 */
747 static PeriodicSnapshotsActive;
748 /**
749 * @group Characteristic Definitions
750 */
751 static PictureMode;
752 /**
753 * @group Characteristic Definitions
754 */
755 static Ping;
756 /**
757 * @group Characteristic Definitions
758 */
759 static PM10Density;
760 /**
761 * @group Characteristic Definitions
762 */
763 static PM2_5Density;
764 /**
765 * @group Characteristic Definitions
766 */
767 static PositionState;
768 /**
769 * @group Characteristic Definitions
770 */
771 static PowerModeSelection;
772 /**
773 * @group Characteristic Definitions
774 */
775 static ProductData;
776 /**
777 * @group Characteristic Definitions
778 */
779 static ProgrammableSwitchEvent;
780 /**
781 * @group Characteristic Definitions
782 */
783 static ProgrammableSwitchOutputState;
784 /**
785 * @group Characteristic Definitions
786 */
787 static ProgramMode;
788 /**
789 * @group Characteristic Definitions
790 */
791 static ReceivedSignalStrengthIndication;
792 /**
793 * @group Characteristic Definitions
794 */
795 static ReceiverSensitivity;
796 /**
797 * @group Characteristic Definitions
798 */
799 static RecordingAudioActive;
800 /**
801 * @group Characteristic Definitions
802 */
803 static RelativeHumidityDehumidifierThreshold;
804 /**
805 * @group Characteristic Definitions
806 */
807 static RelativeHumidityHumidifierThreshold;
808 /**
809 * @group Characteristic Definitions
810 */
811 static RelayControlPoint;
812 /**
813 * @group Characteristic Definitions
814 */
815 static RelayEnabled;
816 /**
817 * @group Characteristic Definitions
818 */
819 static RelayState;
820 /**
821 * @group Characteristic Definitions
822 */
823 static RemainingDuration;
824 /**
825 * @group Characteristic Definitions
826 */
827 static RemoteKey;
828 /**
829 * @group Characteristic Definitions
830 */
831 static ResetFilterIndication;
832 /**
833 * @group Characteristic Definitions
834 */
835 static RotationDirection;
836 /**
837 * @group Characteristic Definitions
838 */
839 static RotationSpeed;
840 /**
841 * @group Characteristic Definitions
842 */
843 static RouterStatus;
844 /**
845 * @group Characteristic Definitions
846 */
847 static Saturation;
848 /**
849 * @group Characteristic Definitions
850 */
851 static SecuritySystemAlarmType;
852 /**
853 * @group Characteristic Definitions
854 */
855 static SecuritySystemCurrentState;
856 /**
857 * @group Characteristic Definitions
858 */
859 static SecuritySystemTargetState;
860 /**
861 * @group Characteristic Definitions
862 */
863 static SelectedAudioStreamConfiguration;
864 /**
865 * @group Characteristic Definitions
866 */
867 static SelectedCameraRecordingConfiguration;
868 /**
869 * @group Characteristic Definitions
870 */
871 static SelectedDiagnosticsModes;
872 /**
873 * @group Characteristic Definitions
874 */
875 static SelectedRTPStreamConfiguration;
876 /**
877 * @group Characteristic Definitions
878 */
879 static SelectedSleepConfiguration;
880 /**
881 * @group Characteristic Definitions
882 */
883 static SerialNumber;
884 /**
885 * @group Characteristic Definitions
886 */
887 static ServiceLabelIndex;
888 /**
889 * @group Characteristic Definitions
890 */
891 static ServiceLabelNamespace;
892 /**
893 * @group Characteristic Definitions
894 */
895 static SetDuration;
896 /**
897 * @group Characteristic Definitions
898 */
899 static SetupDataStreamTransport;
900 /**
901 * @group Characteristic Definitions
902 */
903 static SetupEndpoints;
904 /**
905 * @group Characteristic Definitions
906 */
907 static SetupTransferTransport;
908 /**
909 * @group Characteristic Definitions
910 */
911 static SignalToNoiseRatio;
912 /**
913 * @group Characteristic Definitions
914 */
915 static SiriEnable;
916 /**
917 * @group Characteristic Definitions
918 */
919 static SiriEndpointSessionStatus;
920 /**
921 * @group Characteristic Definitions
922 */
923 static SiriEngineVersion;
924 /**
925 * @group Characteristic Definitions
926 */
927 static SiriInputType;
928 /**
929 * @group Characteristic Definitions
930 */
931 static SiriLightOnUse;
932 /**
933 * @group Characteristic Definitions
934 */
935 static SiriListening;
936 /**
937 * @group Characteristic Definitions
938 */
939 static SiriTouchToUse;
940 /**
941 * @group Characteristic Definitions
942 */
943 static SlatType;
944 /**
945 * @group Characteristic Definitions
946 */
947 static SleepDiscoveryMode;
948 /**
949 * @group Characteristic Definitions
950 */
951 static SleepInterval;
952 /**
953 * @group Characteristic Definitions
954 */
955 static SmokeDetected;
956 /**
957 * @group Characteristic Definitions
958 */
959 static SoftwareRevision;
960 /**
961 * @group Characteristic Definitions
962 */
963 static StagedFirmwareVersion;
964 /**
965 * @group Characteristic Definitions
966 */
967 static StatusActive;
968 /**
969 * @group Characteristic Definitions
970 */
971 static StatusFault;
972 /**
973 * @group Characteristic Definitions
974 */
975 static StatusJammed;
976 /**
977 * @group Characteristic Definitions
978 */
979 static StatusLowBattery;
980 /**
981 * @group Characteristic Definitions
982 */
983 static StatusTampered;
984 /**
985 * @group Characteristic Definitions
986 */
987 static StreamingStatus;
988 /**
989 * @group Characteristic Definitions
990 */
991 static SulphurDioxideDensity;
992 /**
993 * @group Characteristic Definitions
994 */
995 static SupportedAssetTypes;
996 /**
997 * @group Characteristic Definitions
998 */
999 static SupportedAudioRecordingConfiguration;
1000 /**
1001 * @group Characteristic Definitions
1002 */
1003 static SupportedAudioStreamConfiguration;
1004 /**
1005 * @group Characteristic Definitions
1006 */
1007 static SupportedCameraRecordingConfiguration;
1008 /**
1009 * @group Characteristic Definitions
1010 */
1011 static SupportedCharacteristicValueTransitionConfiguration;
1012 /**
1013 * @group Characteristic Definitions
1014 */
1015 static SupportedDataStreamTransportConfiguration;
1016 /**
1017 * @group Characteristic Definitions
1018 */
1019 static SupportedDiagnosticsModes;
1020 /**
1021 * @group Characteristic Definitions
1022 */
1023 static SupportedDiagnosticsSnapshot;
1024 /**
1025 * @group Characteristic Definitions
1026 */
1027 static SupportedFirmwareUpdateConfiguration;
1028 /**
1029 * @group Characteristic Definitions
1030 */
1031 static SupportedMetrics;
1032 /**
1033 * @group Characteristic Definitions
1034 */
1035 static SupportedRouterConfiguration;
1036 /**
1037 * @group Characteristic Definitions
1038 */
1039 static SupportedRTPConfiguration;
1040 /**
1041 * @group Characteristic Definitions
1042 */
1043 static SupportedSleepConfiguration;
1044 /**
1045 * @group Characteristic Definitions
1046 */
1047 static SupportedTransferTransportConfiguration;
1048 /**
1049 * @group Characteristic Definitions
1050 */
1051 static SupportedVideoRecordingConfiguration;
1052 /**
1053 * @group Characteristic Definitions
1054 */
1055 static SupportedVideoStreamConfiguration;
1056 /**
1057 * @group Characteristic Definitions
1058 */
1059 static SwingMode;
1060 /**
1061 * @group Characteristic Definitions
1062 */
1063 static TapType;
1064 /**
1065 * @group Characteristic Definitions
1066 */
1067 static TargetAirPurifierState;
1068 /**
1069 * @group Characteristic Definitions
1070 */
1071 static TargetControlList;
1072 /**
1073 * @group Characteristic Definitions
1074 */
1075 static TargetControlSupportedConfiguration;
1076 /**
1077 * @group Characteristic Definitions
1078 */
1079 static TargetDoorState;
1080 /**
1081 * @group Characteristic Definitions
1082 */
1083 static TargetFanState;
1084 /**
1085 * @group Characteristic Definitions
1086 */
1087 static TargetHeaterCoolerState;
1088 /**
1089 * @group Characteristic Definitions
1090 */
1091 static TargetHeatingCoolingState;
1092 /**
1093 * @group Characteristic Definitions
1094 */
1095 static TargetHorizontalTiltAngle;
1096 /**
1097 * @group Characteristic Definitions
1098 */
1099 static TargetHumidifierDehumidifierState;
1100 /**
1101 * @group Characteristic Definitions
1102 */
1103 static TargetMediaState;
1104 /**
1105 * @group Characteristic Definitions
1106 */
1107 static TargetPosition;
1108 /**
1109 * @group Characteristic Definitions
1110 */
1111 static TargetRelativeHumidity;
1112 /**
1113 * @group Characteristic Definitions
1114 */
1115 static TargetTemperature;
1116 /**
1117 * @group Characteristic Definitions
1118 */
1119 static TargetTiltAngle;
1120 /**
1121 * @group Characteristic Definitions
1122 */
1123 static TargetVerticalTiltAngle;
1124 /**
1125 * @group Characteristic Definitions
1126 */
1127 static TargetVisibilityState;
1128 /**
1129 * @group Characteristic Definitions
1130 */
1131 static TemperatureDisplayUnits;
1132 /**
1133 * @group Characteristic Definitions
1134 */
1135 static ThirdPartyCameraActive;
1136 /**
1137 * @group Characteristic Definitions
1138 */
1139 static ThreadControlPoint;
1140 /**
1141 * @group Characteristic Definitions
1142 */
1143 static ThreadNodeCapabilities;
1144 /**
1145 * @group Characteristic Definitions
1146 */
1147 static ThreadOpenThreadVersion;
1148 /**
1149 * @group Characteristic Definitions
1150 */
1151 static ThreadStatus;
1152 /**
1153 * @group Characteristic Definitions
1154 */
1155 static Token;
1156 /**
1157 * @group Characteristic Definitions
1158 */
1159 static TransmitPower;
1160 /**
1161 * @group Characteristic Definitions
1162 */
1163 static TunnelConnectionTimeout;
1164 /**
1165 * @group Characteristic Definitions
1166 */
1167 static TunneledAccessoryAdvertising;
1168 /**
1169 * @group Characteristic Definitions
1170 */
1171 static TunneledAccessoryConnected;
1172 /**
1173 * @group Characteristic Definitions
1174 */
1175 static TunneledAccessoryStateNumber;
1176 /**
1177 * @group Characteristic Definitions
1178 */
1179 static ValveType;
1180 /**
1181 * @group Characteristic Definitions
1182 */
1183 static Version;
1184 /**
1185 * @group Characteristic Definitions
1186 */
1187 static VideoAnalysisActive;
1188 /**
1189 * @group Characteristic Definitions
1190 */
1191 static VOCDensity;
1192 /**
1193 * @group Characteristic Definitions
1194 */
1195 static Volume;
1196 /**
1197 * @group Characteristic Definitions
1198 */
1199 static VolumeControlType;
1200 /**
1201 * @group Characteristic Definitions
1202 */
1203 static VolumeSelector;
1204 /**
1205 * @group Characteristic Definitions
1206 */
1207 static WakeConfiguration;
1208 /**
1209 * @group Characteristic Definitions
1210 */
1211 static WANConfigurationList;
1212 /**
1213 * @group Characteristic Definitions
1214 */
1215 static WANStatusList;
1216 /**
1217 * @group Characteristic Definitions
1218 */
1219 static WaterLevel;
1220 /**
1221 * @group Characteristic Definitions
1222 */
1223 static WiFiCapabilities;
1224 /**
1225 * @group Characteristic Definitions
1226 */
1227 static WiFiConfigurationControl;
1228 /**
1229 * @group Characteristic Definitions
1230 */
1231 static WiFiSatelliteStatus;
1232 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1233 // NOTICE: when adding/changing properties, remember to possibly adjust the serialize/deserialize functions
1234 displayName;
1235 UUID;
1236 iid = null;
1237 value = null;
1238 /**
1239 * @private
1240 */
1241 statusCode = 0 /* HAPStatus.SUCCESS */;
1242 props;
1243 /**
1244 * The {@link Characteristic.onGet} handler
1245 */
1246 getHandler;
1247 /**
1248 * The {@link Characteristic.onSet} handler
1249 */
1250 setHandler;
1251 subscriptions = 0;
1252 /**
1253 * @private
1254 */
1255 additionalAuthorizationHandler;
1256 constructor(displayName, UUID, props) {
1257 super();
1258 this.displayName = displayName;
1259 this.UUID = UUID;
1260 this.props = {
1261 format: "int" /* Formats.INT */,
1262 perms: ["ev" /* Perms.NOTIFY */],
1263 };
1264 this.setProps(props || {}); // ensure sanity checks are called
1265 }
1266 /**
1267 * Accepts a function that will be called to retrieve the current value of a Characteristic.
1268 * The function must return a valid Characteristic value for the Characteristic type.
1269 * May optionally return a promise.
1270 *
1271 * @example
1272 * ```ts
1273 * Characteristic.onGet(async () => {
1274 * return true;
1275 * });
1276 * ```
1277 * @param handler
1278 */
1279 onGet(handler) {
1280 if (typeof handler !== "function") {
1281 this.characteristicWarning(".onGet handler must be a function");
1282 return this;
1283 }
1284 this.getHandler = handler;
1285 return this;
1286 }
1287 /**
1288 * Removes the {@link CharacteristicGetHandler} handler which was configured using {@link onGet}.
1289 */
1290 removeOnGet() {
1291 this.getHandler = undefined;
1292 return this;
1293 }
1294 /**
1295 * Accepts a function that will be called when setting the value of a Characteristic.
1296 * If the characteristic supports {@link Perms.WRITE_RESPONSE} and the request requests a write-response value,
1297 * the returned value will be used.
1298 * May optionally return a promise.
1299 *
1300 * @example
1301 * ```ts
1302 * Characteristic.onSet(async (value: CharacteristicValue) => {
1303 * console.log(value);
1304 * });
1305 * ```
1306 * @param handler
1307 */
1308 onSet(handler) {
1309 if (typeof handler !== "function") {
1310 this.characteristicWarning(".onSet handler must be a function");
1311 return this;
1312 }
1313 this.setHandler = handler;
1314 return this;
1315 }
1316 /**
1317 * Removes the {@link CharacteristicSetHandler} which was configured using {@link onSet}.
1318 */
1319 removeOnSet() {
1320 this.setHandler = undefined;
1321 return this;
1322 }
1323 /**
1324 * Updates the properties of this characteristic.
1325 * Properties passed via the parameter will be set. Any parameter set to null will be deleted.
1326 * See {@link CharacteristicProps}.
1327 *
1328 * @param props - Partial properties object with the desired updates.
1329 */
1330 setProps(props) {
1331 (0, assert_1.default)(props, "props cannot be undefined when setting props");
1332 // TODO calling setProps after publish doesn't lead to a increment in the current configuration number
1333 let formatDidChange = false;
1334 // for every value "null" can be used to reset props, except for required props
1335 if (props.format) {
1336 formatDidChange = this.props.format !== props.format;
1337 this.props.format = props.format;
1338 }
1339 if (props.perms) {
1340 (0, assert_1.default)(props.perms.length > 0, "characteristic prop perms cannot be empty array");
1341 this.props.perms = props.perms;
1342 }
1343 if (props.unit !== undefined) {
1344 this.props.unit = props.unit != null ? props.unit : undefined;
1345 }
1346 if (props.description !== undefined) {
1347 this.props.description = props.description != null ? props.description : undefined;
1348 }
1349 // check minValue is valid for the format type
1350 if (props.minValue !== undefined) {
1351 if (props.minValue === null) {
1352 props.minValue = undefined;
1353 }
1354 else if (!(0, request_util_1.isNumericFormat)(this.props.format)) {
1355 this.characteristicWarning("Characteristic Property 'minValue' can only be set for characteristics with numeric format, but not for " + this.props.format, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1356 props.minValue = undefined;
1357 }
1358 else if (typeof props.minValue !== "number" || !Number.isFinite(props.minValue)) {
1359 this.characteristicWarning(`Characteristic Property 'minValue' must be a finite number, received "${props.minValue}" (${typeof props.minValue})`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1360 props.minValue = undefined;
1361 }
1362 else {
1363 if (props.minValue < (0, request_util_1.numericLowerBound)(this.props.format)) {
1364 this.characteristicWarning("Characteristic Property 'minValue' was set to " + props.minValue + ", but for numeric format " +
1365 this.props.format + " minimum possible is " + (0, request_util_1.numericLowerBound)(this.props.format), "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1366 props.minValue = (0, request_util_1.numericLowerBound)(this.props.format);
1367 }
1368 else if (props.minValue > (0, request_util_1.numericUpperBound)(this.props.format)) {
1369 this.characteristicWarning("Characteristic Property 'minValue' was set to " + props.minValue + ", but for numeric format " +
1370 this.props.format + " maximum possible is " + (0, request_util_1.numericUpperBound)(this.props.format), "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1371 props.minValue = (0, request_util_1.numericLowerBound)(this.props.format);
1372 }
1373 }
1374 this.props.minValue = props.minValue;
1375 }
1376 // check maxValue is valid for the format type
1377 if (props.maxValue !== undefined) {
1378 if (props.maxValue === null) {
1379 props.maxValue = undefined;
1380 }
1381 else if (!(0, request_util_1.isNumericFormat)(this.props.format)) {
1382 this.characteristicWarning("Characteristic Property 'maxValue' can only be set for characteristics with numeric format, but not for " + this.props.format, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1383 props.maxValue = undefined;
1384 }
1385 else if (typeof props.maxValue !== "number" || !Number.isFinite(props.maxValue)) {
1386 this.characteristicWarning(`Characteristic Property 'maxValue' must be a finite number, received "${props.maxValue}" (${typeof props.maxValue})`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1387 props.maxValue = undefined;
1388 }
1389 else {
1390 if (props.maxValue > (0, request_util_1.numericUpperBound)(this.props.format)) {
1391 this.characteristicWarning("Characteristic Property 'maxValue' was set to " + props.maxValue + ", but for numeric format " +
1392 this.props.format + " maximum possible is " + (0, request_util_1.numericUpperBound)(this.props.format), "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1393 props.maxValue = (0, request_util_1.numericUpperBound)(this.props.format);
1394 }
1395 else if (props.maxValue < (0, request_util_1.numericLowerBound)(this.props.format)) {
1396 this.characteristicWarning("Characteristic Property 'maxValue' was set to " + props.maxValue + ", but for numeric format " +
1397 this.props.format + " minimum possible is " + (0, request_util_1.numericLowerBound)(this.props.format), "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1398 props.maxValue = (0, request_util_1.numericUpperBound)(this.props.format);
1399 }
1400 }
1401 this.props.maxValue = props.maxValue;
1402 }
1403 if (props.minStep !== undefined) {
1404 if (props.minStep === null) {
1405 this.props.minStep = undefined;
1406 }
1407 else if (!(0, request_util_1.isNumericFormat)(this.props.format)) {
1408 this.characteristicWarning("Characteristic Property `minStep` can only be set for characteristics with numeric format, but not for " + this.props.format, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1409 }
1410 else {
1411 if (props.minStep < 1 && (0, request_util_1.isIntegerNumericFormat)(this.props.format)) {
1412 this.characteristicWarning("Characteristic Property `minStep` was set to a value lower than 1, " +
1413 "this will have no effect on format `" + this.props.format);
1414 }
1415 this.props.minStep = props.minStep;
1416 }
1417 }
1418 if (props.maxLen !== undefined) {
1419 if (props.maxLen === null) {
1420 this.props.maxLen = undefined;
1421 }
1422 else if (this.props.format !== "string" /* Formats.STRING */) {
1423 this.characteristicWarning("Characteristic Property `maxLen` can only be set for characteristics with format `STRING`, but not for " + this.props.format, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1424 }
1425 else {
1426 if (props.maxLen > 256) {
1427 this.characteristicWarning("Characteristic Property string `maxLen` cannot be bigger than 256");
1428 props.maxLen = 256;
1429 }
1430 this.props.maxLen = props.maxLen;
1431 }
1432 }
1433 if (props.maxDataLen !== undefined) {
1434 if (props.maxDataLen === null) {
1435 this.props.maxDataLen = undefined;
1436 }
1437 else if (this.props.format !== "data" /* Formats.DATA */) {
1438 this.characteristicWarning("Characteristic Property `maxDataLen` can only be set for characteristics with format `DATA`, but not for " + this.props.format, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
1439 }
1440 else {
1441 this.props.maxDataLen = props.maxDataLen;
1442 }
1443 }
1444 if (props.validValues !== undefined) {
1445 if (props.validValues === null) {
1446 this.props.validValues = undefined;
1447 }
1448 else if (!(0, request_util_1.isNumericFormat)(this.props.format)) {
1449 this.characteristicWarning("Characteristic Property `validValues` was supplied for non numeric format " + this.props.format);
1450 }
1451 else {
1452 (0, assert_1.default)(props.validValues.length, "characteristic prop validValues cannot be empty array");
1453 this.props.validValues = props.validValues;
1454 }
1455 }
1456 if (props.validValueRanges !== undefined) {
1457 if (props.validValueRanges === null) {
1458 this.props.validValueRanges = undefined;
1459 }
1460 else if (!(0, request_util_1.isNumericFormat)(this.props.format)) {
1461 this.characteristicWarning("Characteristic Property `validValueRanges` was supplied for non numeric format " + this.props.format);
1462 }
1463 else {
1464 (0, assert_1.default)(props.validValueRanges.length === 2, "characteristic prop validValueRanges must have a length of 2");
1465 this.props.validValueRanges = props.validValueRanges;
1466 }
1467 }
1468 if (props.adminOnlyAccess !== undefined) {
1469 this.props.adminOnlyAccess = props.adminOnlyAccess != null ? props.adminOnlyAccess : undefined;
1470 }
1471 if (this.props.minValue != null && this.props.maxValue != null) { // the eqeq instead of eqeqeq is important here
1472 if (this.props.minValue > this.props.maxValue) { // see https://github.com/homebridge/HAP-NodeJS/issues/690
1473 this.props.minValue = undefined;
1474 this.props.maxValue = undefined;
1475 throw new Error("Error setting CharacteristicsProps for '" + this.displayName + "': 'minValue' cannot be greater or equal the 'maxValue'!");
1476 }
1477 }
1478 if (((0, request_util_1.isNumericFormat)(this.props.format) || this.props.format === "string" /* Formats.STRING */)
1479 && this.value != null
1480 && !formatDidChange
1481 && this.statusCode === 0 /* HAPStatus.SUCCESS */
1482 && this.UUID !== Characteristic.ProgrammableSwitchEvent.UUID) {
1483 // explaining the if statement above:
1484 // - We only do a check for numeric and string formats as they are the only ones affected by characteristic property restrictions.
1485 // - There must be a value to begin with. Otherwise, it should just stay not having a value at all (anything else is guess work).
1486 // - If the format is changed through `setProps` we rely on the user to supply a valid value after the `setProps` call!
1487 // - If the characteristic is marked as erroneous the value is not considered valid anyway, and we must not remove the `statusCode`.
1488 // - Special case for `ProgrammableSwitchEvent` where every change in value is considered an event which would result in ghost button presses
1489 // validateUserInput when called from setProps is intended to clamp value withing allowed range. It is why warnings should not be displayed.
1490 const correctedValue = this.validateUserInput(this.value, "debug-message" /* CharacteristicWarningType.DEBUG_MESSAGE */);
1491 if (correctedValue !== this.value) {
1492 // we don't want to emit a CHANGE event if the value didn't change at all!
1493 this.updateValue(correctedValue);
1494 }
1495 }
1496 return this;
1497 }
1498 /**
1499 * This method can be used to gain an Iterator to loop over all valid values defined for this characteristic.
1500 *
1501 * The range of valid values can be defined using three different ways via the {@link CharacteristicProps} object
1502 * (set via the {@link setProps} method):
1503 * * First method is to specifically list every valid value inside {@link CharacteristicProps.validValues}
1504 * * Second you can specify a range via {@link CharacteristicProps.minValue} and {@link CharacteristicProps.maxValue} (with optionally defining
1505 * {@link CharacteristicProps.minStep})
1506 * * And lastly you can specify a range via {@link CharacteristicProps.validValueRanges}
1507 * * Implicitly a valid value range is predefined for characteristics with Format {@link Formats.UINT8}, {@link Formats.UINT16},
1508 * {@link Formats.UINT32} and {@link Formats.UINT64}: starting by zero to their respective maximum number
1509 *
1510 * The method will automatically detect which type of valid values definition is used and provide
1511 * the correct Iterator for that case.
1512 *
1513 * Note: This method is (obviously) only valid for numeric characteristics.
1514 *
1515 * @example
1516 * ```ts
1517 * // use the iterator to loop over every valid value...
1518 * for (const value of characteristic.validValuesIterator()) {
1519 * // Insert logic to run for every
1520 * }
1521 *
1522 * // ... or collect them in an array for storage or manipulation
1523 * const validValues = Array.from(characteristic.validValuesIterator());
1524 * ```
1525 */
1526 validValuesIterator() {
1527 return new ValidValuesIterable(this.props);
1528 }
1529 // noinspection JSUnusedGlobalSymbols
1530 /**
1531 * This method can be used to set up additional authorization for a characteristic.
1532 * For one, it adds the {@link Perms.ADDITIONAL_AUTHORIZATION} permission to the characteristic
1533 * (if it wasn't already) to signal support for additional authorization to HomeKit.
1534 * Additionally, an {@link AdditionalAuthorizationHandler} is set up which is called
1535 * before a write request is performed.
1536 *
1537 * Additional Authorization Data can be added to SET request via a custom iOS App.
1538 * Before hap-nodejs executes a write request it will call the {@link AdditionalAuthorizationHandler}
1539 * with 'authData' supplied in the write request. The 'authData' is a base64 encoded string
1540 * (or undefined if no authData was supplied).
1541 * The {@link AdditionalAuthorizationHandler} must then return true or false to indicate if the write request
1542 * is authorized and should be accepted.
1543 *
1544 * @param handler - Handler called to check additional authorization data.
1545 */
1546 setupAdditionalAuthorization(handler) {
1547 if (!this.props.perms.includes("aa" /* Perms.ADDITIONAL_AUTHORIZATION */)) {
1548 this.props.perms.push("aa" /* Perms.ADDITIONAL_AUTHORIZATION */);
1549 }
1550 this.additionalAuthorizationHandler = handler;
1551 }
1552 setValue(value, callback, context) {
1553 if (value instanceof Error) {
1554 this.statusCode = value instanceof hapStatusError_1.HapStatusError ? value.hapStatus : extractHAPStatusFromError(value);
1555 if (callback) {
1556 callback();
1557 }
1558 return this;
1559 }
1560 if (callback && !context && typeof callback !== "function") {
1561 context = callback;
1562 callback = undefined;
1563 }
1564 try {
1565 value = this.validateUserInput(value);
1566 }
1567 catch (error) {
1568 this.characteristicWarning(error?.message + "", "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1569 if (callback) {
1570 callback(error);
1571 }
1572 return this;
1573 }
1574 this.handleSetRequest(value, undefined, context).then(value => {
1575 if (callback) {
1576 if (value) { // possible write response
1577 callback(null, value);
1578 }
1579 else {
1580 callback(null);
1581 }
1582 }
1583 }, reason => {
1584 if (callback) {
1585 callback(reason);
1586 }
1587 });
1588 return this;
1589 }
1590 updateValue(value, callback, context) {
1591 if (value instanceof Error) {
1592 this.statusCode = value instanceof hapStatusError_1.HapStatusError ? value.hapStatus : extractHAPStatusFromError(value);
1593 if (callback) {
1594 callback();
1595 }
1596 return this;
1597 }
1598 if (callback && !context && typeof callback !== "function") {
1599 context = callback;
1600 callback = undefined;
1601 }
1602 try {
1603 value = this.validateUserInput(value);
1604 }
1605 catch (error) {
1606 this.characteristicWarning(error?.message + "", "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1607 if (callback) {
1608 callback();
1609 }
1610 return this;
1611 }
1612 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1613 const oldValue = this.value;
1614 this.value = value;
1615 if (callback) {
1616 callback();
1617 }
1618 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: undefined, oldValue: oldValue, newValue: value, reason: "update" /* ChangeReason.UPDATE */, context: context });
1619 return this; // for chaining
1620 }
1621 /**
1622 * This method acts similarly to {@link updateValue} by setting the current value of the characteristic
1623 * without calling any {@link CharacteristicEventTypes.SET} or {@link onSet} handlers.
1624 * The difference is that this method forces an event notification sent (updateValue only sends one if the value changed).
1625 * This is especially useful for characteristics like {@link Characteristic.ButtonEvent} or {@link Characteristic.ProgrammableSwitchEvent}.
1626 *
1627 * @param value - The new value.
1628 * @param context - Passed to the {@link CharacteristicEventTypes.CHANGE} event handler.
1629 */
1630 sendEventNotification(value, context) {
1631 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1632 value = this.validateUserInput(value);
1633 const oldValue = this.value;
1634 this.value = value;
1635 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: undefined, oldValue: oldValue, newValue: value, reason: "event" /* ChangeReason.EVENT */, context: context });
1636 return this; // for chaining
1637 }
1638 /**
1639 * Called when a HAP requests wants to know the current value of the characteristic.
1640 *
1641 * @param connection - The HAP connection from which the request originated from.
1642 * @param context - Deprecated parameter. There for backwards compatibility.
1643 * @private Used by the Accessory to load the characteristic value
1644 */
1645 async handleGetRequest(connection, context) {
1646 if (!this.props.perms.includes("pr" /* Perms.PAIRED_READ */)) { // check if we are allowed to read from this characteristic
1647 throw -70405 /* HAPStatus.WRITE_ONLY_CHARACTERISTIC */;
1648 }
1649 if (this.UUID === Characteristic.ProgrammableSwitchEvent.UUID) {
1650 // special workaround for event only programmable switch event, which must always return null
1651 return null;
1652 }
1653 if (this.getHandler) {
1654 if (this.listeners("get" /* CharacteristicEventTypes.GET */).length > 0) {
1655 this.characteristicWarning("Ignoring on('get') handler as onGet handler was defined instead");
1656 }
1657 try {
1658 let value = await this.getHandler(context, connection);
1659 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1660 try {
1661 value = this.validateUserInput(value);
1662 }
1663 catch (error) {
1664 this.characteristicWarning(`An illegal value was supplied by the read handler for characteristic: ${error?.message}`, "warn-message" /* CharacteristicWarningType.WARN_MESSAGE */, error?.stack);
1665 this.statusCode = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
1666 return Promise.reject(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
1667 }
1668 const oldValue = this.value;
1669 this.value = value;
1670 if (oldValue !== value) { // emit a change event if necessary
1671 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: connection, oldValue: oldValue, newValue: value, reason: "read" /* ChangeReason.READ */, context: context });
1672 }
1673 return value;
1674 }
1675 catch (error) {
1676 if (typeof error === "number") {
1677 const hapStatusError = new hapStatusError_1.HapStatusError(error);
1678 this.statusCode = hapStatusError.hapStatus;
1679 }
1680 else if (error instanceof hapStatusError_1.HapStatusError) {
1681 this.statusCode = error.hapStatus;
1682 }
1683 else {
1684 this.characteristicWarning(`Unhandled error thrown inside read handler for characteristic: ${error?.message}`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1685 this.statusCode = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
1686 }
1687 throw this.statusCode;
1688 }
1689 }
1690 if (this.listeners("get" /* CharacteristicEventTypes.GET */).length === 0) {
1691 if (this.statusCode) {
1692 throw this.statusCode;
1693 }
1694 try {
1695 return this.validateUserInput(this.value);
1696 }
1697 catch (error) {
1698 this.characteristicWarning(`An illegal value was supplied by setting \`value\` for characteristic: ${error?.message}`, "warn-message" /* CharacteristicWarningType.WARN_MESSAGE */, error?.stack);
1699 return Promise.reject(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
1700 }
1701 }
1702 return new Promise((resolve, reject) => {
1703 try {
1704 this.emit("get" /* CharacteristicEventTypes.GET */, (0, once_1.once)((status, value) => {
1705 if (status) {
1706 if (typeof status === "number") {
1707 const hapStatusError = new hapStatusError_1.HapStatusError(status);
1708 this.statusCode = hapStatusError.hapStatus;
1709 }
1710 else if (status instanceof hapStatusError_1.HapStatusError) {
1711 this.statusCode = status.hapStatus;
1712 }
1713 else {
1714 debug("[%s] Received error from get handler %s", this.displayName, status.stack);
1715 this.statusCode = extractHAPStatusFromError(status);
1716 }
1717 reject(this.statusCode);
1718 return;
1719 }
1720 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1721 value = this.validateUserInput(value);
1722 const oldValue = this.value;
1723 this.value = value;
1724 resolve(value);
1725 if (oldValue !== value) { // emit a change event if necessary
1726 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: connection, oldValue: oldValue, newValue: value, reason: "read" /* ChangeReason.READ */, context: context });
1727 }
1728 }), context, connection);
1729 }
1730 catch (error) {
1731 this.characteristicWarning(`Unhandled error thrown inside read handler for characteristic: ${error?.message}`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1732 this.statusCode = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
1733 reject(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
1734 }
1735 });
1736 }
1737 /**
1738 * Called when a HAP requests update the current value of the characteristic.
1739 *
1740 * @param value - The updated value
1741 * @param connection - The connection from which the request originated from
1742 * @param context - Deprecated parameter. There for backwards compatibility.
1743 * @returns Promise resolve to void in normal operation. When characteristic supports write-response, HAP
1744 * requests a write-response and the set handler returns a write-response value, the respective
1745 * write response value is resolved.
1746 * @private
1747 */
1748 async handleSetRequest(value, connection, context) {
1749 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1750 if (connection !== undefined) {
1751 // if connection is undefined, the set "request" comes from the setValue method.
1752 // for setValue a value of "null" is allowed and checked via validateUserInput.
1753 try {
1754 value = this.validateClientSuppliedValue(value);
1755 }
1756 catch (e) {
1757 debug(`[${this.displayName}]`, e.message);
1758 return Promise.reject(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */);
1759 }
1760 }
1761 const oldValue = this.value;
1762 if (this.setHandler) {
1763 if (this.listeners("set" /* CharacteristicEventTypes.SET */).length > 0) {
1764 this.characteristicWarning("Ignoring on('set') handler as onSet handler was defined instead");
1765 }
1766 try {
1767 const writeResponse = await this.setHandler(value, context, connection);
1768 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1769 if (writeResponse != null && this.props.perms.includes("wr" /* Perms.WRITE_RESPONSE */)) {
1770 this.value = this.validateUserInput(writeResponse);
1771 return this.value;
1772 }
1773 else {
1774 if (writeResponse != null) {
1775 this.characteristicWarning("SET handler returned write response value, though the characteristic doesn't support write response", "debug-message" /* CharacteristicWarningType.DEBUG_MESSAGE */);
1776 }
1777 this.value = value;
1778 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: connection, oldValue: oldValue, newValue: value, reason: "write" /* ChangeReason.WRITE */, context: context });
1779 return;
1780 }
1781 }
1782 catch (error) {
1783 if (typeof error === "number") {
1784 const hapStatusError = new hapStatusError_1.HapStatusError(error);
1785 this.statusCode = hapStatusError.hapStatus;
1786 }
1787 else if (error instanceof hapStatusError_1.HapStatusError) {
1788 this.statusCode = error.hapStatus;
1789 }
1790 else {
1791 this.characteristicWarning(`Unhandled error thrown inside write handler for characteristic: ${error?.message}`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1792 this.statusCode = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
1793 }
1794 throw this.statusCode;
1795 }
1796 }
1797 if (this.listeners("set" /* CharacteristicEventTypes.SET */).length === 0) {
1798 this.value = value;
1799 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: connection, oldValue: oldValue, newValue: value, reason: "write" /* ChangeReason.WRITE */, context: context });
1800 return Promise.resolve();
1801 }
1802 else {
1803 return new Promise((resolve, reject) => {
1804 try {
1805 this.emit("set" /* CharacteristicEventTypes.SET */, value, (0, once_1.once)((status, writeResponse) => {
1806 if (status) {
1807 if (typeof status === "number") {
1808 const hapStatusError = new hapStatusError_1.HapStatusError(status);
1809 this.statusCode = hapStatusError.hapStatus;
1810 }
1811 else if (status instanceof hapStatusError_1.HapStatusError) {
1812 this.statusCode = status.hapStatus;
1813 }
1814 else {
1815 debug("[%s] Received error from set handler %s", this.displayName, status.stack);
1816 this.statusCode = extractHAPStatusFromError(status);
1817 }
1818 reject(this.statusCode);
1819 return;
1820 }
1821 this.statusCode = 0 /* HAPStatus.SUCCESS */;
1822 if (writeResponse != null && this.props.perms.includes("wr" /* Perms.WRITE_RESPONSE */)) {
1823 // support write response simply by letting the implementor pass the response as second argument to the callback
1824 this.value = this.validateUserInput(writeResponse);
1825 resolve(this.value);
1826 }
1827 else {
1828 if (writeResponse != null) {
1829 this.characteristicWarning("SET handler returned write response value, though the characteristic doesn't support write response", "debug-message" /* CharacteristicWarningType.DEBUG_MESSAGE */);
1830 }
1831 this.value = value;
1832 resolve();
1833 this.emit("change" /* CharacteristicEventTypes.CHANGE */, { originator: connection, oldValue: oldValue, newValue: value, reason: "write" /* ChangeReason.WRITE */, context: context });
1834 }
1835 }), context, connection);
1836 }
1837 catch (error) {
1838 this.characteristicWarning(`Unhandled error thrown inside write handler for characteristic: ${error?.message}`, "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */, error?.stack);
1839 this.statusCode = -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */;
1840 reject(-70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
1841 }
1842 });
1843 }
1844 }
1845 /**
1846 * Called once a HomeKit controller subscribes to events of this characteristic.
1847 * @private
1848 */
1849 subscribe() {
1850 if (this.subscriptions === 0) {
1851 this.emit("subscribe" /* CharacteristicEventTypes.SUBSCRIBE */);
1852 }
1853 this.subscriptions++;
1854 }
1855 /**
1856 * Called once a HomeKit controller unsubscribe to events of this characteristic or a HomeKit controller
1857 * which was subscribed to this characteristic disconnects.
1858 * @private
1859 */
1860 unsubscribe() {
1861 const wasOne = this.subscriptions === 1;
1862 this.subscriptions--;
1863 this.subscriptions = Math.max(this.subscriptions, 0);
1864 if (wasOne) {
1865 this.emit("unsubscribe" /* CharacteristicEventTypes.UNSUBSCRIBE */);
1866 }
1867 }
1868 getDefaultValue() {
1869 // noinspection JSDeprecatedSymbols
1870 switch (this.props.format) {
1871 case "bool" /* Formats.BOOL */:
1872 return false;
1873 case "string" /* Formats.STRING */:
1874 switch (this.UUID) {
1875 case Characteristic.Manufacturer.UUID:
1876 return "Default-Manufacturer";
1877 case Characteristic.Model.UUID:
1878 return "Default-Model";
1879 case Characteristic.SerialNumber.UUID:
1880 return "Default-SerialNumber";
1881 case Characteristic.FirmwareRevision.UUID:
1882 return "0.0.0";
1883 default:
1884 return "";
1885 }
1886 case "data" /* Formats.DATA */:
1887 return ""; // who knows!
1888 case "tlv8" /* Formats.TLV8 */:
1889 return ""; // who knows!
1890 case "int" /* Formats.INT */:
1891 case "float" /* Formats.FLOAT */:
1892 case "uint8" /* Formats.UINT8 */:
1893 case "uint16" /* Formats.UINT16 */:
1894 case "uint32" /* Formats.UINT32 */:
1895 case "uint64" /* Formats.UINT64 */:
1896 switch (this.UUID) {
1897 case Characteristic.CurrentTemperature.UUID:
1898 return 0; // some existing integrations expect this to be 0 by default
1899 default: {
1900 if (this.props.validValues?.length && typeof this.props.validValues[0] === "number") {
1901 return this.props.validValues[0];
1902 }
1903 if (typeof this.props.minValue === "number" && Number.isFinite(this.props.minValue)) {
1904 return this.props.minValue;
1905 }
1906 return 0;
1907 }
1908 }
1909 default:
1910 return 0;
1911 }
1912 }
1913 /**
1914 * Checks if the value received from the HAP request is valid.
1915 * If returned false the received value is not valid and {@link HAPStatus.INVALID_VALUE_IN_REQUEST}
1916 * must be returned.
1917 * @param value - Value supplied by the HomeKit controller
1918 */
1919 validateClientSuppliedValue(value) {
1920 if (value == null) {
1921 throw new Error(`Client supplied invalid value for ${this.props.format}: ${value}`);
1922 }
1923 switch (this.props.format) {
1924 case "bool" /* Formats.BOOL */: {
1925 if (typeof value === "boolean") {
1926 return value;
1927 }
1928 if (typeof value === "number" && (value === 1 || value === 0)) {
1929 return Boolean(value);
1930 }
1931 throw new Error(`Client supplied invalid type for ${this.props.format}: "${value}" (${typeof value})`);
1932 }
1933 case "int" /* Formats.INT */:
1934 case "float" /* Formats.FLOAT */:
1935 case "uint8" /* Formats.UINT8 */:
1936 case "uint16" /* Formats.UINT16 */:
1937 case "uint32" /* Formats.UINT32 */:
1938 case "uint64" /* Formats.UINT64 */: {
1939 if (typeof value === "boolean") {
1940 value = value ? 1 : 0;
1941 }
1942 if (typeof value !== "number" || !Number.isFinite(value)) {
1943 throw new Error(`Client supplied invalid type for ${this.props.format}: "${value}" (${typeof value})`);
1944 }
1945 const numericMin = maxWithUndefined(this.props.minValue, (0, request_util_1.numericLowerBound)(this.props.format));
1946 const numericMax = minWithUndefined(this.props.maxValue, (0, request_util_1.numericUpperBound)(this.props.format));
1947 if (typeof numericMin === "number" && value < numericMin) {
1948 throw new Error(`Client supplied value of ${value} is less than the minimum allowed value of ${numericMin}`);
1949 }
1950 if (typeof numericMax === "number" && value > numericMax) {
1951 throw new Error(`Client supplied value of ${value} is greater than the maximum allowed value of ${numericMax}`);
1952 }
1953 if (this.props.validValues && !this.props.validValues.includes(value)) {
1954 throw new Error(`Client supplied value of ${value} is not in ${this.props.validValues.toString()}`);
1955 }
1956 if (this.props.validValueRanges && this.props.validValueRanges.length === 2) {
1957 if (value < this.props.validValueRanges[0]) {
1958 throw new Error(`Client supplied value of ${value} is less than the minimum allowed value of ${this.props.validValueRanges[0]}`);
1959 }
1960 if (value > this.props.validValueRanges[1]) {
1961 throw new Error(`Client supplied value of ${value} is greater than the maximum allowed value of ${this.props.validValueRanges[1]}`);
1962 }
1963 }
1964 return value;
1965 }
1966 case "string" /* Formats.STRING */: {
1967 if (typeof value !== "string") {
1968 throw new Error(`Client supplied invalid type for ${this.props.format}: "${value}" (${typeof value})`);
1969 }
1970 const maxLength = this.props.maxLen != null ? this.props.maxLen : 64; // default is 64; max is 256 which is set in setProps
1971 if (value.length > maxLength) {
1972 throw new Error(`Client supplied value length of ${value.length} exceeds maximum length allowed of ${maxLength}`);
1973 }
1974 return value;
1975 }
1976 case "data" /* Formats.DATA */: {
1977 if (typeof value !== "string") {
1978 throw new Error(`Client supplied invalid type for ${this.props.format}: "${value}" (${typeof value})`);
1979 }
1980 // we don't validate base64 here
1981 const maxLength = this.props.maxDataLen != null ? this.props.maxDataLen : 0x200000; // default is 0x200000
1982 if (value.length > maxLength) {
1983 throw new Error(`Client supplied value length of ${value.length} exceeds maximum length allowed of ${maxLength}`);
1984 }
1985 return value;
1986 }
1987 case "tlv8" /* Formats.TLV8 */:
1988 if (typeof value !== "string") {
1989 throw new Error(`Client supplied invalid type for ${this.props.format}: "${value}" (${typeof value})`);
1990 }
1991 return value;
1992 }
1993 return value;
1994 }
1995 /**
1996 * Checks if the value received from the API call is valid.
1997 * It adjusts the value where it makes sense, prints a warning where values may be rejected with an error
1998 * in the future and throws an error which can't be converted to a valid value.
1999 *
2000 * @param value - The value received from the API call
2001 * @param warningType - Optionally defines the warning type to use when raising a {@link CharacteristicEventTypes.CHARACTERISTIC_WARNING}.
2002 */
2003 validateUserInput(value, warningType = "warn-message" /* CharacteristicWarningType.WARN_MESSAGE */) {
2004 if (value === null) {
2005 if (this.UUID === Characteristic.Model.UUID || this.UUID === Characteristic.SerialNumber.UUID) { // mirrors the statement in case: Formats.STRING
2006 this.characteristicWarning("characteristic must have a non null value otherwise HomeKit will reject this accessory, ignoring new value", "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
2007 return this.value; // don't change the value
2008 }
2009 if (this.props.format === "data" /* Formats.DATA */ || this.props.format === "tlv8" /* Formats.TLV8 */) {
2010 return value; // TLV8 and DATA formats are allowed to have null as a value
2011 }
2012 /**
2013 * A short disclaimer here.
2014 * null is actually a perfectly valid value for characteristics to have.
2015 * The Home app will show "no response" for some characteristics for which it can't handle null
2016 * but ultimately its valid and the developers decision what the return.
2017 * BUT: out of history hap-nodejs did replace null with the last known value and thus
2018 * homebridge devs started to adopting this method as a way of not changing the value in a GET handler.
2019 * As an intermediate step we kept the behavior but added a warning printed to the console.
2020 * In a future update we will do the breaking change of return null below!
2021 */
2022 if (this.UUID.endsWith(uuid_1.BASE_UUID)) { // we have an apple defined characteristic (at least assuming nobody else uses the UUID namespace)
2023 if (this.UUID === Characteristic.ProgrammableSwitchEvent.UUID) {
2024 return value; // null is allowed as a value for ProgrammableSwitchEvent
2025 }
2026 this.characteristicWarning("characteristic was supplied illegal value: null! Home App will reject null for Apple defined characteristics", warningType);
2027 // if the value has been set previously, return it now, otherwise continue with validation to have a default value set.
2028 if (this.value !== null) {
2029 return this.value;
2030 }
2031 }
2032 else {
2033 // we currently allow null for any non-custom defined characteristics
2034 return value;
2035 }
2036 }
2037 switch (this.props.format) {
2038 case "bool" /* Formats.BOOL */: {
2039 if (typeof value === "boolean") {
2040 return value;
2041 }
2042 if (typeof value === "number") {
2043 return value === 1;
2044 }
2045 if (typeof value === "string") {
2046 return value === "1" || value === "true";
2047 }
2048 this.characteristicWarning("characteristic value expected boolean and received " + typeof value, warningType);
2049 return false;
2050 }
2051 case "int" /* Formats.INT */:
2052 case "float" /* Formats.FLOAT */:
2053 case "uint8" /* Formats.UINT8 */:
2054 case "uint16" /* Formats.UINT16 */:
2055 case "uint32" /* Formats.UINT32 */:
2056 case "uint64" /* Formats.UINT64 */: {
2057 if (typeof value === "boolean") {
2058 value = value ? 1 : 0;
2059 }
2060 if (typeof value === "string") {
2061 value = this.props.format === "float" /* Formats.FLOAT */ ? parseFloat(value) : parseInt(value, 10);
2062 }
2063 if (typeof value !== "number" || !Number.isFinite(value)) {
2064 this.characteristicWarning(`characteristic value expected valid finite number and received "${value}" (${typeof value})`, warningType);
2065 value = typeof this.value === "number" ? this.value : this.props.minValue || 0;
2066 }
2067 const numericMin = maxWithUndefined(this.props.minValue, (0, request_util_1.numericLowerBound)(this.props.format));
2068 const numericMax = minWithUndefined(this.props.maxValue, (0, request_util_1.numericUpperBound)(this.props.format));
2069 let stepValue = undefined;
2070 if (this.props.format === "float" /* Formats.FLOAT */) {
2071 stepValue = this.props.minStep;
2072 }
2073 else {
2074 stepValue = maxWithUndefined(this.props.minStep, 1);
2075 }
2076 if (stepValue != null && stepValue > 0) {
2077 const minValue = this.props.minValue != null ? this.props.minValue : 0;
2078 value = stepValue * Math.round((value - minValue) / stepValue) + minValue;
2079 }
2080 if (numericMin != null && value < numericMin) {
2081 this.characteristicWarning(`characteristic was supplied illegal value: number ${value} exceeded minimum of ${numericMin}`, warningType);
2082 value = numericMin;
2083 }
2084 if (numericMax != null && value > numericMax) {
2085 this.characteristicWarning(`characteristic was supplied illegal value: number ${value} exceeded maximum of ${numericMax}`, warningType);
2086 value = numericMax;
2087 }
2088 if (this.props.validValues && !this.props.validValues.includes(value)) {
2089 this.characteristicWarning(`characteristic value ${value} is not contained in valid values array`, warningType);
2090 return this.props.validValues.includes(this.value) ? this.value : (this.props.validValues[0] || 0);
2091 }
2092 if (this.props.validValueRanges && this.props.validValueRanges.length === 2) {
2093 if (value < this.props.validValueRanges[0]) {
2094 this.characteristicWarning(`characteristic was supplied illegal value: number ${value} not contained in valid value range of `
2095 + `${this.props.validValueRanges}, supplying illegal values will throw errors in the future`, warningType);
2096 value = this.props.validValueRanges[0];
2097 }
2098 else if (value > this.props.validValueRanges[1]) {
2099 this.characteristicWarning(`characteristic was supplied illegal value: number ${value} not contained in valid value range of `
2100 + `${this.props.validValueRanges}, supplying illegal values will throw errors in the future`, warningType);
2101 value = this.props.validValueRanges[1];
2102 }
2103 }
2104 return value;
2105 }
2106 case "string" /* Formats.STRING */: {
2107 if (typeof value === "number") {
2108 this.characteristicWarning("characteristic was supplied illegal value: number instead of string, " +
2109 "supplying illegal values will throw errors in the future", warningType);
2110 value = String(value);
2111 }
2112 if (typeof value !== "string") {
2113 this.characteristicWarning("characteristic value expected string and received " + (typeof value), warningType);
2114 value = typeof this.value === "string" ? this.value : value + "";
2115 }
2116 // mirrors the case value = null at the beginning
2117 if (value.length <= 1 && (this.UUID === Characteristic.Model.UUID || this.UUID === Characteristic.SerialNumber.UUID)) {
2118 this.characteristicWarning(`[${this.displayName}] characteristic must have a length of more than 1 character otherwise`
2119 + ` HomeKit will reject this accessory, ignoring new value ${warningType}`);
2120 return this.value; // just return the current value
2121 }
2122 const maxLength = this.props.maxLen ?? 64; // default is 64 (max is 256 which is set in setProps)
2123 if (value.length > maxLength) {
2124 this.characteristicWarning(`characteristic was supplied illegal value: string '${value}' exceeded max length of ${maxLength}`, warningType);
2125 value = value.substring(0, maxLength);
2126 }
2127 if (value.length > 0 && this.UUID === Characteristic.ConfiguredName.UUID) {
2128 (0, checkName_1.checkName)(this.displayName, "ConfiguredName", value);
2129 }
2130 return value;
2131 }
2132 case "data" /* Formats.DATA */:
2133 if (typeof value !== "string") {
2134 throw new Error("characteristic with DATA format must have string value");
2135 }
2136 if (this.props.maxDataLen != null && value.length > this.props.maxDataLen) {
2137 // can't cut it as we would basically set binary rubbish afterwards
2138 throw new Error("characteristic with DATA format exceeds specified maxDataLen");
2139 }
2140 return value;
2141 case "tlv8" /* Formats.TLV8 */:
2142 if (value === undefined) {
2143 this.characteristicWarning("characteristic was supplied illegal value: undefined", warningType);
2144 return this.value;
2145 }
2146 return value; // we trust that this is valid tlv8
2147 }
2148 // hopefully it shouldn't get to this point
2149 if (value === undefined) {
2150 this.characteristicWarning("characteristic was supplied illegal value: undefined", "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */);
2151 return this.value;
2152 }
2153 return value;
2154 }
2155 /**
2156 * @private used to assign iid to characteristic
2157 */
2158 _assignID(identifierCache, accessoryName, serviceUUID, serviceSubtype) {
2159 // generate our IID based on our UUID
2160 this.iid = identifierCache.getIID(accessoryName, serviceUUID, serviceSubtype, this.UUID);
2161 }
2162 characteristicWarning(message, type = "warn-message" /* CharacteristicWarningType.WARN_MESSAGE */, stack = new Error().stack) {
2163 this.emit("characteristic-warning" /* CharacteristicEventTypes.CHARACTERISTIC_WARNING */, type, message, stack);
2164 }
2165 /**
2166 * @param event
2167 * @private
2168 */
2169 removeAllListeners(event) {
2170 if (!event) {
2171 this.removeOnGet();
2172 this.removeOnSet();
2173 }
2174 return super.removeAllListeners(event);
2175 }
2176 /**
2177 * @param characteristic
2178 * @private
2179 */
2180 replaceBy(characteristic) {
2181 this.props = characteristic.props;
2182 this.updateValue(characteristic.value);
2183 const getListeners = characteristic.listeners("get" /* CharacteristicEventTypes.GET */);
2184 if (getListeners.length) {
2185 // the callback can only be called once, so we remove all old listeners
2186 this.removeAllListeners("get" /* CharacteristicEventTypes.GET */);
2187 // @ts-expect-error: force type
2188 getListeners.forEach(listener => this.addListener("get" /* CharacteristicEventTypes.GET */, listener));
2189 }
2190 this.removeOnGet();
2191 if (characteristic.getHandler) {
2192 this.onGet(characteristic.getHandler);
2193 }
2194 const setListeners = characteristic.listeners("set" /* CharacteristicEventTypes.SET */);
2195 if (setListeners.length) {
2196 // the callback can only be called once, so we remove all old listeners
2197 this.removeAllListeners("set" /* CharacteristicEventTypes.SET */);
2198 // @ts-expect-error: force type
2199 setListeners.forEach(listener => this.addListener("set" /* CharacteristicEventTypes.SET */, listener));
2200 }
2201 this.removeOnSet();
2202 if (characteristic.setHandler) {
2203 this.onSet(characteristic.setHandler);
2204 }
2205 }
2206 /**
2207 * Returns a JSON representation of this characteristic suitable for delivering to HAP clients.
2208 * @private used to generate response to /accessories query
2209 */
2210 async toHAP(connection, contactGetHandlers = true) {
2211 const object = this.internalHAPRepresentation();
2212 if (!this.props.perms.includes("pr" /* Perms.PAIRED_READ */)) {
2213 object.value = undefined;
2214 }
2215 else if (this.UUID === Characteristic.ProgrammableSwitchEvent.UUID) {
2216 // special workaround for event only programmable switch event, which must always return null
2217 object.value = null;
2218 }
2219 else { // query the current value
2220 const value = contactGetHandlers
2221 ? await this.handleGetRequest(connection).catch(() => {
2222 const value = this.getDefaultValue();
2223 debug("[%s] Error getting value for characteristic on /accessories request. Returning default value instead: %s", this.displayName, `${value}`);
2224 return value; // use default value
2225 })
2226 : this.value;
2227 object.value = (0, request_util_1.formatOutgoingCharacteristicValue)(value, this.props);
2228 }
2229 return object;
2230 }
2231 /**
2232 * Returns a JSON representation of this characteristic without the value.
2233 * @private used to generate the config hash
2234 */
2235 internalHAPRepresentation() {
2236 (0, assert_1.default)(this.iid, "iid cannot be undefined for characteristic '" + this.displayName + "'");
2237 // TODO include the value for characteristics of the AccessoryInformation service
2238 return {
2239 type: (0, uuid_1.toShortForm)(this.UUID),
2240 iid: this.iid,
2241 value: null,
2242 perms: this.props.perms,
2243 description: this.props.description || this.displayName,
2244 format: this.props.format,
2245 unit: this.props.unit,
2246 minValue: this.props.minValue,
2247 maxValue: this.props.maxValue,
2248 minStep: this.props.minStep,
2249 maxLen: this.props.maxLen,
2250 maxDataLen: this.props.maxDataLen,
2251 "valid-values": this.props.validValues,
2252 "valid-values-range": this.props.validValueRanges,
2253 };
2254 }
2255 /**
2256 * Serialize characteristic into json string.
2257 *
2258 * @param characteristic - Characteristic object.
2259 * @private used to store characteristic on disk
2260 */
2261 static serialize(characteristic) {
2262 let constructorName;
2263 if (characteristic.constructor.name !== "Characteristic") {
2264 constructorName = characteristic.constructor.name;
2265 }
2266 return {
2267 displayName: characteristic.displayName,
2268 UUID: characteristic.UUID,
2269 eventOnlyCharacteristic: characteristic.UUID === Characteristic.ProgrammableSwitchEvent.UUID, // support downgrades for now
2270 constructorName: constructorName,
2271 value: characteristic.value,
2272 props: (0, clone_1.clone)({}, characteristic.props),
2273 };
2274 }
2275 /**
2276 * Deserialize characteristic from json string.
2277 *
2278 * @param json - Json string representing a characteristic.
2279 * @private used to recreate characteristic from disk
2280 */
2281 static deserialize(json) {
2282 let characteristic;
2283 if (json.constructorName && json.constructorName.charAt(0).toUpperCase() === json.constructorName.charAt(0)
2284 && Characteristic[json.constructorName]) { // MUST start with uppercase character and must exist on Characteristic object
2285 const constructor = Characteristic[json.constructorName];
2286 characteristic = new constructor();
2287 characteristic.displayName = json.displayName;
2288 characteristic.setProps(json.props);
2289 }
2290 else {
2291 characteristic = new Characteristic(json.displayName, json.UUID, json.props);
2292 }
2293 characteristic.value = json.value;
2294 return characteristic;
2295 }
2296}
2297exports.Characteristic = Characteristic;
2298// We have a cyclic dependency problem. Within this file we have the definitions of "./definitions" as
2299// type imports only (in order to define the static properties). Setting those properties is done outside
2300// this file, within the definition files. Therefore, we import it at the end of this file. Seems weird, but is important.
2301require("./definitions/CharacteristicDefinitions");
2302//# sourceMappingURL=Characteristic.js.map
\No newline at end of file