hap-nodejs 1.1.1-beta.1 → 1.1.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/bonjour-hap.d.ts +50 -53
- package/README.md +16 -19
- package/dist/accessories/AirConditioner_accessory.js +28 -30
- package/dist/accessories/AirConditioner_accessory.js.map +1 -1
- package/dist/accessories/AppleTVRemote_accessory.js +69 -81
- package/dist/accessories/AppleTVRemote_accessory.js.map +1 -1
- package/dist/accessories/Camera_accessory.js +141 -136
- package/dist/accessories/Camera_accessory.js.map +1 -1
- package/dist/accessories/Fan_accessory.js +18 -22
- package/dist/accessories/Fan_accessory.js.map +1 -1
- package/dist/accessories/GarageDoorOpener_accessory.js +33 -35
- package/dist/accessories/GarageDoorOpener_accessory.js.map +1 -1
- package/dist/accessories/Light-AdaptiveLighting_accessory.js +42 -44
- package/dist/accessories/Light-AdaptiveLighting_accessory.js.map +1 -1
- package/dist/accessories/Light_accessory.js +32 -34
- package/dist/accessories/Light_accessory.js.map +1 -1
- package/dist/accessories/Lock_accessory.js +25 -26
- package/dist/accessories/Lock_accessory.js.map +1 -1
- package/dist/accessories/MotionSensor_accessory.js +13 -16
- package/dist/accessories/MotionSensor_accessory.js.map +1 -1
- package/dist/accessories/Outlet_accessory.js +20 -22
- package/dist/accessories/Outlet_accessory.js.map +1 -1
- package/dist/accessories/SmartSpeaker_accessory.js +18 -20
- package/dist/accessories/SmartSpeaker_accessory.js.map +1 -1
- package/dist/accessories/Sprinkler_accessory.js +34 -37
- package/dist/accessories/Sprinkler_accessory.js.map +1 -1
- package/dist/accessories/TV_accessory.js +43 -45
- package/dist/accessories/TV_accessory.js.map +1 -1
- package/dist/accessories/TemperatureSensor_accessory.js +12 -15
- package/dist/accessories/TemperatureSensor_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.d.ts +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.d.ts.map +1 -1
- package/dist/accessories/Wi-FiRouter_accessory.js +9 -12
- package/dist/accessories/Wi-FiRouter_accessory.js.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.d.ts +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.d.ts.map +1 -1
- package/dist/accessories/Wi-FiSatellite_accessory.js +11 -14
- package/dist/accessories/Wi-FiSatellite_accessory.js.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.d.ts +3 -3
- package/dist/accessories/gstreamer-audioProducer.d.ts.map +1 -1
- package/dist/accessories/gstreamer-audioProducer.js +37 -38
- package/dist/accessories/gstreamer-audioProducer.js.map +1 -1
- package/dist/accessories/types.d.ts +63 -63
- package/dist/accessories/types.d.ts.map +1 -1
- package/dist/accessories/types.js +83 -87
- package/dist/accessories/types.js.map +1 -1
- package/dist/index.d.ts +26 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -34
- package/dist/index.js.map +1 -1
- package/dist/internal-types.d.ts +1 -1
- package/dist/internal-types.d.ts.map +1 -1
- package/dist/internal-types.js +19 -21
- package/dist/internal-types.js.map +1 -1
- package/dist/lib/Accessory.d.ts +39 -38
- package/dist/lib/Accessory.d.ts.map +1 -1
- package/dist/lib/Accessory.js +314 -292
- package/dist/lib/Accessory.js.map +1 -1
- package/dist/lib/Advertiser.d.ts +7 -7
- package/dist/lib/Advertiser.d.ts.map +1 -1
- package/dist/lib/Advertiser.js +128 -136
- package/dist/lib/Advertiser.js.map +1 -1
- package/dist/lib/Bridge.d.ts +1 -1
- package/dist/lib/Bridge.d.ts.map +1 -1
- package/dist/lib/Bridge.js +2 -6
- package/dist/lib/Bridge.js.map +1 -1
- package/dist/lib/Characteristic.d.ts +40 -41
- package/dist/lib/Characteristic.d.ts.map +1 -1
- package/dist/lib/Characteristic.js +204 -208
- package/dist/lib/Characteristic.js.map +1 -1
- package/dist/lib/HAPServer.d.ts +31 -30
- package/dist/lib/HAPServer.d.ts.map +1 -1
- package/dist/lib/HAPServer.js +220 -229
- package/dist/lib/HAPServer.js.map +1 -1
- package/dist/lib/Service.d.ts +22 -22
- package/dist/lib/Service.d.ts.map +1 -1
- package/dist/lib/Service.js +63 -67
- package/dist/lib/Service.js.map +1 -1
- package/dist/lib/camera/RTPProxy.d.ts +2 -1
- package/dist/lib/camera/RTPProxy.d.ts.map +1 -1
- package/dist/lib/camera/RTPProxy.js +28 -44
- package/dist/lib/camera/RTPProxy.js.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.d.ts +34 -33
- package/dist/lib/camera/RTPStreamManagement.d.ts.map +1 -1
- package/dist/lib/camera/RTPStreamManagement.js +179 -151
- package/dist/lib/camera/RTPStreamManagement.js.map +1 -1
- package/dist/lib/camera/RecordingManagement.d.ts +19 -17
- package/dist/lib/camera/RecordingManagement.d.ts.map +1 -1
- package/dist/lib/camera/RecordingManagement.js +160 -151
- package/dist/lib/camera/RecordingManagement.js.map +1 -1
- package/dist/lib/camera/index.d.ts +3 -3
- package/dist/lib/camera/index.d.ts.map +1 -1
- package/dist/lib/camera/index.js +3 -6
- package/dist/lib/camera/index.js.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.d.ts +33 -33
- package/dist/lib/controller/AdaptiveLightingController.d.ts.map +1 -1
- package/dist/lib/controller/AdaptiveLightingController.js +152 -144
- package/dist/lib/controller/AdaptiveLightingController.js.map +1 -1
- package/dist/lib/controller/CameraController.d.ts +22 -20
- package/dist/lib/controller/CameraController.d.ts.map +1 -1
- package/dist/lib/controller/CameraController.js +74 -78
- package/dist/lib/controller/CameraController.js.map +1 -1
- package/dist/lib/controller/Controller.d.ts +4 -4
- package/dist/lib/controller/Controller.d.ts.map +1 -1
- package/dist/lib/controller/Controller.js +5 -8
- package/dist/lib/controller/Controller.js.map +1 -1
- package/dist/lib/controller/DoorbellController.d.ts +5 -4
- package/dist/lib/controller/DoorbellController.d.ts.map +1 -1
- package/dist/lib/controller/DoorbellController.js +9 -13
- package/dist/lib/controller/DoorbellController.js.map +1 -1
- package/dist/lib/controller/RemoteController.d.ts +39 -37
- package/dist/lib/controller/RemoteController.d.ts.map +1 -1
- package/dist/lib/controller/RemoteController.js +208 -197
- package/dist/lib/controller/RemoteController.js.map +1 -1
- package/dist/lib/controller/index.d.ts +5 -5
- package/dist/lib/controller/index.d.ts.map +1 -1
- package/dist/lib/controller/index.js +5 -8
- package/dist/lib/controller/index.js.map +1 -1
- package/dist/lib/datastream/DataStreamManagement.d.ts +2 -2
- package/dist/lib/datastream/DataStreamManagement.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamManagement.js +39 -39
- package/dist/lib/datastream/DataStreamManagement.js.map +1 -1
- package/dist/lib/datastream/DataStreamParser.d.ts +1 -0
- package/dist/lib/datastream/DataStreamParser.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamParser.js +57 -77
- package/dist/lib/datastream/DataStreamParser.js.map +1 -1
- package/dist/lib/datastream/DataStreamServer.d.ts +23 -22
- package/dist/lib/datastream/DataStreamServer.d.ts.map +1 -1
- package/dist/lib/datastream/DataStreamServer.js +154 -164
- package/dist/lib/datastream/DataStreamServer.js.map +1 -1
- package/dist/lib/datastream/index.d.ts +3 -3
- package/dist/lib/datastream/index.d.ts.map +1 -1
- package/dist/lib/datastream/index.js +3 -6
- package/dist/lib/datastream/index.js.map +1 -1
- package/dist/lib/dbus/align.d.ts +2 -0
- package/dist/lib/dbus/align.d.ts.map +1 -0
- package/dist/lib/dbus/align.js +12 -0
- package/dist/lib/dbus/align.js.map +1 -0
- package/dist/lib/dbus/bus.d.ts +38 -0
- package/dist/lib/dbus/bus.d.ts.map +1 -0
- package/dist/lib/dbus/bus.js +222 -0
- package/dist/lib/dbus/bus.js.map +1 -0
- package/dist/lib/dbus/constants.d.ts +43 -0
- package/dist/lib/dbus/constants.d.ts.map +1 -0
- package/dist/lib/dbus/constants.js +53 -0
- package/dist/lib/dbus/constants.js.map +1 -0
- package/dist/lib/dbus/dbus-buffer.d.ts +30 -0
- package/dist/lib/dbus/dbus-buffer.d.ts.map +1 -0
- package/dist/lib/dbus/dbus-buffer.js +175 -0
- package/dist/lib/dbus/dbus-buffer.js.map +1 -0
- package/dist/lib/dbus/handshake.d.ts +2 -0
- package/dist/lib/dbus/handshake.d.ts.map +1 -0
- package/dist/lib/dbus/handshake.js +130 -0
- package/dist/lib/dbus/handshake.js.map +1 -0
- package/dist/lib/dbus/index.d.ts +3 -0
- package/dist/lib/dbus/index.d.ts.map +1 -0
- package/dist/lib/dbus/index.js +123 -0
- package/dist/lib/dbus/index.js.map +1 -0
- package/dist/lib/dbus/introspect.d.ts +30 -0
- package/dist/lib/dbus/introspect.d.ts.map +1 -0
- package/dist/lib/dbus/introspect.js +208 -0
- package/dist/lib/dbus/introspect.js.map +1 -0
- package/dist/lib/dbus/marshall.d.ts +2 -0
- package/dist/lib/dbus/marshall.d.ts.map +1 -0
- package/dist/lib/dbus/marshall.js +97 -0
- package/dist/lib/dbus/marshall.js.map +1 -0
- package/dist/lib/dbus/marshallers.d.ts +10 -0
- package/dist/lib/dbus/marshallers.d.ts.map +1 -0
- package/dist/lib/dbus/marshallers.js +329 -0
- package/dist/lib/dbus/marshallers.js.map +1 -0
- package/dist/lib/dbus/message.d.ts +4 -0
- package/dist/lib/dbus/message.d.ts.map +1 -0
- package/dist/lib/dbus/message.js +116 -0
- package/dist/lib/dbus/message.js.map +1 -0
- package/dist/lib/dbus/put.d.ts +21 -0
- package/dist/lib/dbus/put.d.ts.map +1 -0
- package/dist/lib/dbus/put.js +120 -0
- package/dist/lib/dbus/put.js.map +1 -0
- package/dist/lib/dbus/readline.d.ts +2 -0
- package/dist/lib/dbus/readline.d.ts.map +1 -0
- package/dist/lib/dbus/readline.js +27 -0
- package/dist/lib/dbus/readline.js.map +1 -0
- package/dist/lib/dbus/signature.d.ts +2 -0
- package/dist/lib/dbus/signature.d.ts.map +1 -0
- package/dist/lib/dbus/signature.js +58 -0
- package/dist/lib/dbus/signature.js.map +1 -0
- package/dist/lib/dbus/stdifaces.d.ts +3 -0
- package/dist/lib/dbus/stdifaces.d.ts.map +1 -0
- package/dist/lib/dbus/stdifaces.js +206 -0
- package/dist/lib/dbus/stdifaces.js.map +1 -0
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/CharacteristicDefinitions.js +958 -1204
- package/dist/lib/definitions/CharacteristicDefinitions.js.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.d.ts +1 -1
- package/dist/lib/definitions/ServiceDefinitions.d.ts.map +1 -1
- package/dist/lib/definitions/ServiceDefinitions.js +620 -695
- package/dist/lib/definitions/ServiceDefinitions.js.map +1 -1
- package/dist/lib/definitions/generate-definitions.d.ts +3 -3
- package/dist/lib/definitions/generate-definitions.d.ts.map +1 -1
- package/dist/lib/definitions/generate-definitions.js +246 -253
- package/dist/lib/definitions/generate-definitions.js.map +1 -1
- package/dist/lib/definitions/generator-configuration.d.ts +1 -1
- package/dist/lib/definitions/generator-configuration.d.ts.map +1 -1
- package/dist/lib/definitions/generator-configuration.js +160 -165
- package/dist/lib/definitions/generator-configuration.js.map +1 -1
- package/dist/lib/definitions/index.d.ts +2 -2
- package/dist/lib/definitions/index.d.ts.map +1 -1
- package/dist/lib/definitions/index.js +2 -5
- package/dist/lib/definitions/index.js.map +1 -1
- package/dist/lib/model/AccessoryInfo.d.ts +4 -3
- package/dist/lib/model/AccessoryInfo.d.ts.map +1 -1
- package/dist/lib/model/AccessoryInfo.js +50 -53
- package/dist/lib/model/AccessoryInfo.js.map +1 -1
- package/dist/lib/model/ControllerStorage.d.ts +3 -3
- package/dist/lib/model/ControllerStorage.d.ts.map +1 -1
- package/dist/lib/model/ControllerStorage.js +17 -22
- package/dist/lib/model/ControllerStorage.js.map +1 -1
- package/dist/lib/model/HAPStorage.d.ts +2 -2
- package/dist/lib/model/HAPStorage.d.ts.map +1 -1
- package/dist/lib/model/HAPStorage.js +4 -11
- package/dist/lib/model/HAPStorage.js.map +1 -1
- package/dist/lib/model/IdentifierCache.d.ts +1 -1
- package/dist/lib/model/IdentifierCache.d.ts.map +1 -1
- package/dist/lib/model/IdentifierCache.js +19 -27
- package/dist/lib/model/IdentifierCache.js.map +1 -1
- package/dist/lib/tv/AccessControlManagement.d.ts +9 -9
- package/dist/lib/tv/AccessControlManagement.d.ts.map +1 -1
- package/dist/lib/tv/AccessControlManagement.js +27 -29
- package/dist/lib/tv/AccessControlManagement.js.map +1 -1
- package/dist/lib/util/checkName.d.ts +2 -2
- package/dist/lib/util/checkName.d.ts.map +1 -1
- package/dist/lib/util/checkName.js +6 -9
- package/dist/lib/util/checkName.js.map +1 -1
- package/dist/lib/util/clone.d.ts.map +1 -1
- package/dist/lib/util/clone.js +1 -5
- package/dist/lib/util/clone.js.map +1 -1
- package/dist/lib/util/color-utils.d.ts +1 -1
- package/dist/lib/util/color-utils.d.ts.map +1 -1
- package/dist/lib/util/color-utils.js +4 -9
- package/dist/lib/util/color-utils.js.map +1 -1
- package/dist/lib/util/eventedhttp.d.ts +23 -22
- package/dist/lib/util/eventedhttp.d.ts.map +1 -1
- package/dist/lib/util/eventedhttp.js +109 -116
- package/dist/lib/util/eventedhttp.js.map +1 -1
- package/dist/lib/util/hapCrypto.d.ts +3 -2
- package/dist/lib/util/hapCrypto.d.ts.map +1 -1
- package/dist/lib/util/hapCrypto.js +31 -40
- package/dist/lib/util/hapCrypto.js.map +1 -1
- package/dist/lib/util/hapStatusError.d.ts +1 -1
- package/dist/lib/util/hapStatusError.d.ts.map +1 -1
- package/dist/lib/util/hapStatusError.js +4 -8
- package/dist/lib/util/hapStatusError.js.map +1 -1
- package/dist/lib/util/net-utils.d.ts +1 -1
- package/dist/lib/util/net-utils.js +17 -23
- package/dist/lib/util/net-utils.js.map +1 -1
- package/dist/lib/util/once.d.ts.map +1 -1
- package/dist/lib/util/once.js +2 -6
- package/dist/lib/util/once.js.map +1 -1
- package/dist/lib/util/promise-utils.d.ts +1 -1
- package/dist/lib/util/promise-utils.d.ts.map +1 -1
- package/dist/lib/util/promise-utils.js +3 -9
- package/dist/lib/util/promise-utils.js.map +1 -1
- package/dist/lib/util/request-util.d.ts +3 -2
- package/dist/lib/util/request-util.d.ts.map +1 -1
- package/dist/lib/util/request-util.js +11 -19
- package/dist/lib/util/request-util.js.map +1 -1
- package/dist/lib/util/time.d.ts +1 -0
- package/dist/lib/util/time.d.ts.map +1 -1
- package/dist/lib/util/time.js +6 -11
- package/dist/lib/util/time.js.map +1 -1
- package/dist/lib/util/tlv.d.ts +1 -0
- package/dist/lib/util/tlv.d.ts.map +1 -1
- package/dist/lib/util/tlv.js +28 -43
- package/dist/lib/util/tlv.js.map +1 -1
- package/dist/lib/util/uuid.d.ts +1 -0
- package/dist/lib/util/uuid.d.ts.map +1 -1
- package/dist/lib/util/uuid.js +26 -38
- package/dist/lib/util/uuid.js.map +1 -1
- package/dist/types.d.ts +24 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -5
- package/dist/types.js.map +1 -1
- package/package.json +51 -46
- package/@types/simple-plist.d.ts +0 -4
- package/dist/lib/gen/HomeKit.d.ts +0 -7
- package/dist/lib/gen/HomeKit.d.ts.map +0 -1
- package/dist/lib/gen/HomeKit.js +0 -8
- package/dist/lib/gen/HomeKit.js.map +0 -1
package/dist/lib/Accessory.js
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const request_util_1 = require("./util/request-util");
|
|
21
|
-
const uuid = tslib_1.__importStar(require("./util/uuid"));
|
|
22
|
-
const uuid_1 = require("./util/uuid");
|
|
23
|
-
const checkName_1 = require("./util/checkName");
|
|
24
|
-
const debug = (0, debug_1.default)("HAP-NodeJS:Accessory");
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { EventEmitter } from 'node:events';
|
|
5
|
+
import { isIP } from 'node:net';
|
|
6
|
+
import createDebug from 'debug';
|
|
7
|
+
import { AvahiAdvertiser, BonjourHAPAdvertiser, CiaoAdvertiser, ResolvedAdvertiser } from './Advertiser.js';
|
|
8
|
+
import { Characteristic, } from './Characteristic.js';
|
|
9
|
+
import { CameraController, isSerializableController } from './controller/index.js';
|
|
10
|
+
import { HAPServer, } from './HAPServer.js';
|
|
11
|
+
import { AccessoryInfo } from './model/AccessoryInfo.js';
|
|
12
|
+
import { ControllerStorage } from './model/ControllerStorage.js';
|
|
13
|
+
import { IdentifierCache } from './model/IdentifierCache.js';
|
|
14
|
+
import { Service } from './Service.js';
|
|
15
|
+
import { checkName } from './util/checkName.js';
|
|
16
|
+
import { clone } from './util/clone.js';
|
|
17
|
+
import { formatOutgoingCharacteristicValue } from './util/request-util.js';
|
|
18
|
+
import { isValid, toShortForm } from './util/uuid.js';
|
|
19
|
+
const debug = createDebug('HAP-NodeJS:Accessory');
|
|
25
20
|
const MAX_ACCESSORIES = 149; // Maximum number of bridged accessories per bridge.
|
|
26
21
|
const MAX_SERVICES = 100;
|
|
27
22
|
/**
|
|
@@ -29,9 +24,9 @@ const MAX_SERVICES = 100;
|
|
|
29
24
|
*
|
|
30
25
|
* @group Accessory
|
|
31
26
|
*/
|
|
32
|
-
|
|
27
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
28
|
+
export var Categories;
|
|
33
29
|
(function (Categories) {
|
|
34
|
-
// noinspection JSUnusedGlobalSymbols
|
|
35
30
|
Categories[Categories["OTHER"] = 1] = "OTHER";
|
|
36
31
|
Categories[Categories["BRIDGE"] = 2] = "BRIDGE";
|
|
37
32
|
Categories[Categories["FAN"] = 3] = "FAN";
|
|
@@ -43,7 +38,7 @@ var Categories;
|
|
|
43
38
|
Categories[Categories["THERMOSTAT"] = 9] = "THERMOSTAT";
|
|
44
39
|
Categories[Categories["SENSOR"] = 10] = "SENSOR";
|
|
45
40
|
Categories[Categories["ALARM_SYSTEM"] = 11] = "ALARM_SYSTEM";
|
|
46
|
-
// eslint-disable-next-line
|
|
41
|
+
// eslint-disable-next-line ts/no-duplicate-enum-values
|
|
47
42
|
Categories[Categories["SECURITY_SYSTEM"] = 11] = "SECURITY_SYSTEM";
|
|
48
43
|
Categories[Categories["DOOR"] = 12] = "DOOR";
|
|
49
44
|
Categories[Categories["WINDOW"] = 13] = "WINDOW";
|
|
@@ -51,7 +46,7 @@ var Categories;
|
|
|
51
46
|
Categories[Categories["PROGRAMMABLE_SWITCH"] = 15] = "PROGRAMMABLE_SWITCH";
|
|
52
47
|
Categories[Categories["RANGE_EXTENDER"] = 16] = "RANGE_EXTENDER";
|
|
53
48
|
Categories[Categories["CAMERA"] = 17] = "CAMERA";
|
|
54
|
-
// eslint-disable-next-line
|
|
49
|
+
// eslint-disable-next-line ts/no-duplicate-enum-values
|
|
55
50
|
Categories[Categories["IP_CAMERA"] = 17] = "IP_CAMERA";
|
|
56
51
|
Categories[Categories["VIDEO_DOORBELL"] = 18] = "VIDEO_DOORBELL";
|
|
57
52
|
Categories[Categories["AIR_PURIFIER"] = 19] = "AIR_PURIFIER";
|
|
@@ -72,11 +67,12 @@ var Categories;
|
|
|
72
67
|
Categories[Categories["AUDIO_RECEIVER"] = 34] = "AUDIO_RECEIVER";
|
|
73
68
|
Categories[Categories["TV_SET_TOP_BOX"] = 35] = "TV_SET_TOP_BOX";
|
|
74
69
|
Categories[Categories["TV_STREAMING_STICK"] = 36] = "TV_STREAMING_STICK";
|
|
75
|
-
})(Categories || (
|
|
70
|
+
})(Categories || (Categories = {}));
|
|
76
71
|
/**
|
|
77
72
|
* @group Accessory
|
|
78
73
|
*/
|
|
79
|
-
|
|
74
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
75
|
+
export var CharacteristicWarningType;
|
|
80
76
|
(function (CharacteristicWarningType) {
|
|
81
77
|
CharacteristicWarningType["SLOW_WRITE"] = "slow-write";
|
|
82
78
|
CharacteristicWarningType["TIMEOUT_WRITE"] = "timeout-write";
|
|
@@ -85,11 +81,12 @@ var CharacteristicWarningType;
|
|
|
85
81
|
CharacteristicWarningType["WARN_MESSAGE"] = "warn-message";
|
|
86
82
|
CharacteristicWarningType["ERROR_MESSAGE"] = "error-message";
|
|
87
83
|
CharacteristicWarningType["DEBUG_MESSAGE"] = "debug-message";
|
|
88
|
-
})(CharacteristicWarningType || (
|
|
84
|
+
})(CharacteristicWarningType || (CharacteristicWarningType = {}));
|
|
89
85
|
/**
|
|
90
86
|
* @group Accessory
|
|
91
87
|
*/
|
|
92
|
-
|
|
88
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
89
|
+
export var MDNSAdvertiser;
|
|
93
90
|
(function (MDNSAdvertiser) {
|
|
94
91
|
/**
|
|
95
92
|
* Use the `@homebridge/ciao` module as advertiser.
|
|
@@ -111,7 +108,8 @@ var MDNSAdvertiser;
|
|
|
111
108
|
* Consequentially, treat this feature as an experimental feature.
|
|
112
109
|
*/
|
|
113
110
|
MDNSAdvertiser["RESOLVED"] = "resolved";
|
|
114
|
-
})(MDNSAdvertiser || (
|
|
111
|
+
})(MDNSAdvertiser || (MDNSAdvertiser = {}));
|
|
112
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
115
113
|
var WriteRequestState;
|
|
116
114
|
(function (WriteRequestState) {
|
|
117
115
|
WriteRequestState[WriteRequestState["REGULAR_REQUEST"] = 0] = "REGULAR_REQUEST";
|
|
@@ -121,7 +119,8 @@ var WriteRequestState;
|
|
|
121
119
|
/**
|
|
122
120
|
* @group Accessory
|
|
123
121
|
*/
|
|
124
|
-
|
|
122
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
123
|
+
export var AccessoryEventTypes;
|
|
125
124
|
(function (AccessoryEventTypes) {
|
|
126
125
|
/**
|
|
127
126
|
* Emitted when an iOS device wishes for this Accessory to identify itself. If `paired` is false, then
|
|
@@ -150,7 +149,7 @@ var AccessoryEventTypes;
|
|
|
150
149
|
AccessoryEventTypes["PAIRED"] = "paired";
|
|
151
150
|
AccessoryEventTypes["UNPAIRED"] = "unpaired";
|
|
152
151
|
AccessoryEventTypes["CHARACTERISTIC_WARNING"] = "characteristic-warning";
|
|
153
|
-
})(AccessoryEventTypes || (
|
|
152
|
+
})(AccessoryEventTypes || (AccessoryEventTypes = {}));
|
|
154
153
|
/**
|
|
155
154
|
* Accessory is a virtual HomeKit device. It can publish an associated HAP server for iOS devices to communicate
|
|
156
155
|
* with - or it can run behind another "Bridge" Accessory server.
|
|
@@ -162,8 +161,8 @@ var AccessoryEventTypes;
|
|
|
162
161
|
*
|
|
163
162
|
* @group Accessory
|
|
164
163
|
*/
|
|
165
|
-
// eslint-disable-next-line
|
|
166
|
-
class Accessory extends
|
|
164
|
+
// eslint-disable-next-line ts/no-unsafe-declaration-merging
|
|
165
|
+
export class Accessory extends EventEmitter {
|
|
167
166
|
displayName;
|
|
168
167
|
UUID;
|
|
169
168
|
// Timeout in milliseconds until a characteristic warning is issue
|
|
@@ -185,38 +184,38 @@ class Accessory extends events_1.EventEmitter {
|
|
|
185
184
|
/**
|
|
186
185
|
* Captures if initialization steps inside {@link publish} have been called.
|
|
187
186
|
* This is important when calling {@link publish} multiple times (e.g. after calling {@link unpublish}).
|
|
188
|
-
* @private
|
|
187
|
+
* @private
|
|
189
188
|
*/
|
|
190
189
|
initialized = false;
|
|
191
190
|
controllers = {};
|
|
192
191
|
serializedControllers; // store uninitialized controller data after a Accessory.deserialize call
|
|
193
192
|
activeCameraController;
|
|
194
193
|
/**
|
|
195
|
-
* @private
|
|
194
|
+
* @private
|
|
196
195
|
*/
|
|
197
196
|
_accessoryInfo;
|
|
198
197
|
/**
|
|
199
|
-
* @private
|
|
198
|
+
* @private
|
|
200
199
|
*/
|
|
201
200
|
_setupID = null;
|
|
202
201
|
/**
|
|
203
|
-
* @private
|
|
202
|
+
* @private
|
|
204
203
|
*/
|
|
205
204
|
_identifierCache;
|
|
206
205
|
/**
|
|
207
|
-
* @private
|
|
206
|
+
* @private
|
|
208
207
|
*/
|
|
209
|
-
controllerStorage = new
|
|
208
|
+
controllerStorage = new ControllerStorage(this);
|
|
210
209
|
/**
|
|
211
|
-
* @private
|
|
210
|
+
* @private
|
|
212
211
|
*/
|
|
213
212
|
_advertiser;
|
|
214
213
|
/**
|
|
215
|
-
* @private
|
|
214
|
+
* @private
|
|
216
215
|
*/
|
|
217
216
|
_server;
|
|
218
217
|
/**
|
|
219
|
-
* @private
|
|
218
|
+
* @private
|
|
220
219
|
*/
|
|
221
220
|
_setupURI;
|
|
222
221
|
configurationChangeDebounceTimeout;
|
|
@@ -229,18 +228,18 @@ class Accessory extends events_1.EventEmitter {
|
|
|
229
228
|
super();
|
|
230
229
|
this.displayName = displayName;
|
|
231
230
|
this.UUID = UUID;
|
|
232
|
-
(
|
|
233
|
-
(
|
|
234
|
-
(
|
|
235
|
-
|
|
231
|
+
assert(displayName, 'Accessories must be created with a non-empty displayName.');
|
|
232
|
+
assert(UUID, 'Accessories must be created with a valid UUID.');
|
|
233
|
+
assert(isValid(UUID), `UUID '${UUID}' is not a valid UUID. Try using the provided 'generateUUID' function to create a `
|
|
234
|
+
+ `valid UUID from any arbitrary string, like a serial number.`);
|
|
236
235
|
// create our initial "Accessory Information" Service that all Accessories are expected to have
|
|
237
|
-
|
|
238
|
-
this.addService(
|
|
239
|
-
.setCharacteristic(
|
|
236
|
+
checkName(this.displayName, 'Name', displayName);
|
|
237
|
+
this.addService(Service.AccessoryInformation)
|
|
238
|
+
.setCharacteristic(Characteristic.Name, displayName);
|
|
240
239
|
// sign up for when iOS attempts to "set" the `Identify` characteristic - this means a paired device wishes
|
|
241
240
|
// for us to identify ourselves (as opposed to an unpaired device - that case is handled by HAPServer 'identify' event)
|
|
242
|
-
this.getService(
|
|
243
|
-
.getCharacteristic(
|
|
241
|
+
this.getService(Service.AccessoryInformation)
|
|
242
|
+
.getCharacteristic(Characteristic.Identify)
|
|
244
243
|
.on("set" /* CharacteristicEventTypes.SET */, (value, callback) => {
|
|
245
244
|
if (value) {
|
|
246
245
|
const paired = true;
|
|
@@ -249,40 +248,37 @@ class Accessory extends events_1.EventEmitter {
|
|
|
249
248
|
});
|
|
250
249
|
}
|
|
251
250
|
identificationRequest(paired, callback) {
|
|
252
|
-
debug(
|
|
251
|
+
debug('[%s] Identification request', this.displayName);
|
|
253
252
|
if (this.listeners("identify" /* AccessoryEventTypes.IDENTIFY */).length > 0) {
|
|
254
253
|
// allow implementors to identify this Accessory in whatever way is appropriate, and pass along
|
|
255
254
|
// the standard callback for completion.
|
|
256
255
|
this.emit("identify" /* AccessoryEventTypes.IDENTIFY */, paired, callback);
|
|
257
256
|
}
|
|
258
257
|
else {
|
|
259
|
-
debug(
|
|
258
|
+
debug('[%s] Identification request ignored; no listeners to \'identify\' event', this.displayName);
|
|
260
259
|
callback();
|
|
261
260
|
}
|
|
262
261
|
}
|
|
263
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
264
262
|
addService(serviceParam, ...constructorArgs) {
|
|
265
263
|
// service might be a constructor like `Service.AccessoryInformation` instead of an instance
|
|
266
264
|
// of Service. Coerce if necessary.
|
|
267
|
-
const service = typeof serviceParam ===
|
|
268
|
-
? new serviceParam(constructorArgs[0], constructorArgs[1], constructorArgs[2])
|
|
265
|
+
const service = typeof serviceParam === 'function'
|
|
266
|
+
? new serviceParam(constructorArgs[0], constructorArgs[1], constructorArgs[2]) // eslint-disable-line new-cap
|
|
269
267
|
: serviceParam;
|
|
270
268
|
// check for UUID+subtype conflict
|
|
271
269
|
for (const existing of this.services) {
|
|
272
270
|
if (existing.UUID === service.UUID) {
|
|
273
271
|
// OK we have two Services with the same UUID. Check that each defines a `subtype` property and that each is unique.
|
|
274
272
|
if (!service.subtype) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
"' as another Service in this Accessory without also defining a unique 'subtype' property.");
|
|
273
|
+
throw new Error(`Cannot add a Service with the same UUID '${existing.UUID}' as another Service in this Accessory without also defining a unique 'subtype' property.`);
|
|
277
274
|
}
|
|
278
275
|
if (service.subtype === existing.subtype) {
|
|
279
|
-
throw new Error(
|
|
280
|
-
"' and subtype '" + existing.subtype + "' as another Service in this Accessory.");
|
|
276
|
+
throw new Error(`Cannot add a Service with the same UUID '${existing.UUID}' and subtype '${existing.subtype}' as another Service in this Accessory.`);
|
|
281
277
|
}
|
|
282
278
|
}
|
|
283
279
|
}
|
|
284
280
|
if (this.services.length >= MAX_SERVICES) {
|
|
285
|
-
throw new Error(
|
|
281
|
+
throw new Error(`Cannot add more than ${MAX_SERVICES} services to a single accessory!`);
|
|
286
282
|
}
|
|
287
283
|
this.services.push(service);
|
|
288
284
|
if (service.isPrimaryService) { // check if a primary service was added
|
|
@@ -295,7 +291,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
295
291
|
this.enqueueConfigurationUpdate();
|
|
296
292
|
}
|
|
297
293
|
else {
|
|
298
|
-
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service
|
|
294
|
+
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service });
|
|
299
295
|
}
|
|
300
296
|
this.setupServiceEventHandlers(service);
|
|
301
297
|
return service;
|
|
@@ -312,7 +308,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
312
308
|
this.enqueueConfigurationUpdate();
|
|
313
309
|
}
|
|
314
310
|
else {
|
|
315
|
-
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service
|
|
311
|
+
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service });
|
|
316
312
|
}
|
|
317
313
|
service.removeAllListeners();
|
|
318
314
|
}
|
|
@@ -324,12 +320,12 @@ class Accessory extends events_1.EventEmitter {
|
|
|
324
320
|
}
|
|
325
321
|
getService(name) {
|
|
326
322
|
for (const service of this.services) {
|
|
327
|
-
if (typeof name ===
|
|
323
|
+
if (typeof name === 'string' && (service.displayName === name || service.name === name || service.subtype === name)) {
|
|
328
324
|
return service;
|
|
329
325
|
}
|
|
330
326
|
else {
|
|
331
327
|
// @ts-expect-error ('UUID' does not exist on type 'never')
|
|
332
|
-
if (typeof name ===
|
|
328
|
+
if (typeof name === 'function' && ((service instanceof name) || (name.UUID === service.UUID))) {
|
|
333
329
|
return service;
|
|
334
330
|
}
|
|
335
331
|
}
|
|
@@ -338,12 +334,12 @@ class Accessory extends events_1.EventEmitter {
|
|
|
338
334
|
}
|
|
339
335
|
getServiceById(uuid, subType) {
|
|
340
336
|
for (const service of this.services) {
|
|
341
|
-
if (typeof uuid ===
|
|
337
|
+
if (typeof uuid === 'string' && (service.displayName === uuid || service.name === uuid) && service.subtype === subType) {
|
|
342
338
|
return service;
|
|
343
339
|
}
|
|
344
340
|
else {
|
|
345
341
|
// @ts-expect-error ('UUID' does not exist on type 'never')
|
|
346
|
-
if (typeof uuid ===
|
|
342
|
+
if (typeof uuid === 'function' && ((service instanceof uuid) || (uuid.UUID === service.UUID)) && service.subtype === subType) {
|
|
347
343
|
return service;
|
|
348
344
|
}
|
|
349
345
|
}
|
|
@@ -361,17 +357,17 @@ class Accessory extends events_1.EventEmitter {
|
|
|
361
357
|
};
|
|
362
358
|
addBridgedAccessory(accessory, deferUpdate = false) {
|
|
363
359
|
if (accessory._isBridge || accessory === this) {
|
|
364
|
-
throw new Error(
|
|
360
|
+
throw new Error('Illegal state: either trying to bridge a bridge or trying to bridge itself!');
|
|
365
361
|
}
|
|
366
362
|
if (accessory.initialized) {
|
|
367
|
-
throw new Error(
|
|
363
|
+
throw new Error('Tried to bridge an accessory which was already published once!');
|
|
368
364
|
}
|
|
369
365
|
if (accessory.bridge != null) {
|
|
370
366
|
// this also prevents that we bridge the same accessory twice!
|
|
371
|
-
throw new Error(
|
|
367
|
+
throw new Error(`Tried to bridge ${accessory.displayName} while it was already bridged by ${accessory.bridge.displayName}`);
|
|
372
368
|
}
|
|
373
369
|
if (this.bridgedAccessories.length >= MAX_ACCESSORIES) {
|
|
374
|
-
throw new Error(
|
|
370
|
+
throw new Error(`Cannot Bridge more than ${MAX_ACCESSORIES} Accessories`);
|
|
375
371
|
}
|
|
376
372
|
// listen for changes in ANY characteristics of ANY services on this Accessory
|
|
377
373
|
accessory.on("service-characteristic-change" /* AccessoryEventTypes.SERVICE_CHARACTERISTIC_CHANGE */, change => this.handleCharacteristicChangeEvent(accessory, change.service, change));
|
|
@@ -396,7 +392,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
396
392
|
// check for UUID conflict
|
|
397
393
|
const accessoryIndex = this.bridgedAccessories.indexOf(accessory);
|
|
398
394
|
if (accessoryIndex === -1) {
|
|
399
|
-
throw new Error(
|
|
395
|
+
throw new Error('Cannot find the bridged Accessory to remove.');
|
|
400
396
|
}
|
|
401
397
|
this.bridgedAccessories.splice(accessoryIndex, 1);
|
|
402
398
|
accessory.bridged = false;
|
|
@@ -451,8 +447,9 @@ class Accessory extends events_1.EventEmitter {
|
|
|
451
447
|
* @param controllerConstructor - The Controller instance or constructor to the Controller with no required arguments.
|
|
452
448
|
*/
|
|
453
449
|
configureController(controllerConstructor) {
|
|
454
|
-
|
|
455
|
-
|
|
450
|
+
// any custom constructor arguments should be passed before using .bind(...)
|
|
451
|
+
const controller = typeof controllerConstructor === 'function'
|
|
452
|
+
? new controllerConstructor() // eslint-disable-line new-cap
|
|
456
453
|
: controllerConstructor;
|
|
457
454
|
const id = controller.controllerId();
|
|
458
455
|
if (this.controllers[id]) {
|
|
@@ -461,7 +458,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
461
458
|
const savedServiceMap = this.serializedControllers && this.serializedControllers[id];
|
|
462
459
|
let serviceMap;
|
|
463
460
|
if (savedServiceMap) { // we found data to restore from
|
|
464
|
-
const clonedServiceMap =
|
|
461
|
+
const clonedServiceMap = clone(savedServiceMap);
|
|
465
462
|
const updatedServiceMap = controller.initWithServices(savedServiceMap); // init controller with existing services
|
|
466
463
|
serviceMap = updatedServiceMap || savedServiceMap; // initWithServices could return an updated serviceMap, otherwise just use the existing one
|
|
467
464
|
if (updatedServiceMap) { // controller returned a ServiceMap and thus signaled an updated set of services
|
|
@@ -478,7 +475,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
478
475
|
else {
|
|
479
476
|
serviceMap = controller.constructServices(); // let the controller create his services
|
|
480
477
|
controller.configureServices(); // let the controller setup all its handlers
|
|
481
|
-
Object.values(serviceMap).forEach(service => {
|
|
478
|
+
Object.values(serviceMap).forEach((service) => {
|
|
482
479
|
if (service && !this.services.includes(service)) {
|
|
483
480
|
this.addService(service);
|
|
484
481
|
}
|
|
@@ -486,14 +483,14 @@ class Accessory extends events_1.EventEmitter {
|
|
|
486
483
|
}
|
|
487
484
|
// --- init handlers and setup context ---
|
|
488
485
|
const context = {
|
|
489
|
-
controller
|
|
490
|
-
serviceMap
|
|
486
|
+
controller,
|
|
487
|
+
serviceMap,
|
|
491
488
|
};
|
|
492
|
-
if (
|
|
489
|
+
if (isSerializableController(controller)) {
|
|
493
490
|
this.controllerStorage.trackController(controller);
|
|
494
491
|
}
|
|
495
492
|
this.controllers[id] = context;
|
|
496
|
-
if (controller instanceof
|
|
493
|
+
if (controller instanceof CameraController) { // save CameraController for Snapshot handling
|
|
497
494
|
this.activeCameraController = controller;
|
|
498
495
|
}
|
|
499
496
|
}
|
|
@@ -509,10 +506,9 @@ class Accessory extends events_1.EventEmitter {
|
|
|
509
506
|
const storedController = this.controllers[id];
|
|
510
507
|
if (storedController) {
|
|
511
508
|
if (storedController.controller !== controller) {
|
|
512
|
-
throw new Error(
|
|
513
|
-
"' though provided controller isn't the same instance that is registered!");
|
|
509
|
+
throw new Error(`[${this.displayName}] tried removing a controller with the id/type '${id}' though provided controller isn't the same instance that is registered!`);
|
|
514
510
|
}
|
|
515
|
-
if (
|
|
511
|
+
if (isSerializableController(controller)) {
|
|
516
512
|
// this will reset the state change delegate before we call handleControllerRemoved()
|
|
517
513
|
this.controllerStorage.untrackController(controller);
|
|
518
514
|
}
|
|
@@ -524,7 +520,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
524
520
|
if (this.activeCameraController === controller) {
|
|
525
521
|
this.activeCameraController = undefined;
|
|
526
522
|
}
|
|
527
|
-
Object.values(storedController.serviceMap).forEach(service => {
|
|
523
|
+
Object.values(storedController.serviceMap).forEach((service) => {
|
|
528
524
|
if (service) {
|
|
529
525
|
this.removeService(service);
|
|
530
526
|
}
|
|
@@ -540,14 +536,14 @@ class Accessory extends events_1.EventEmitter {
|
|
|
540
536
|
if (controller.handleFactoryReset) { // if the controller implements handleFactoryReset, setup event handlers for this controller
|
|
541
537
|
controller.handleFactoryReset();
|
|
542
538
|
}
|
|
543
|
-
if (
|
|
539
|
+
if (isSerializableController(controller)) {
|
|
544
540
|
this.controllerStorage.purgeControllerData(controller);
|
|
545
541
|
}
|
|
546
542
|
}
|
|
547
543
|
}
|
|
548
544
|
handleUpdatedControllerServiceMap(originalServiceMap, updatedServiceMap) {
|
|
549
|
-
updatedServiceMap =
|
|
550
|
-
Object.keys(originalServiceMap).forEach(name => {
|
|
545
|
+
updatedServiceMap = clone(updatedServiceMap); // clone it so we can alter it
|
|
546
|
+
Object.keys(originalServiceMap).forEach((name) => {
|
|
551
547
|
const service = originalServiceMap[name];
|
|
552
548
|
const updatedService = updatedServiceMap[name];
|
|
553
549
|
if (service && updatedService) { // we check all names contained in both ServiceMaps for changes
|
|
@@ -560,12 +556,12 @@ class Accessory extends events_1.EventEmitter {
|
|
|
560
556
|
}
|
|
561
557
|
});
|
|
562
558
|
// now originalServiceMap contains only deleted services and updateServiceMap only added services
|
|
563
|
-
Object.values(originalServiceMap).forEach(service => {
|
|
559
|
+
Object.values(originalServiceMap).forEach((service) => {
|
|
564
560
|
if (service) {
|
|
565
561
|
this.removeService(service);
|
|
566
562
|
}
|
|
567
563
|
});
|
|
568
|
-
Object.values(updatedServiceMap).forEach(service => {
|
|
564
|
+
Object.values(updatedServiceMap).forEach((service) => {
|
|
569
565
|
if (service) {
|
|
570
566
|
this.addService(service);
|
|
571
567
|
}
|
|
@@ -575,23 +571,23 @@ class Accessory extends events_1.EventEmitter {
|
|
|
575
571
|
if (this._setupURI) {
|
|
576
572
|
return this._setupURI;
|
|
577
573
|
}
|
|
578
|
-
(
|
|
574
|
+
assert(!!this._accessoryInfo, 'Cannot generate setupURI on an accessory that isn\'t published yet!');
|
|
579
575
|
const buffer = Buffer.alloc(8);
|
|
580
|
-
let value_low = parseInt(this._accessoryInfo.pincode.replace(/-/g,
|
|
576
|
+
let value_low = Number.parseInt(this._accessoryInfo.pincode.replace(/-/g, ''), 10);
|
|
581
577
|
const value_high = this._accessoryInfo.category >> 1;
|
|
582
578
|
value_low |= 1 << 28; // Supports IP;
|
|
583
579
|
buffer.writeUInt32BE(value_low, 4);
|
|
584
|
-
if (this._accessoryInfo.category
|
|
585
|
-
buffer[4]
|
|
580
|
+
if (this._accessoryInfo.category % 2 !== 0) { // check if the category is odd
|
|
581
|
+
buffer[4] += 128; // set the 7th bit by adding 128
|
|
586
582
|
}
|
|
587
583
|
buffer.writeUInt32BE(value_high, 0);
|
|
588
584
|
let encodedPayload = (buffer.readUInt32BE(4) + (buffer.readUInt32BE(0) * 0x100000000)).toString(36).toUpperCase();
|
|
589
585
|
if (encodedPayload.length !== 9) {
|
|
590
586
|
for (let i = 0; i <= 9 - encodedPayload.length; i++) {
|
|
591
|
-
encodedPayload =
|
|
587
|
+
encodedPayload = `0${encodedPayload}`;
|
|
592
588
|
}
|
|
593
589
|
}
|
|
594
|
-
this._setupURI =
|
|
590
|
+
this._setupURI = `X-HM://${encodedPayload}${this._setupID}`;
|
|
595
591
|
return this._setupURI;
|
|
596
592
|
}
|
|
597
593
|
/**
|
|
@@ -600,30 +596,31 @@ class Accessory extends events_1.EventEmitter {
|
|
|
600
596
|
* If it is called on a bridge it will call this method for all bridged accessories.
|
|
601
597
|
*/
|
|
602
598
|
validateAccessory(mainAccessory) {
|
|
603
|
-
const service = this.getService(
|
|
599
|
+
const service = this.getService(Service.AccessoryInformation);
|
|
604
600
|
if (!service) {
|
|
605
|
-
|
|
606
|
-
|
|
601
|
+
// eslint-disable-next-line no-console
|
|
602
|
+
console.log(`HAP-NodeJS WARNING: The accessory '${this.displayName}' is getting published without a AccessoryInformation service. `
|
|
603
|
+
+ `This might prevent the accessory from being added to the Home app or leading to the accessory being unresponsive!`);
|
|
607
604
|
}
|
|
608
605
|
else {
|
|
609
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
610
606
|
const checkValue = (name, value) => {
|
|
611
607
|
if (!value) {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
608
|
+
// eslint-disable-next-line no-console
|
|
609
|
+
console.log(`HAP-NodeJS WARNING: The accessory '${this.displayName}' is getting published with the characteristic '${name}'`
|
|
610
|
+
+ ` (of the AccessoryInformation service) not having a value set. `
|
|
611
|
+
+ `This might prevent the accessory from being added to the Home App or leading to the accessory being unresponsive!`);
|
|
615
612
|
}
|
|
616
613
|
};
|
|
617
|
-
|
|
618
|
-
checkValue(
|
|
619
|
-
checkValue(
|
|
620
|
-
checkValue(
|
|
621
|
-
checkValue(
|
|
622
|
-
checkValue(
|
|
614
|
+
checkName(this.displayName, 'Name', service.getCharacteristic(Characteristic.Name).value);
|
|
615
|
+
checkValue('FirmwareRevision', service.getCharacteristic(Characteristic.FirmwareRevision).value);
|
|
616
|
+
checkValue('Manufacturer', service.getCharacteristic(Characteristic.Manufacturer).value);
|
|
617
|
+
checkValue('Model', service.getCharacteristic(Characteristic.Model).value);
|
|
618
|
+
checkValue('Name', service.getCharacteristic(Characteristic.Name).value);
|
|
619
|
+
checkValue('SerialNumber', service.getCharacteristic(Characteristic.SerialNumber).value);
|
|
623
620
|
}
|
|
624
621
|
if (mainAccessory) {
|
|
625
622
|
// the main accessory which is advertised via bonjour must have a name with length <= 63 (limitation of DNS FQDN names)
|
|
626
|
-
(
|
|
623
|
+
assert(Buffer.from(this.displayName, 'utf8').length <= 63, 'Accessory displayName cannot be longer than 63 bytes!');
|
|
627
624
|
}
|
|
628
625
|
if (this.bridged) {
|
|
629
626
|
this.bridgedAccessories.forEach(accessory => accessory.validateAccessory());
|
|
@@ -632,7 +629,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
632
629
|
/**
|
|
633
630
|
* Assigns aid/iid to ourselves, any Accessories we are bridging, and all associated Services+Characteristics. Uses
|
|
634
631
|
* the provided identifierCache to keep IDs stable.
|
|
635
|
-
* @private
|
|
632
|
+
* @private
|
|
636
633
|
*/
|
|
637
634
|
_assignIDs(identifierCache) {
|
|
638
635
|
// if we are responsible for our own identifierCache, start the expiration process
|
|
@@ -665,11 +662,11 @@ class Accessory extends events_1.EventEmitter {
|
|
|
665
662
|
// expire any now-unused cache keys (for Accessories, Services, or Characteristics
|
|
666
663
|
// that have been removed since the last call to assignIDs())
|
|
667
664
|
if (this._identifierCache) {
|
|
668
|
-
//Check weather we want to purge the unused ids
|
|
665
|
+
// Check weather we want to purge the unused ids
|
|
669
666
|
if (this.shouldPurgeUnusedIDs) {
|
|
670
667
|
this._identifierCache.stopTrackingUsageAndExpireUnused();
|
|
671
668
|
}
|
|
672
|
-
//Save in case we have new ones
|
|
669
|
+
// Save in case we have new ones
|
|
673
670
|
this._identifierCache.save();
|
|
674
671
|
}
|
|
675
672
|
}
|
|
@@ -684,10 +681,10 @@ class Accessory extends events_1.EventEmitter {
|
|
|
684
681
|
* when you have disabled auto purge, so you can do it manually
|
|
685
682
|
*/
|
|
686
683
|
purgeUnusedIDs() {
|
|
687
|
-
//Cache the state of the purge mechanism and set it to true
|
|
684
|
+
// Cache the state of the purge mechanism and set it to true
|
|
688
685
|
const oldValue = this.shouldPurgeUnusedIDs;
|
|
689
686
|
this.shouldPurgeUnusedIDs = true;
|
|
690
|
-
//Reassign all ids
|
|
687
|
+
// Reassign all ids
|
|
691
688
|
this._assignIDs(this._identifierCache);
|
|
692
689
|
// Revert the purge mechanism state
|
|
693
690
|
this.shouldPurgeUnusedIDs = oldValue;
|
|
@@ -696,8 +693,8 @@ class Accessory extends events_1.EventEmitter {
|
|
|
696
693
|
* Returns a JSON representation of this accessory suitable for delivering to HAP clients.
|
|
697
694
|
*/
|
|
698
695
|
async toHAP(connection, contactGetHandlers = true) {
|
|
699
|
-
(
|
|
700
|
-
(
|
|
696
|
+
assert(this.aid, `aid cannot be undefined for accessory '${this.displayName}'`);
|
|
697
|
+
assert(this.services.length, `accessory '${this.displayName}' does not have any services!`);
|
|
701
698
|
const accessory = {
|
|
702
699
|
aid: this.aid,
|
|
703
700
|
services: await Promise.all(this.services.map(service => service.toHAP(connection, contactGetHandlers))),
|
|
@@ -716,8 +713,8 @@ class Accessory extends events_1.EventEmitter {
|
|
|
716
713
|
if (assignIds) {
|
|
717
714
|
this._assignIDs(this._identifierCache); // make sure our aid/iid's are all assigned
|
|
718
715
|
}
|
|
719
|
-
(
|
|
720
|
-
(
|
|
716
|
+
assert(this.aid, `aid cannot be undefined for accessory '${this.displayName}'`);
|
|
717
|
+
assert(this.services.length, `accessory '${this.displayName}' does not have any services!`);
|
|
721
718
|
const accessory = {
|
|
722
719
|
aid: this.aid,
|
|
723
720
|
services: this.services.map(service => service.internalHAPRepresentation()),
|
|
@@ -743,40 +740,40 @@ class Accessory extends events_1.EventEmitter {
|
|
|
743
740
|
* @param {{
|
|
744
741
|
* username: string;
|
|
745
742
|
* pincode: string;
|
|
746
|
-
* category:
|
|
743
|
+
* category: Categories;
|
|
747
744
|
* }} info - Required info for publishing.
|
|
748
745
|
* @param {boolean} allowInsecureRequest - Will allow unencrypted and unauthenticated access to the http server
|
|
749
746
|
*/
|
|
750
747
|
async publish(info, allowInsecureRequest) {
|
|
751
748
|
if (this.bridged) {
|
|
752
|
-
throw new Error(
|
|
749
|
+
throw new Error(`Can't publish in accessory which is bridged by another accessory. Bridged by ${this.bridge?.displayName}`);
|
|
753
750
|
}
|
|
754
|
-
let service = this.getService(
|
|
751
|
+
let service = this.getService(Service.ProtocolInformation);
|
|
755
752
|
if (!service) {
|
|
756
|
-
service = this.addService(
|
|
753
|
+
service = this.addService(Service.ProtocolInformation); // add the protocol information service to the primary accessory
|
|
757
754
|
}
|
|
758
|
-
service.setCharacteristic(
|
|
755
|
+
service.setCharacteristic(Characteristic.Version, CiaoAdvertiser.protocolVersionService);
|
|
759
756
|
if (this.lastKnownUsername && this.lastKnownUsername !== info.username) { // username changed since last publish
|
|
760
757
|
Accessory.cleanupAccessoryData(this.lastKnownUsername); // delete old Accessory data
|
|
761
758
|
}
|
|
762
759
|
if (!this.initialized && (info.addIdentifyingMaterial ?? true)) {
|
|
763
760
|
// adding some identifying material to our displayName if it's our first publish() call
|
|
764
|
-
this.displayName = this.displayName
|
|
765
|
-
.update(info.username,
|
|
766
|
-
.digest(
|
|
767
|
-
this.getService(
|
|
761
|
+
this.displayName = `${this.displayName} ${createHash('sha512')
|
|
762
|
+
.update(info.username, 'utf8')
|
|
763
|
+
.digest('hex').slice(0, 4).toUpperCase()}`;
|
|
764
|
+
this.getService(Service.AccessoryInformation).updateCharacteristic(Characteristic.Name, this.displayName);
|
|
768
765
|
}
|
|
769
766
|
// attempt to load existing AccessoryInfo from disk
|
|
770
|
-
this._accessoryInfo =
|
|
767
|
+
this._accessoryInfo = AccessoryInfo.load(info.username);
|
|
771
768
|
// if we don't have one, create a new one.
|
|
772
769
|
if (!this._accessoryInfo) {
|
|
773
|
-
debug(
|
|
774
|
-
this._accessoryInfo =
|
|
770
|
+
debug('[%s] Creating new AccessoryInfo for our HAP server', this.displayName);
|
|
771
|
+
this._accessoryInfo = AccessoryInfo.create(info.username);
|
|
775
772
|
}
|
|
776
773
|
if (info.setupID) {
|
|
777
774
|
this._setupID = info.setupID;
|
|
778
775
|
}
|
|
779
|
-
else if (this._accessoryInfo.setupID === undefined || this._accessoryInfo.setupID ===
|
|
776
|
+
else if (this._accessoryInfo.setupID === undefined || this._accessoryInfo.setupID === '') {
|
|
780
777
|
this._setupID = Accessory._generateSetupID();
|
|
781
778
|
}
|
|
782
779
|
else {
|
|
@@ -785,16 +782,16 @@ class Accessory extends events_1.EventEmitter {
|
|
|
785
782
|
this._accessoryInfo.setupID = this._setupID;
|
|
786
783
|
// make sure we have up-to-date values in AccessoryInfo, then save it in case they changed (or if we just created it)
|
|
787
784
|
this._accessoryInfo.displayName = this.displayName;
|
|
788
|
-
this._accessoryInfo.model = this.getService(
|
|
785
|
+
this._accessoryInfo.model = this.getService(Service.AccessoryInformation).getCharacteristic(Characteristic.Model).value;
|
|
789
786
|
this._accessoryInfo.category = info.category || 1 /* Categories.OTHER */;
|
|
790
787
|
this._accessoryInfo.pincode = info.pincode;
|
|
791
788
|
this._accessoryInfo.save();
|
|
792
789
|
// create our IdentifierCache, so we can provide clients with stable aid/iid's
|
|
793
|
-
this._identifierCache =
|
|
790
|
+
this._identifierCache = IdentifierCache.load(info.username);
|
|
794
791
|
// if we don't have one, create a new one.
|
|
795
792
|
if (!this._identifierCache) {
|
|
796
|
-
debug(
|
|
797
|
-
this._identifierCache = new
|
|
793
|
+
debug('[%s] Creating new IdentifierCache', this.displayName);
|
|
794
|
+
this._identifierCache = new IdentifierCache(info.username);
|
|
798
795
|
}
|
|
799
796
|
// If it's bridge and there are no accessories already assigned to the bridge
|
|
800
797
|
// probably purge is not needed since it's going to delete all the ids
|
|
@@ -819,16 +816,49 @@ class Accessory extends events_1.EventEmitter {
|
|
|
819
816
|
this.validateAccessory(true);
|
|
820
817
|
// create our Advertiser which broadcasts our presence over mdns
|
|
821
818
|
const parsed = Accessory.parseBindOption(info);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
819
|
+
debug('[%s] Starting HAP server and publishing Accessory...', this.displayName);
|
|
820
|
+
// Select the advertiser to use based on the user's choice and availability
|
|
821
|
+
// 1. Check if info.advertiser is set by the user.
|
|
822
|
+
// 2. If info.advertiser is set, check if it is available.
|
|
823
|
+
// > If available, use it.
|
|
824
|
+
// > If not available, check if avahi is available.
|
|
825
|
+
// > If avahi is available, use it.
|
|
826
|
+
// > If not, use ciao.
|
|
827
|
+
// 3. If info.advertiser is not set, check if avahi is available.
|
|
828
|
+
// > If avahi is available, use it.
|
|
829
|
+
// > If not, use ciao.
|
|
830
|
+
if (info.advertiser) {
|
|
831
|
+
const originalAdvertiser = info.advertiser;
|
|
832
|
+
debug('[%s] Advertiser set to %s', this.displayName, info.advertiser);
|
|
833
|
+
if ((info.advertiser === "avahi" /* MDNSAdvertiser.AVAHI */ && await AvahiAdvertiser.isAvailable())
|
|
834
|
+
|| (info.advertiser === "resolved" /* MDNSAdvertiser.RESOLVED */ && await ResolvedAdvertiser.isAvailable())
|
|
835
|
+
|| (info.advertiser === "bonjour-hap" /* MDNSAdvertiser.BONJOUR */)
|
|
836
|
+
|| (info.advertiser === "ciao" /* MDNSAdvertiser.CIAO */)) {
|
|
837
|
+
// User chosen advertiser is available, use it
|
|
838
|
+
debug('[%s] Using advertiser %s', this.displayName, info.advertiser);
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
if (await AvahiAdvertiser.isAvailable()) {
|
|
842
|
+
info.advertiser = "avahi" /* MDNSAdvertiser.AVAHI */;
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
info.advertiser = "ciao" /* MDNSAdvertiser.CIAO */;
|
|
846
|
+
}
|
|
847
|
+
console.error(`[${this.displayName}] The selected advertiser [${originalAdvertiser}] isn't available on this platform. `
|
|
848
|
+
+ `Reverting to [${info.advertiser}].`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
else {
|
|
852
|
+
if (await AvahiAdvertiser.isAvailable()) {
|
|
853
|
+
info.advertiser = "avahi" /* MDNSAdvertiser.AVAHI */;
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
info.advertiser = "ciao" /* MDNSAdvertiser.CIAO */;
|
|
857
|
+
}
|
|
828
858
|
}
|
|
829
859
|
switch (info.advertiser) {
|
|
830
860
|
case "ciao" /* MDNSAdvertiser.CIAO */:
|
|
831
|
-
this._advertiser = new
|
|
861
|
+
this._advertiser = new CiaoAdvertiser(this._accessoryInfo, {
|
|
832
862
|
interface: parsed.advertiserAddress,
|
|
833
863
|
}, {
|
|
834
864
|
restrictedAddresses: parsed.serviceRestrictedAddress,
|
|
@@ -836,32 +866,31 @@ class Accessory extends events_1.EventEmitter {
|
|
|
836
866
|
});
|
|
837
867
|
break;
|
|
838
868
|
case "bonjour-hap" /* MDNSAdvertiser.BONJOUR */:
|
|
839
|
-
this._advertiser = new
|
|
869
|
+
this._advertiser = new BonjourHAPAdvertiser(this._accessoryInfo, {
|
|
840
870
|
restrictedAddresses: parsed.serviceRestrictedAddress,
|
|
841
871
|
disabledIpv6: parsed.serviceDisableIpv6,
|
|
842
872
|
});
|
|
843
873
|
break;
|
|
844
874
|
case "avahi" /* MDNSAdvertiser.AVAHI */:
|
|
845
|
-
this._advertiser = new
|
|
875
|
+
this._advertiser = new AvahiAdvertiser(this._accessoryInfo);
|
|
846
876
|
break;
|
|
847
877
|
case "resolved" /* MDNSAdvertiser.RESOLVED */:
|
|
848
|
-
this._advertiser = new
|
|
878
|
+
this._advertiser = new ResolvedAdvertiser(this._accessoryInfo);
|
|
849
879
|
break;
|
|
850
|
-
default:
|
|
851
|
-
throw new Error("Unsupported advertiser setting: '" + info.advertiser + "'");
|
|
852
880
|
}
|
|
853
|
-
|
|
881
|
+
debug('[%s] Advertiser created', this.displayName);
|
|
882
|
+
this._advertiser.on("updated-name" /* AdvertiserEvent.UPDATED_NAME */, (name) => {
|
|
854
883
|
this.displayName = name;
|
|
855
884
|
if (this._accessoryInfo) {
|
|
856
885
|
this._accessoryInfo.displayName = name;
|
|
857
886
|
this._accessoryInfo.save();
|
|
858
887
|
}
|
|
859
888
|
// bonjour service name MUST match the name in the accessory information service
|
|
860
|
-
this.getService(
|
|
861
|
-
.updateCharacteristic(
|
|
889
|
+
this.getService(Service.AccessoryInformation)
|
|
890
|
+
.updateCharacteristic(Characteristic.Name, name);
|
|
862
891
|
});
|
|
863
892
|
// create our HAP server which handles all communication between iOS devices and us
|
|
864
|
-
this._server = new
|
|
893
|
+
this._server = new HAPServer(this._accessoryInfo);
|
|
865
894
|
this._server.allowInsecureRequest = !!allowInsecureRequest;
|
|
866
895
|
this._server.on("listening" /* HAPServerEventTypes.LISTENING */, this.onListening.bind(this));
|
|
867
896
|
this._server.on("identify" /* HAPServerEventTypes.IDENTIFY */, this.identificationRequest.bind(this, false));
|
|
@@ -888,7 +917,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
888
917
|
Accessory.cleanupAccessoryData(this._accessoryInfo.username);
|
|
889
918
|
this._accessoryInfo = undefined;
|
|
890
919
|
this._identifierCache = undefined;
|
|
891
|
-
this.controllerStorage = new
|
|
920
|
+
this.controllerStorage = new ControllerStorage(this);
|
|
892
921
|
}
|
|
893
922
|
this.removeAllListeners();
|
|
894
923
|
return promise;
|
|
@@ -899,8 +928,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
899
928
|
this._server = undefined;
|
|
900
929
|
}
|
|
901
930
|
if (this._advertiser) {
|
|
902
|
-
|
|
903
|
-
await this._advertiser.destroy();
|
|
931
|
+
this._advertiser.destroy();
|
|
904
932
|
this._advertiser = undefined;
|
|
905
933
|
}
|
|
906
934
|
}
|
|
@@ -926,21 +954,21 @@ class Accessory extends events_1.EventEmitter {
|
|
|
926
954
|
// not responding or new accessories/services not yet shown
|
|
927
955
|
}
|
|
928
956
|
onListening(port, hostname) {
|
|
929
|
-
(
|
|
957
|
+
assert(this._advertiser, 'Advertiser wasn\'t created at onListening!');
|
|
930
958
|
// the HAP server is listening, so we can now start advertising our presence.
|
|
931
959
|
this._advertiser.initPort(port);
|
|
932
960
|
this._advertiser.startAdvertising()
|
|
933
961
|
.then(() => this.emit("advertised" /* AccessoryEventTypes.ADVERTISED */))
|
|
934
|
-
.catch(reason => {
|
|
935
|
-
console.error(
|
|
962
|
+
.catch((reason) => {
|
|
963
|
+
console.error(`Could not create mDNS advertisement. The HAP-Server won't be discoverable: ${reason}`);
|
|
936
964
|
if (reason.stack) {
|
|
937
|
-
debug(
|
|
965
|
+
debug(`Detailed error: ${reason.stack}`);
|
|
938
966
|
}
|
|
939
967
|
});
|
|
940
968
|
this.emit("listening" /* AccessoryEventTypes.LISTENING */, port, hostname);
|
|
941
969
|
}
|
|
942
970
|
handleInitialPairSetupFinished(username, publicKey, callback) {
|
|
943
|
-
debug(
|
|
971
|
+
debug('[%s] Paired with client %s', this.displayName, username);
|
|
944
972
|
this._accessoryInfo?.addPairedClient(username, publicKey, 1 /* PermissionTypes.ADMIN */);
|
|
945
973
|
this._accessoryInfo?.save();
|
|
946
974
|
// update our advertisement, so it can pick up on the paired status of AccessoryInfo
|
|
@@ -1009,19 +1037,19 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1009
1037
|
const now = Date.now();
|
|
1010
1038
|
const contactGetHandlers = now - this.lastAccessoriesRequest > 5_000; // we query the latest value if last /accessories was more than 5s ago
|
|
1011
1039
|
this.lastAccessoriesRequest = now;
|
|
1012
|
-
this.toHAP(connection, contactGetHandlers).then(value => {
|
|
1040
|
+
this.toHAP(connection, contactGetHandlers).then((value) => {
|
|
1013
1041
|
callback(undefined, {
|
|
1014
1042
|
accessories: value,
|
|
1015
1043
|
});
|
|
1016
|
-
}, reason => {
|
|
1017
|
-
console.error(
|
|
1044
|
+
}, (reason) => {
|
|
1045
|
+
console.error(`[${this.displayName}] /accessories request error with: ${reason.stack}`);
|
|
1018
1046
|
callback({ httpCode: 500 /* HAPHTTPCode.INTERNAL_SERVER_ERROR */, status: -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */ });
|
|
1019
1047
|
});
|
|
1020
1048
|
}
|
|
1021
1049
|
handleGetCharacteristics(connection, request, callback) {
|
|
1022
1050
|
const characteristics = [];
|
|
1023
|
-
const response = { characteristics
|
|
1024
|
-
const missingCharacteristics = new Set(request.ids.map(id => id.aid
|
|
1051
|
+
const response = { characteristics };
|
|
1052
|
+
const missingCharacteristics = new Set(request.ids.map(id => `${id.aid}.${id.iid}`));
|
|
1025
1053
|
if (missingCharacteristics.size !== request.ids.length) {
|
|
1026
1054
|
// if those sizes differ, we have duplicates and can't properly handle that
|
|
1027
1055
|
callback({ httpCode: 422 /* HAPHTTPCode.UNPROCESSABLE_ENTITY */, status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ });
|
|
@@ -1029,29 +1057,27 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1029
1057
|
}
|
|
1030
1058
|
let timeout = setTimeout(() => {
|
|
1031
1059
|
for (const id of missingCharacteristics) {
|
|
1032
|
-
const split = id.split(
|
|
1033
|
-
const aid = parseInt(split[0], 10);
|
|
1034
|
-
const iid = parseInt(split[1], 10);
|
|
1060
|
+
const split = id.split('.');
|
|
1061
|
+
const aid = Number.parseInt(split[0], 10);
|
|
1062
|
+
const iid = Number.parseInt(split[1], 10);
|
|
1035
1063
|
const accessory = this.getAccessoryByAID(aid);
|
|
1036
1064
|
const characteristic = accessory.getCharacteristicByIID(iid);
|
|
1037
|
-
this.sendCharacteristicWarning(characteristic, "slow-read" /* CharacteristicWarningType.SLOW_READ */,
|
|
1038
|
-
characteristic.displayName + "' on the accessory '" + accessory.displayName + "' was slow to respond!");
|
|
1065
|
+
this.sendCharacteristicWarning(characteristic, "slow-read" /* CharacteristicWarningType.SLOW_READ */, `The read handler for the characteristic '${characteristic.displayName}' on the accessory '${accessory.displayName}' was slow to respond!`);
|
|
1039
1066
|
}
|
|
1040
1067
|
// after a total of 10s we do no longer wait for a request to appear and just return status code timeout
|
|
1041
1068
|
timeout = setTimeout(() => {
|
|
1042
1069
|
timeout = undefined;
|
|
1043
1070
|
for (const id of missingCharacteristics) {
|
|
1044
|
-
const split = id.split(
|
|
1045
|
-
const aid = parseInt(split[0], 10);
|
|
1046
|
-
const iid = parseInt(split[1], 10);
|
|
1071
|
+
const split = id.split('.');
|
|
1072
|
+
const aid = Number.parseInt(split[0], 10);
|
|
1073
|
+
const iid = Number.parseInt(split[1], 10);
|
|
1047
1074
|
const accessory = this.getAccessoryByAID(aid);
|
|
1048
1075
|
const characteristic = accessory.getCharacteristicByIID(iid);
|
|
1049
|
-
this.sendCharacteristicWarning(characteristic, "timeout-read" /* CharacteristicWarningType.TIMEOUT_READ */,
|
|
1050
|
-
|
|
1051
|
-
"Please check that you properly call the callback!");
|
|
1076
|
+
this.sendCharacteristicWarning(characteristic, "timeout-read" /* CharacteristicWarningType.TIMEOUT_READ */, `The read handler for the characteristic '${characteristic.displayName}' on the accessory '${accessory.displayName}' didn't respond at all!. `
|
|
1077
|
+
+ `Please check that you properly call the callback!`);
|
|
1052
1078
|
characteristics.push({
|
|
1053
|
-
aid
|
|
1054
|
-
iid
|
|
1079
|
+
aid,
|
|
1080
|
+
iid,
|
|
1055
1081
|
status: -70408 /* HAPStatus.OPERATION_TIMED_OUT */,
|
|
1056
1082
|
});
|
|
1057
1083
|
}
|
|
@@ -1062,21 +1088,21 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1062
1088
|
}, Accessory.TIMEOUT_WARNING);
|
|
1063
1089
|
timeout.unref();
|
|
1064
1090
|
for (const id of request.ids) {
|
|
1065
|
-
const name = id.aid
|
|
1066
|
-
this.handleCharacteristicRead(connection, id, request).then(value => {
|
|
1091
|
+
const name = `${id.aid}.${id.iid}`;
|
|
1092
|
+
this.handleCharacteristicRead(connection, id, request).then((value) => {
|
|
1067
1093
|
return {
|
|
1068
1094
|
aid: id.aid,
|
|
1069
1095
|
iid: id.iid,
|
|
1070
1096
|
...value,
|
|
1071
1097
|
};
|
|
1072
|
-
}, reason => {
|
|
1098
|
+
}, (reason) => {
|
|
1073
1099
|
console.error(`[${this.displayName}] Read request for characteristic ${name} encountered an error: ${reason.stack}`);
|
|
1074
1100
|
return {
|
|
1075
1101
|
aid: id.aid,
|
|
1076
1102
|
iid: id.iid,
|
|
1077
1103
|
status: -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */,
|
|
1078
1104
|
};
|
|
1079
|
-
}).then(value => {
|
|
1105
|
+
}).then((value) => {
|
|
1080
1106
|
if (!timeout) {
|
|
1081
1107
|
return; // if timeout is undefined, response was already sent out
|
|
1082
1108
|
}
|
|
@@ -1095,25 +1121,25 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1095
1121
|
async handleCharacteristicRead(connection, id, request) {
|
|
1096
1122
|
const characteristic = this.findCharacteristic(id.aid, id.iid);
|
|
1097
1123
|
if (!characteristic) {
|
|
1098
|
-
debug(
|
|
1124
|
+
debug('[%s] Could not find a Characteristic with aid of %s and iid of %s', this.displayName, id.aid, id.iid);
|
|
1099
1125
|
return { status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ };
|
|
1100
1126
|
}
|
|
1101
1127
|
if (!characteristic.props.perms.includes("pr" /* Perms.PAIRED_READ */)) { // check if read is allowed for this characteristic
|
|
1102
|
-
debug(
|
|
1128
|
+
debug('[%s] Tried reading from characteristic which does not allow reading (aid of %s and iid of %s)', this.displayName, id.aid, id.iid);
|
|
1103
1129
|
return { status: -70405 /* HAPStatus.WRITE_ONLY_CHARACTERISTIC */ };
|
|
1104
1130
|
}
|
|
1105
1131
|
if (characteristic.props.adminOnlyAccess && characteristic.props.adminOnlyAccess.includes(0 /* Access.READ */)) {
|
|
1106
1132
|
const verifiable = this._accessoryInfo && connection.username;
|
|
1107
1133
|
if (!verifiable) {
|
|
1108
|
-
debug(
|
|
1134
|
+
debug('[%s] Could not verify admin permissions for Characteristic which requires admin permissions for reading (aid of %s and iid of %s)', this.displayName, id.aid, id.iid);
|
|
1109
1135
|
}
|
|
1110
1136
|
if (!verifiable || !this._accessoryInfo.hasAdminPermissions(connection.username)) {
|
|
1111
1137
|
return { status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ };
|
|
1112
1138
|
}
|
|
1113
1139
|
}
|
|
1114
|
-
return characteristic.handleGetRequest(connection).then(value => {
|
|
1115
|
-
value =
|
|
1116
|
-
debug(
|
|
1140
|
+
return characteristic.handleGetRequest(connection).then((value) => {
|
|
1141
|
+
value = formatOutgoingCharacteristicValue(value, characteristic.props);
|
|
1142
|
+
debug('[%s] Got Characteristic "%s" value: "%s"', this.displayName, characteristic.displayName, value);
|
|
1117
1143
|
const data = {
|
|
1118
1144
|
value: value == null ? null : value,
|
|
1119
1145
|
};
|
|
@@ -1129,7 +1155,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1129
1155
|
data.perms = characteristic.props.perms;
|
|
1130
1156
|
}
|
|
1131
1157
|
if (request.includeType) {
|
|
1132
|
-
data.type =
|
|
1158
|
+
data.type = toShortForm(characteristic.UUID);
|
|
1133
1159
|
}
|
|
1134
1160
|
if (request.includeEvent) {
|
|
1135
1161
|
data.ev = connection.hasEventNotifications(id.aid, id.iid);
|
|
@@ -1137,12 +1163,12 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1137
1163
|
return data;
|
|
1138
1164
|
}, (reason) => {
|
|
1139
1165
|
// @ts-expect-error: preserveConstEnums compiler option
|
|
1140
|
-
debug(
|
|
1166
|
+
debug('[%s] Error getting value for characteristic "%s": %s', this.displayName, characteristic.displayName, HAPStatus[reason]);
|
|
1141
1167
|
return { status: reason };
|
|
1142
1168
|
});
|
|
1143
1169
|
}
|
|
1144
1170
|
handleSetCharacteristics(connection, writeRequest, callback) {
|
|
1145
|
-
debug(
|
|
1171
|
+
debug('[%s] Processing characteristic set: %s', this.displayName, JSON.stringify(writeRequest));
|
|
1146
1172
|
let writeState = 0 /* WriteRequestState.REGULAR_REQUEST */;
|
|
1147
1173
|
if (writeRequest.pid !== undefined) { // check for timed writes
|
|
1148
1174
|
if (connection.timedWritePid === writeRequest.pid) {
|
|
@@ -1150,17 +1176,17 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1150
1176
|
clearTimeout(connection.timedWriteTimeout);
|
|
1151
1177
|
connection.timedWritePid = undefined;
|
|
1152
1178
|
connection.timedWriteTimeout = undefined;
|
|
1153
|
-
debug(
|
|
1179
|
+
debug('[%s] Timed write request got acknowledged for pid %d', this.displayName, writeRequest.pid);
|
|
1154
1180
|
}
|
|
1155
1181
|
else {
|
|
1156
1182
|
writeState = 2 /* WriteRequestState.TIMED_WRITE_REJECTED */;
|
|
1157
|
-
debug(
|
|
1183
|
+
debug('[%s] TTL for timed write request has probably expired for pid %d', this.displayName, writeRequest.pid);
|
|
1158
1184
|
}
|
|
1159
1185
|
}
|
|
1160
1186
|
const characteristics = [];
|
|
1161
|
-
const response = { characteristics
|
|
1187
|
+
const response = { characteristics };
|
|
1162
1188
|
const missingCharacteristics = new Set(writeRequest.characteristics
|
|
1163
|
-
.map(characteristic => characteristic.aid
|
|
1189
|
+
.map(characteristic => `${characteristic.aid}.${characteristic.iid}`));
|
|
1164
1190
|
if (missingCharacteristics.size !== writeRequest.characteristics.length) {
|
|
1165
1191
|
// if those sizes differ, we have duplicates and can't properly handle that
|
|
1166
1192
|
callback({ httpCode: 422 /* HAPHTTPCode.UNPROCESSABLE_ENTITY */, status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ });
|
|
@@ -1168,29 +1194,27 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1168
1194
|
}
|
|
1169
1195
|
let timeout = setTimeout(() => {
|
|
1170
1196
|
for (const id of missingCharacteristics) {
|
|
1171
|
-
const split = id.split(
|
|
1172
|
-
const aid = parseInt(split[0], 10);
|
|
1173
|
-
const iid = parseInt(split[1], 10);
|
|
1197
|
+
const split = id.split('.');
|
|
1198
|
+
const aid = Number.parseInt(split[0], 10);
|
|
1199
|
+
const iid = Number.parseInt(split[1], 10);
|
|
1174
1200
|
const accessory = this.getAccessoryByAID(aid);
|
|
1175
1201
|
const characteristic = accessory.getCharacteristicByIID(iid);
|
|
1176
|
-
this.sendCharacteristicWarning(characteristic, "slow-write" /* CharacteristicWarningType.SLOW_WRITE */,
|
|
1177
|
-
characteristic.displayName + "' on the accessory '" + accessory.displayName + "' was slow to respond!");
|
|
1202
|
+
this.sendCharacteristicWarning(characteristic, "slow-write" /* CharacteristicWarningType.SLOW_WRITE */, `The write handler for the characteristic '${characteristic.displayName}' on the accessory '${accessory.displayName}' was slow to respond!`);
|
|
1178
1203
|
}
|
|
1179
1204
|
// after a total of 10s we do no longer wait for a request to appear and just return status code timeout
|
|
1180
1205
|
timeout = setTimeout(() => {
|
|
1181
1206
|
timeout = undefined;
|
|
1182
1207
|
for (const id of missingCharacteristics) {
|
|
1183
|
-
const split = id.split(
|
|
1184
|
-
const aid = parseInt(split[0], 10);
|
|
1185
|
-
const iid = parseInt(split[1], 10);
|
|
1208
|
+
const split = id.split('.');
|
|
1209
|
+
const aid = Number.parseInt(split[0], 10);
|
|
1210
|
+
const iid = Number.parseInt(split[1], 10);
|
|
1186
1211
|
const accessory = this.getAccessoryByAID(aid);
|
|
1187
1212
|
const characteristic = accessory.getCharacteristicByIID(iid);
|
|
1188
|
-
this.sendCharacteristicWarning(characteristic, "timeout-write" /* CharacteristicWarningType.TIMEOUT_WRITE */,
|
|
1189
|
-
|
|
1190
|
-
"Please check that you properly call the callback!");
|
|
1213
|
+
this.sendCharacteristicWarning(characteristic, "timeout-write" /* CharacteristicWarningType.TIMEOUT_WRITE */, `The write handler for the characteristic '${characteristic.displayName}' on the accessory '${accessory.displayName}' didn't respond at all!. `
|
|
1214
|
+
+ `Please check that you properly call the callback!`);
|
|
1191
1215
|
characteristics.push({
|
|
1192
|
-
aid
|
|
1193
|
-
iid
|
|
1216
|
+
aid,
|
|
1217
|
+
iid,
|
|
1194
1218
|
status: -70408 /* HAPStatus.OPERATION_TIMED_OUT */,
|
|
1195
1219
|
});
|
|
1196
1220
|
}
|
|
@@ -1201,21 +1225,21 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1201
1225
|
}, Accessory.TIMEOUT_WARNING);
|
|
1202
1226
|
timeout.unref();
|
|
1203
1227
|
for (const data of writeRequest.characteristics) {
|
|
1204
|
-
const name = data.aid
|
|
1205
|
-
this.handleCharacteristicWrite(connection, data, writeState).then(value => {
|
|
1228
|
+
const name = `${data.aid}.${data.iid}`;
|
|
1229
|
+
this.handleCharacteristicWrite(connection, data, writeState).then((value) => {
|
|
1206
1230
|
return {
|
|
1207
1231
|
aid: data.aid,
|
|
1208
1232
|
iid: data.iid,
|
|
1209
1233
|
...value,
|
|
1210
1234
|
};
|
|
1211
|
-
}, reason => {
|
|
1235
|
+
}, (reason) => {
|
|
1212
1236
|
console.error(`[${this.displayName}] Write request for characteristic ${name} encountered an error: ${reason.stack}`);
|
|
1213
1237
|
return {
|
|
1214
1238
|
aid: data.aid,
|
|
1215
1239
|
iid: data.iid,
|
|
1216
1240
|
status: -70402 /* HAPStatus.SERVICE_COMMUNICATION_FAILURE */,
|
|
1217
1241
|
};
|
|
1218
|
-
}).then(value => {
|
|
1242
|
+
}).then((value) => {
|
|
1219
1243
|
if (!timeout) {
|
|
1220
1244
|
return; // if timeout is undefined, response was already sent out
|
|
1221
1245
|
}
|
|
@@ -1234,7 +1258,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1234
1258
|
async handleCharacteristicWrite(connection, data, writeState) {
|
|
1235
1259
|
const characteristic = this.findCharacteristic(data.aid, data.iid);
|
|
1236
1260
|
if (!characteristic) {
|
|
1237
|
-
debug(
|
|
1261
|
+
debug('[%s] Could not find a Characteristic with aid of %s and iid of %s', this.displayName, data.aid, data.iid);
|
|
1238
1262
|
return { status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ };
|
|
1239
1263
|
}
|
|
1240
1264
|
if (writeState === 2 /* WriteRequestState.TIMED_WRITE_REJECTED */) {
|
|
@@ -1245,13 +1269,13 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1245
1269
|
}
|
|
1246
1270
|
if (data.ev != null) { // register/unregister event notifications
|
|
1247
1271
|
if (!characteristic.props.perms.includes("ev" /* Perms.NOTIFY */)) { // check if notify is allowed for this characteristic
|
|
1248
|
-
debug(
|
|
1272
|
+
debug('[%s] Tried %s notifications for Characteristic which does not allow notify (aid of %s and iid of %s)', this.displayName, data.ev ? 'enabling' : 'disabling', data.aid, data.iid);
|
|
1249
1273
|
return { status: -70406 /* HAPStatus.NOTIFICATION_NOT_SUPPORTED */ };
|
|
1250
1274
|
}
|
|
1251
1275
|
if (characteristic.props.adminOnlyAccess && characteristic.props.adminOnlyAccess.includes(2 /* Access.NOTIFY */)) {
|
|
1252
1276
|
const verifiable = connection.username && this._accessoryInfo;
|
|
1253
1277
|
if (!verifiable) {
|
|
1254
|
-
debug(
|
|
1278
|
+
debug('[%s] Could not verify admin permissions for Characteristic which requires admin permissions for notify (aid of %s and iid of %s)', this.displayName, data.aid, data.iid);
|
|
1255
1279
|
}
|
|
1256
1280
|
if (!verifiable || !this._accessoryInfo.hasAdminPermissions(connection.username)) {
|
|
1257
1281
|
return { status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ };
|
|
@@ -1261,24 +1285,24 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1261
1285
|
if (data.ev && !notificationsEnabled) {
|
|
1262
1286
|
connection.enableEventNotifications(data.aid, data.iid);
|
|
1263
1287
|
characteristic.subscribe();
|
|
1264
|
-
debug(
|
|
1288
|
+
debug('[%s] Registered Characteristic "%s" on "%s" for events', connection.remoteAddress, characteristic.displayName, this.displayName);
|
|
1265
1289
|
}
|
|
1266
1290
|
else if (!data.ev && notificationsEnabled) {
|
|
1267
1291
|
characteristic.unsubscribe();
|
|
1268
1292
|
connection.disableEventNotifications(data.aid, data.iid);
|
|
1269
|
-
debug(
|
|
1293
|
+
debug('[%s] Unregistered Characteristic "%s" on "%s" for events', connection.remoteAddress, characteristic.displayName, this.displayName);
|
|
1270
1294
|
}
|
|
1271
1295
|
// response is returned below in the else block
|
|
1272
1296
|
}
|
|
1273
1297
|
if (data.value != null) {
|
|
1274
1298
|
if (!characteristic.props.perms.includes("pw" /* Perms.PAIRED_WRITE */)) { // check if write is allowed for this characteristic
|
|
1275
|
-
debug(
|
|
1299
|
+
debug('[%s] Tried writing to Characteristic which does not allow writing (aid of %s and iid of %s)', this.displayName, data.aid, data.iid);
|
|
1276
1300
|
return { status: -70404 /* HAPStatus.READ_ONLY_CHARACTERISTIC */ };
|
|
1277
1301
|
}
|
|
1278
1302
|
if (characteristic.props.adminOnlyAccess && characteristic.props.adminOnlyAccess.includes(1 /* Access.WRITE */)) {
|
|
1279
1303
|
const verifiable = connection.username && this._accessoryInfo;
|
|
1280
1304
|
if (!verifiable) {
|
|
1281
|
-
debug(
|
|
1305
|
+
debug('[%s] Could not verify admin permissions for Characteristic which requires admin permissions for write (aid of %s and iid of %s)', this.displayName, data.aid, data.iid);
|
|
1282
1306
|
}
|
|
1283
1307
|
if (!verifiable || !this._accessoryInfo.hasAdminPermissions(connection.username)) {
|
|
1284
1308
|
return { status: -70401 /* HAPStatus.INSUFFICIENT_PRIVILEGES */ };
|
|
@@ -1292,7 +1316,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1292
1316
|
allowWrite = characteristic.additionalAuthorizationHandler(data.authData);
|
|
1293
1317
|
}
|
|
1294
1318
|
catch (error) {
|
|
1295
|
-
console.warn(
|
|
1319
|
+
console.warn(`[${this.displayName}] Additional authorization handler has thrown an error when checking authData: ${error.stack}`);
|
|
1296
1320
|
allowWrite = false;
|
|
1297
1321
|
}
|
|
1298
1322
|
if (!allowWrite) {
|
|
@@ -1300,29 +1324,29 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1300
1324
|
}
|
|
1301
1325
|
}
|
|
1302
1326
|
if (characteristic.props.perms.includes("tw" /* Perms.TIMED_WRITE */) && writeState !== 1 /* WriteRequestState.TIMED_WRITE_AUTHENTICATED */) {
|
|
1303
|
-
debug(
|
|
1327
|
+
debug('[%s] Tried writing to a timed write only Characteristic without properly preparing (iid of %s and aid of %s)', this.displayName, data.aid, data.iid);
|
|
1304
1328
|
return { status: -70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */ };
|
|
1305
1329
|
}
|
|
1306
|
-
return characteristic.handleSetRequest(data.value, connection).then(value => {
|
|
1307
|
-
debug(
|
|
1330
|
+
return characteristic.handleSetRequest(data.value, connection).then((value) => {
|
|
1331
|
+
debug('[%s] Setting Characteristic "%s" to value %s', this.displayName, characteristic.displayName, data.value);
|
|
1308
1332
|
return {
|
|
1309
1333
|
// if write response is requests and value is provided, return that
|
|
1310
|
-
value: data.r && value ?
|
|
1334
|
+
value: data.r && value ? formatOutgoingCharacteristicValue(value, characteristic.props) : undefined,
|
|
1311
1335
|
status: 0 /* HAPStatus.SUCCESS */,
|
|
1312
1336
|
};
|
|
1313
1337
|
}, (status) => {
|
|
1314
|
-
// @ts-expect-error:
|
|
1315
|
-
debug(
|
|
1316
|
-
return { status
|
|
1338
|
+
// @ts-expect-error: preserveConstEnums compiler option
|
|
1339
|
+
debug('[%s] Error setting Characteristic "%s" to value %s: ', this.displayName, characteristic.displayName, data.value, HAPStatus[status]);
|
|
1340
|
+
return { status };
|
|
1317
1341
|
});
|
|
1318
1342
|
}
|
|
1319
1343
|
return { status: 0 /* HAPStatus.SUCCESS */ };
|
|
1320
1344
|
}
|
|
1321
1345
|
handleResource(data, callback) {
|
|
1322
|
-
if (data[
|
|
1323
|
-
const aid = data.aid; // aid is optionally supplied by HomeKit (for example when camera is bridged, multiple cams, etc)
|
|
1324
|
-
let accessory
|
|
1325
|
-
let controller
|
|
1346
|
+
if (data['resource-type'] === "image" /* ResourceRequestType.IMAGE */) {
|
|
1347
|
+
const aid = data.aid; // aid is optionally supplied by HomeKit (for example when camera is bridged, multiple cams, etc.)
|
|
1348
|
+
let accessory;
|
|
1349
|
+
let controller;
|
|
1326
1350
|
if (aid) {
|
|
1327
1351
|
accessory = this.getAccessoryByAID(aid);
|
|
1328
1352
|
if (accessory && accessory.activeCameraController) {
|
|
@@ -1330,31 +1354,30 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1330
1354
|
}
|
|
1331
1355
|
}
|
|
1332
1356
|
else if (this.activeCameraController) { // aid was not supplied, check if this accessory is a camera
|
|
1333
|
-
// eslint-disable-
|
|
1334
|
-
accessory = this;
|
|
1357
|
+
accessory = this; // eslint-disable-line ts/no-this-alias
|
|
1335
1358
|
controller = this.activeCameraController;
|
|
1336
1359
|
}
|
|
1337
1360
|
if (!controller) {
|
|
1338
|
-
debug(
|
|
1361
|
+
debug('[%s] received snapshot request though no camera controller was associated!');
|
|
1339
1362
|
callback({ httpCode: 404 /* HAPHTTPCode.NOT_FOUND */, status: -70409 /* HAPStatus.RESOURCE_DOES_NOT_EXIST */ });
|
|
1340
1363
|
return;
|
|
1341
1364
|
}
|
|
1342
|
-
controller.handleSnapshotRequest(data[
|
|
1343
|
-
.then(buffer => {
|
|
1365
|
+
controller.handleSnapshotRequest(data['image-height'], data['image-width'], accessory?.displayName, data.reason)
|
|
1366
|
+
.then((buffer) => {
|
|
1344
1367
|
callback(undefined, buffer);
|
|
1345
1368
|
}, (status) => {
|
|
1346
|
-
callback({ httpCode: 207 /* HAPHTTPCode.MULTI_STATUS */, status
|
|
1369
|
+
callback({ httpCode: 207 /* HAPHTTPCode.MULTI_STATUS */, status });
|
|
1347
1370
|
});
|
|
1348
1371
|
return;
|
|
1349
1372
|
}
|
|
1350
|
-
debug(
|
|
1373
|
+
debug(`[%s] received request for unsupported image type: ${data['resource-type']}`, this._accessoryInfo?.username);
|
|
1351
1374
|
callback({ httpCode: 404 /* HAPHTTPCode.NOT_FOUND */, status: -70409 /* HAPStatus.RESOURCE_DOES_NOT_EXIST */ });
|
|
1352
1375
|
}
|
|
1353
1376
|
handleHAPConnectionClosed(connection) {
|
|
1354
1377
|
for (const event of connection.getRegisteredEvents()) {
|
|
1355
|
-
const ids = event.split(
|
|
1356
|
-
const aid = parseInt(ids[0], 10);
|
|
1357
|
-
const iid = parseInt(ids[1], 10);
|
|
1378
|
+
const ids = event.split('.');
|
|
1379
|
+
const aid = Number.parseInt(ids[0], 10);
|
|
1380
|
+
const iid = Number.parseInt(ids[1], 10);
|
|
1358
1381
|
const characteristic = this.findCharacteristic(aid, iid);
|
|
1359
1382
|
if (characteristic) {
|
|
1360
1383
|
characteristic.unsubscribe();
|
|
@@ -1375,7 +1398,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1375
1398
|
this.primaryService = service;
|
|
1376
1399
|
}
|
|
1377
1400
|
if (this.bridged) {
|
|
1378
|
-
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service
|
|
1401
|
+
this.emit("service-configurationChange" /* AccessoryEventTypes.SERVICE_CONFIGURATION_CHANGE */, { service });
|
|
1379
1402
|
}
|
|
1380
1403
|
else {
|
|
1381
1404
|
this.enqueueConfigurationUpdate();
|
|
@@ -1383,23 +1406,23 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1383
1406
|
}
|
|
1384
1407
|
handleCharacteristicChangeEvent(accessory, service, change) {
|
|
1385
1408
|
if (this.bridged) { // forward this to our main accessory
|
|
1386
|
-
this.emit("service-characteristic-change" /* AccessoryEventTypes.SERVICE_CHARACTERISTIC_CHANGE */, { ...change, service
|
|
1409
|
+
this.emit("service-characteristic-change" /* AccessoryEventTypes.SERVICE_CHARACTERISTIC_CHANGE */, { ...change, service });
|
|
1387
1410
|
}
|
|
1388
1411
|
else {
|
|
1389
1412
|
if (!this._server) {
|
|
1390
1413
|
return; // we're not running a HAPServer, so there's no one to notify about this event
|
|
1391
1414
|
}
|
|
1392
1415
|
if (accessory.aid == null || change.characteristic.iid == null) {
|
|
1393
|
-
debug(
|
|
1416
|
+
debug('[%s] Muting event notification for %s as ids aren\'t yet assigned!', accessory.displayName, change.characteristic.displayName);
|
|
1394
1417
|
return;
|
|
1395
1418
|
}
|
|
1396
|
-
if (change.context != null && typeof change.context ===
|
|
1397
|
-
debug(
|
|
1419
|
+
if (change.context != null && typeof change.context === 'object' && change.context.omitEventUpdate) {
|
|
1420
|
+
debug('[%s] Omitting event updates for %s as specified in the context object!', accessory.displayName, change.characteristic.displayName);
|
|
1398
1421
|
return;
|
|
1399
1422
|
}
|
|
1400
1423
|
if (!(change.reason === "event" /* ChangeReason.EVENT */ || change.oldValue !== change.newValue
|
|
1401
|
-
|| change.characteristic.UUID ===
|
|
1402
|
-
|| change.characteristic.UUID ===
|
|
1424
|
+
|| change.characteristic.UUID === Characteristic.ProgrammableSwitchEvent.UUID // those specific checks are out of backwards compatibility
|
|
1425
|
+
|| change.characteristic.UUID === Characteristic.ButtonEvent.UUID // new characteristics should use sendEventNotification call
|
|
1403
1426
|
)) {
|
|
1404
1427
|
// we only emit a change event if the reason was a call to sendEventNotification, if the value changed
|
|
1405
1428
|
// as of a write request or a read request or if the change happened on dedicated event characteristics
|
|
@@ -1407,26 +1430,26 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1407
1430
|
return;
|
|
1408
1431
|
}
|
|
1409
1432
|
const uuid = change.characteristic.UUID;
|
|
1410
|
-
const immediateDelivery = uuid ===
|
|
1411
|
-
|| uuid ===
|
|
1412
|
-
const value =
|
|
1433
|
+
const immediateDelivery = uuid === Characteristic.ButtonEvent.UUID || uuid === Characteristic.ProgrammableSwitchEvent.UUID
|
|
1434
|
+
|| uuid === Characteristic.MotionDetected.UUID || uuid === Characteristic.ContactSensorState.UUID;
|
|
1435
|
+
const value = formatOutgoingCharacteristicValue(change.newValue, change.characteristic.props);
|
|
1413
1436
|
this._server.sendEventNotifications(accessory.aid, change.characteristic.iid, value, change.originator, immediateDelivery);
|
|
1414
1437
|
}
|
|
1415
1438
|
}
|
|
1416
1439
|
sendCharacteristicWarning(characteristic, type, message) {
|
|
1417
1440
|
this.handleCharacteristicWarning({
|
|
1418
|
-
characteristic
|
|
1419
|
-
type
|
|
1420
|
-
message
|
|
1441
|
+
characteristic,
|
|
1442
|
+
type,
|
|
1443
|
+
message,
|
|
1421
1444
|
originatorChain: [characteristic.displayName], // we are missing the service displayName, but that's okay
|
|
1422
|
-
stack: new Error().stack,
|
|
1445
|
+
stack: new Error().stack, // eslint-disable-line unicorn/error-message
|
|
1423
1446
|
});
|
|
1424
1447
|
}
|
|
1425
1448
|
handleCharacteristicWarning(warning) {
|
|
1426
1449
|
warning.originatorChain = [this.displayName, ...warning.originatorChain];
|
|
1427
1450
|
const emitted = this.emit("characteristic-warning" /* AccessoryEventTypes.CHARACTERISTIC_WARNING */, warning);
|
|
1428
1451
|
if (!emitted) {
|
|
1429
|
-
const message = `[${warning.originatorChain.join(
|
|
1452
|
+
const message = `[${warning.originatorChain.join('@')}] ${warning.message}`;
|
|
1430
1453
|
if (warning.type === "error-message" /* CharacteristicWarningType.ERROR_MESSAGE */
|
|
1431
1454
|
|| warning.type === "timeout-read" /* CharacteristicWarningType.TIMEOUT_READ */ || warning.type === "timeout-write" /* CharacteristicWarningType.TIMEOUT_WRITE */) {
|
|
1432
1455
|
console.error(message);
|
|
@@ -1434,7 +1457,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1434
1457
|
else {
|
|
1435
1458
|
console.warn(message);
|
|
1436
1459
|
}
|
|
1437
|
-
debug(
|
|
1460
|
+
debug('[%s] Above characteristic warning was thrown at: %s', this.displayName, warning.stack ?? 'unknown');
|
|
1438
1461
|
}
|
|
1439
1462
|
}
|
|
1440
1463
|
setupServiceEventHandlers(service) {
|
|
@@ -1449,8 +1472,8 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1449
1472
|
this.services = targetServices.slice();
|
|
1450
1473
|
// Fix Identify
|
|
1451
1474
|
this
|
|
1452
|
-
.getService(
|
|
1453
|
-
.getCharacteristic(
|
|
1475
|
+
.getService(Service.AccessoryInformation)
|
|
1476
|
+
.getCharacteristic(Characteristic.Identify)
|
|
1454
1477
|
.on("set" /* CharacteristicEventTypes.SET */, (value, callback) => {
|
|
1455
1478
|
if (value) {
|
|
1456
1479
|
const paired = true;
|
|
@@ -1459,9 +1482,9 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1459
1482
|
});
|
|
1460
1483
|
}
|
|
1461
1484
|
static _generateSetupID() {
|
|
1462
|
-
const chars =
|
|
1485
|
+
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
1463
1486
|
const max = chars.length;
|
|
1464
|
-
let setupID =
|
|
1487
|
+
let setupID = '';
|
|
1465
1488
|
for (let i = 0; i < 4; i++) {
|
|
1466
1489
|
const index = Math.floor(Math.random() * max);
|
|
1467
1490
|
setupID += chars.charAt(index);
|
|
@@ -1479,10 +1502,10 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1479
1502
|
};
|
|
1480
1503
|
const linkedServices = {};
|
|
1481
1504
|
let hasLinkedServices = false;
|
|
1482
|
-
accessory.services.forEach(service => {
|
|
1483
|
-
json.services.push(
|
|
1505
|
+
accessory.services.forEach((service) => {
|
|
1506
|
+
json.services.push(Service.serialize(service));
|
|
1484
1507
|
const linkedServicesPresentation = [];
|
|
1485
|
-
service.linkedServices.forEach(linkedService => {
|
|
1508
|
+
service.linkedServices.forEach((linkedService) => {
|
|
1486
1509
|
linkedServicesPresentation.push(linkedService.getServiceId());
|
|
1487
1510
|
});
|
|
1488
1511
|
if (linkedServicesPresentation.length > 0) {
|
|
@@ -1519,8 +1542,8 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1519
1542
|
accessory.category = json.category;
|
|
1520
1543
|
const services = [];
|
|
1521
1544
|
const servicesMap = {};
|
|
1522
|
-
json.services.forEach(serialized => {
|
|
1523
|
-
const service =
|
|
1545
|
+
json.services.forEach((serialized) => {
|
|
1546
|
+
const service = Service.deserialize(serialized);
|
|
1524
1547
|
services.push(service);
|
|
1525
1548
|
servicesMap[service.getServiceId()] = service;
|
|
1526
1549
|
});
|
|
@@ -1530,7 +1553,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1530
1553
|
if (!primaryService) {
|
|
1531
1554
|
continue;
|
|
1532
1555
|
}
|
|
1533
|
-
linkedServicesKeys.forEach(linkedServiceKey => {
|
|
1556
|
+
linkedServicesKeys.forEach((linkedServiceKey) => {
|
|
1534
1557
|
const linkedService = servicesMap[linkedServiceKey];
|
|
1535
1558
|
if (linkedService) {
|
|
1536
1559
|
primaryService.addLinkedService(linkedService);
|
|
@@ -1540,7 +1563,7 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1540
1563
|
}
|
|
1541
1564
|
if (json.controllers) { // just save it for later if it exists {@see configureController}
|
|
1542
1565
|
accessory.serializedControllers = {};
|
|
1543
|
-
json.controllers.forEach(serializedController => {
|
|
1566
|
+
json.controllers.forEach((serializedController) => {
|
|
1544
1567
|
accessory.serializedControllers[serializedController.type] = Accessory.deserializeServiceMap(serializedController.services, servicesMap);
|
|
1545
1568
|
});
|
|
1546
1569
|
}
|
|
@@ -1548,9 +1571,9 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1548
1571
|
return accessory;
|
|
1549
1572
|
}
|
|
1550
1573
|
static cleanupAccessoryData(username) {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1574
|
+
IdentifierCache.remove(username);
|
|
1575
|
+
AccessoryInfo.remove(username);
|
|
1576
|
+
ControllerStorage.remove(username);
|
|
1554
1577
|
}
|
|
1555
1578
|
static serializeServiceMap(serviceMap) {
|
|
1556
1579
|
const serialized = {};
|
|
@@ -1573,22 +1596,22 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1573
1596
|
return controllerServiceMap;
|
|
1574
1597
|
}
|
|
1575
1598
|
static parseBindOption(info) {
|
|
1576
|
-
let advertiserAddress
|
|
1577
|
-
let disableIpv6
|
|
1578
|
-
let serverAddress
|
|
1599
|
+
let advertiserAddress;
|
|
1600
|
+
let disableIpv6;
|
|
1601
|
+
let serverAddress;
|
|
1579
1602
|
if (info.bind) {
|
|
1580
1603
|
const entries = new Set(Array.isArray(info.bind) ? info.bind : [info.bind]);
|
|
1581
|
-
if (entries.has(
|
|
1582
|
-
serverAddress =
|
|
1583
|
-
entries.delete(
|
|
1604
|
+
if (entries.has('::')) {
|
|
1605
|
+
serverAddress = '::';
|
|
1606
|
+
entries.delete('::');
|
|
1584
1607
|
if (entries.size) {
|
|
1585
1608
|
advertiserAddress = Array.from(entries);
|
|
1586
1609
|
}
|
|
1587
1610
|
}
|
|
1588
|
-
else if (entries.has(
|
|
1611
|
+
else if (entries.has('0.0.0.0')) {
|
|
1589
1612
|
disableIpv6 = true;
|
|
1590
|
-
serverAddress =
|
|
1591
|
-
entries.delete(
|
|
1613
|
+
serverAddress = '0.0.0.0';
|
|
1614
|
+
entries.delete('0.0.0.0');
|
|
1592
1615
|
if (entries.size) {
|
|
1593
1616
|
advertiserAddress = Array.from(entries);
|
|
1594
1617
|
}
|
|
@@ -1596,39 +1619,38 @@ class Accessory extends events_1.EventEmitter {
|
|
|
1596
1619
|
else if (entries.size === 1) {
|
|
1597
1620
|
advertiserAddress = Array.from(entries);
|
|
1598
1621
|
const entry = entries.values().next().value; // grab the first one
|
|
1599
|
-
const version =
|
|
1622
|
+
const version = isIP(entry); // check if ip address was specified or an interface name
|
|
1600
1623
|
if (version) {
|
|
1601
|
-
serverAddress = version === 4 ?
|
|
1624
|
+
serverAddress = version === 4 ? '0.0.0.0' : '::'; // we currently bind to unspecified addresses so config-ui always has a connection via loopback
|
|
1602
1625
|
}
|
|
1603
1626
|
else {
|
|
1604
|
-
serverAddress =
|
|
1627
|
+
serverAddress = '::'; // the interface could have both ipv4 and ipv6 addresses
|
|
1605
1628
|
}
|
|
1606
1629
|
}
|
|
1607
1630
|
else if (entries.size > 1) {
|
|
1608
1631
|
advertiserAddress = Array.from(entries);
|
|
1609
1632
|
let bindUnspecifiedIpv6 = false; // we bind on "::" if there are interface names, or we detect ipv6 addresses
|
|
1610
1633
|
for (const entry of entries) {
|
|
1611
|
-
const version =
|
|
1634
|
+
const version = isIP(entry);
|
|
1612
1635
|
if (version === 0 || version === 6) {
|
|
1613
1636
|
bindUnspecifiedIpv6 = true;
|
|
1614
1637
|
break;
|
|
1615
1638
|
}
|
|
1616
1639
|
}
|
|
1617
1640
|
if (bindUnspecifiedIpv6) {
|
|
1618
|
-
serverAddress =
|
|
1641
|
+
serverAddress = '::';
|
|
1619
1642
|
}
|
|
1620
1643
|
else {
|
|
1621
|
-
serverAddress =
|
|
1644
|
+
serverAddress = '0.0.0.0';
|
|
1622
1645
|
}
|
|
1623
1646
|
}
|
|
1624
1647
|
}
|
|
1625
1648
|
return {
|
|
1626
|
-
advertiserAddress
|
|
1649
|
+
advertiserAddress,
|
|
1627
1650
|
serviceRestrictedAddress: advertiserAddress,
|
|
1628
1651
|
serviceDisableIpv6: disableIpv6,
|
|
1629
|
-
serverAddress
|
|
1652
|
+
serverAddress,
|
|
1630
1653
|
};
|
|
1631
1654
|
}
|
|
1632
1655
|
}
|
|
1633
|
-
exports.Accessory = Accessory;
|
|
1634
1656
|
//# sourceMappingURL=Accessory.js.map
|