node-red-contrib-homekit-bridged 2.0.0-dev.1 → 2.0.0-dev.10
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/build/lib/HAPHostNode.js +185 -146
- package/build/lib/HAPServiceNode.js +200 -177
- package/build/lib/HAPServiceNode2.js +208 -177
- package/build/lib/NRCHKBError.js +23 -2
- package/build/lib/PairingQRCode.js +62 -0
- package/build/lib/Storage.js +152 -90
- package/build/lib/api.js +654 -290
- package/build/lib/camera/CameraControl.js +125 -0
- package/build/lib/camera/CameraDelegate.js +507 -0
- package/build/lib/camera/MP4StreamingServer.js +159 -0
- package/build/lib/hap/HAPCharacteristic.js +25 -4
- package/build/lib/hap/HAPService.js +25 -4
- package/build/lib/hap/eve-app/EveCharacteristics.js +124 -81
- package/build/lib/hap/eve-app/EveServices.js +50 -17
- package/build/lib/hap/hap-nodejs.js +32 -0
- package/build/lib/migration/HomeKitService2Migration.js +34 -0
- package/build/lib/migration/NodeMigration.js +75 -0
- package/build/lib/types/AccessoryInformationType.js +15 -1
- package/build/lib/types/CameraConfigType.js +15 -1
- package/build/lib/types/CustomCharacteristicType.js +15 -1
- package/build/lib/types/HAPHostConfigType.js +15 -1
- package/build/lib/types/HAPHostNodeType.js +15 -1
- package/build/lib/types/HAPService2ConfigType.js +15 -1
- package/build/lib/types/HAPService2NodeType.js +15 -1
- package/build/lib/types/HAPServiceConfigType.js +15 -1
- package/build/lib/types/HAPServiceNodeType.js +15 -1
- package/build/lib/types/HAPStatusConfigType.js +15 -1
- package/build/lib/types/HAPStatusNodeType.js +15 -1
- package/build/lib/types/HostType.js +28 -7
- package/build/lib/types/NodeType.js +15 -1
- package/build/lib/types/PublishTimersType.js +15 -1
- package/build/lib/types/UniFiControllerConfigType.js +16 -0
- package/build/lib/types/hap-nodejs/HapAdaptiveLightingControllerMode.js +28 -7
- package/build/lib/types/hap-nodejs/HapCategories.js +64 -43
- package/build/lib/types/storage/SerializedHostType.js +15 -1
- package/build/lib/types/storage/StorageType.js +34 -10
- package/build/lib/unifi/ProtectDiscovery.js +80 -0
- package/build/lib/utils/AccessoryUtils.js +152 -112
- package/build/lib/utils/BridgeUtils.js +95 -39
- package/build/lib/utils/CharacteristicUtils.js +5 -49
- package/build/lib/utils/CharacteristicUtils2.js +5 -49
- package/build/lib/utils/CharacteristicUtilsBase.js +81 -0
- package/build/lib/utils/NodeStatusUtils.js +89 -40
- package/build/lib/utils/ServiceUtils.js +433 -368
- package/build/lib/utils/ServiceUtils2.js +519 -304
- package/build/lib/utils/index.js +11 -12
- package/build/nodes/bridge.html +206 -168
- package/build/nodes/bridge.js +27 -9
- package/build/nodes/locales/en-US/node-red-contrib-homekit-bridged.json +22 -0
- package/build/nodes/nrchkb.html +1753 -117
- package/build/nodes/nrchkb.js +66 -88
- package/build/nodes/plugin-instance.html +509 -0
- package/build/nodes/plugin-instance.js +46 -0
- package/build/nodes/service.html +557 -306
- package/build/nodes/service.js +5 -6
- package/build/nodes/service2.html +1735 -455
- package/build/nodes/service2.js +5 -8
- package/build/nodes/standalone.html +208 -176
- package/build/nodes/standalone.js +27 -9
- package/build/nodes/status.html +51 -18
- package/build/nodes/status.js +47 -41
- package/build/nodes/unifi-controller.html +92 -0
- package/build/nodes/unifi-controller.js +20 -0
- package/build/plugins/embedded/homebridge-camera-ffmpeg/index.js +479 -0
- package/build/plugins/embedded/homebridge-unifi-protect/index.js +521 -0
- package/build/plugins/embedded/index.js +58 -0
- package/build/plugins/nrchkb-homekit-plugins.js +17 -0
- package/build/plugins/registry/index.js +203 -0
- package/build/plugins/registry/types.js +16 -0
- package/build/scripts/migrate-homekit-service-flows.js +47 -0
- package/examples/demo/01 - ALL Demos single import.json +1885 -1885
- package/examples/demo/02 - Air Purifier.json +279 -279
- package/examples/demo/03 - Air Quality sensor with Battery.json +254 -254
- package/examples/demo/04 - Dimmable Bulb.json +172 -172
- package/examples/demo/05 - Color Bulb (HSV).json +195 -195
- package/examples/demo/06 - Fan (simple, 3 speeds).json +240 -240
- package/examples/demo/07 - Fan (with speed, oscillate, rotation direction).json +175 -175
- package/examples/demo/08 - CO2 detector.json +224 -224
- package/examples/demo/09 - CO (carbon monoxide) example.json +255 -255
- package/examples/demo/10 - Door window contact sensor.json +234 -234
- package/examples/demos (advanced)/01 - Television with inputs and speaker.json +541 -541
- package/examples/switch/01 - Plain Switch.json +178 -178
- package/package.json +95 -82
- package/build/lib/HAPHostNode.d.ts +0 -1
- package/build/lib/HAPServiceNode.d.ts +0 -1
- package/build/lib/HAPServiceNode2.d.ts +0 -1
- package/build/lib/NRCHKBError.d.ts +0 -3
- package/build/lib/Storage.d.ts +0 -30
- package/build/lib/api.d.ts +0 -1
- package/build/lib/hap/HAPCharacteristic.d.ts +0 -9
- package/build/lib/hap/HAPService.d.ts +0 -6
- package/build/lib/hap/eve-app/EveCharacteristics.d.ts +0 -20
- package/build/lib/hap/eve-app/EveServices.d.ts +0 -5
- package/build/lib/types/AccessoryInformationType.d.ts +0 -11
- package/build/lib/types/CameraConfigType.d.ts +0 -24
- package/build/lib/types/CustomCharacteristicType.d.ts +0 -6
- package/build/lib/types/HAPHostConfigType.d.ts +0 -22
- package/build/lib/types/HAPHostNodeType.d.ts +0 -14
- package/build/lib/types/HAPService2ConfigType.d.ts +0 -6
- package/build/lib/types/HAPService2NodeType.d.ts +0 -7
- package/build/lib/types/HAPServiceConfigType.d.ts +0 -26
- package/build/lib/types/HAPServiceNodeType.d.ts +0 -38
- package/build/lib/types/HAPStatusConfigType.d.ts +0 -5
- package/build/lib/types/HAPStatusNodeType.d.ts +0 -12
- package/build/lib/types/HostType.d.ts +0 -5
- package/build/lib/types/NodeType.d.ts +0 -3
- package/build/lib/types/PublishTimersType.d.ts +0 -4
- package/build/lib/types/hap-nodejs/HapAdaptiveLightingControllerMode.d.ts +0 -5
- package/build/lib/types/hap-nodejs/HapCategories.d.ts +0 -41
- package/build/lib/types/storage/SerializedHostType.d.ts +0 -5
- package/build/lib/types/storage/StorageType.d.ts +0 -8
- package/build/lib/utils/AccessoryUtils.d.ts +0 -1
- package/build/lib/utils/BridgeUtils.d.ts +0 -1
- package/build/lib/utils/CharacteristicUtils.d.ts +0 -1
- package/build/lib/utils/CharacteristicUtils2.d.ts +0 -1
- package/build/lib/utils/NodeStatusUtils.d.ts +0 -17
- package/build/lib/utils/ServiceUtils.d.ts +0 -1
- package/build/lib/utils/ServiceUtils2.d.ts +0 -1
- package/build/lib/utils/index.d.ts +0 -1
- package/build/nodes/bridge.d.ts +0 -1
- package/build/nodes/nrchkb.d.ts +0 -1
- package/build/nodes/service.d.ts +0 -1
- package/build/nodes/service2.d.ts +0 -1
- package/build/nodes/standalone.d.ts +0 -1
- package/build/nodes/status.d.ts +0 -1
|
@@ -1,317 +1,532 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
4
15
|
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
context: { key: this.displayName }
|
|
88
|
-
}, { oldValue, newValue }, connection);
|
|
89
|
-
};
|
|
90
|
-
if (node.config.useEventCallback) {
|
|
91
|
-
const callbackID = Storage_1.Storage.saveCallback({
|
|
92
|
-
event: "get",
|
|
93
|
-
callback: delayedCallback
|
|
94
|
-
});
|
|
95
|
-
log.debug(`Registered callback ${callbackID} for Characteristic ${characteristic.displayName}`);
|
|
96
|
-
output.call(this, allCharacteristics, {
|
|
97
|
-
name: "get",
|
|
98
|
-
context: { callbackID, key: this.displayName }
|
|
99
|
-
}, { oldValue }, connection);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
delayedCallback();
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
const onCharacteristicSet = (allCharacteristics) => function (newValue, callback, _context, connection) {
|
|
106
|
-
var _a;
|
|
107
|
-
try {
|
|
108
|
-
if (callback) {
|
|
109
|
-
callback(((_a = node.parentNode) !== null && _a !== void 0 ? _a : node).reachable
|
|
110
|
-
? null
|
|
111
|
-
: new hap_nodejs_1.HapStatusError(-70402));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
catch (_) { }
|
|
115
|
-
output.call(this, allCharacteristics, {
|
|
116
|
-
name: "set",
|
|
117
|
-
context: { key: this.displayName }
|
|
118
|
-
}, { newValue }, connection);
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var import_hap_nodejs = require("@homebridge/hap-nodejs");
|
|
25
|
+
var import_logger = require("@nrchkb/logger");
|
|
26
|
+
var import_embedded = require("../../plugins/embedded");
|
|
27
|
+
var import_registry = require("../../plugins/registry");
|
|
28
|
+
var import_NRCHKBError = __toESM(require("../NRCHKBError"));
|
|
29
|
+
var import_Storage = require("../Storage");
|
|
30
|
+
const buildServiceUtils = require("./ServiceUtils");
|
|
31
|
+
(0, import_embedded.registerEmbeddedPlugins)();
|
|
32
|
+
const describeContext = (context) => {
|
|
33
|
+
if (context === null) {
|
|
34
|
+
return "null";
|
|
35
|
+
}
|
|
36
|
+
if (context === void 0) {
|
|
37
|
+
return "undefined";
|
|
38
|
+
}
|
|
39
|
+
const type = typeof context;
|
|
40
|
+
if (type !== "object") {
|
|
41
|
+
return String(context);
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(context)) {
|
|
44
|
+
return `Array(${context.length})`;
|
|
45
|
+
}
|
|
46
|
+
const keys = Object.keys(context);
|
|
47
|
+
return keys.length > 0 ? `Object(${keys.slice(0, 5).join(",")}${keys.length > 5 ? ",..." : ""})` : "Object";
|
|
48
|
+
};
|
|
49
|
+
const describeSupported = (supported) => Array.from(supported).join("', '");
|
|
50
|
+
const isPluginEntry = (value) => {
|
|
51
|
+
if (typeof value !== "object" || value === null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const id = value.id;
|
|
55
|
+
return typeof id === "string" && id.trim().length > 0;
|
|
56
|
+
};
|
|
57
|
+
const parsePluginEntries = (value, log) => {
|
|
58
|
+
if (!value) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const normalize = (entries) => {
|
|
62
|
+
const validEntries = entries.filter(isPluginEntry);
|
|
63
|
+
if (validEntries.length !== entries.length) {
|
|
64
|
+
log.error("Skipping malformed plugin configuration entries.");
|
|
65
|
+
}
|
|
66
|
+
return validEntries;
|
|
67
|
+
};
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return normalize(value);
|
|
70
|
+
}
|
|
71
|
+
if (typeof value !== "string") {
|
|
72
|
+
log.error("Plugin configuration must be an array or JSON string.");
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(value);
|
|
77
|
+
return Array.isArray(parsed) ? normalize(parsed) : [];
|
|
78
|
+
} catch (error) {
|
|
79
|
+
log.error(`Failed to parse plugin configuration due to ${error}`);
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const buildServiceUtils2 = (node) => {
|
|
84
|
+
const log = (0, import_logger.logger)("NRCHKB", "ServiceUtils2", node.config.name, node);
|
|
85
|
+
(0, import_registry.registerNodeRedPlugins)(node.RED);
|
|
86
|
+
const ServiceUtilsLegacy = buildServiceUtils(node);
|
|
87
|
+
const { Service, Characteristic } = require("@homebridge/hap-nodejs");
|
|
88
|
+
const NO_RESPONSE_MSG = "NO_RESPONSE";
|
|
89
|
+
const isLegacyOutputMode = () => node.config.outputMode === "legacy";
|
|
90
|
+
const output = function(allCharacteristics, event, { context, oldValue, newValue }, connection) {
|
|
91
|
+
const eventObject = typeof event === "object" ? event : { name: event };
|
|
92
|
+
log.debug(
|
|
93
|
+
`${eventObject.name} event, oldValue: ${oldValue}, newValue: ${newValue}, connection ${connection?.sessionID}`
|
|
94
|
+
);
|
|
95
|
+
const msg = {
|
|
96
|
+
name: node.name,
|
|
97
|
+
topic: node.config.topic ? node.config.topic : node.topic_in
|
|
119
98
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
99
|
+
msg.payload = {};
|
|
100
|
+
const allChars = {};
|
|
101
|
+
for (const singleChar of allCharacteristics) {
|
|
102
|
+
const cKey = singleChar.constructor.name;
|
|
103
|
+
allChars[cKey] = singleChar.value;
|
|
104
|
+
}
|
|
105
|
+
msg.hap = {
|
|
106
|
+
event: eventObject,
|
|
107
|
+
allChars,
|
|
108
|
+
oldValue
|
|
128
109
|
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const topic = (_a = node.config.topic) !== null && _a !== void 0 ? _a : node.name;
|
|
143
|
-
if (node.config.filter && msg.topic !== topic) {
|
|
144
|
-
log.debug("msg.topic doesn't match configured value and filter is enabled. Dropping message.");
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
let context = null;
|
|
148
|
-
if (msg.payload.Context) {
|
|
149
|
-
context = msg.payload.Context;
|
|
150
|
-
delete msg.payload.Context;
|
|
151
|
-
}
|
|
152
|
-
node.topic_in = (_b = msg.topic) !== null && _b !== void 0 ? _b : '';
|
|
153
|
-
Object.keys(msg.payload).map((key) => {
|
|
154
|
-
var _a, _b, _c, _d, _e;
|
|
155
|
-
if (node.supported.indexOf(key) < 0) {
|
|
156
|
-
if (node.config.useEventCallback && Storage_1.Storage.uuid4Validate(key)) {
|
|
157
|
-
const callbackID = key;
|
|
158
|
-
const callbackValue = (_a = msg.payload) === null || _a === void 0 ? void 0 : _a[key];
|
|
159
|
-
const eventCallback = Storage_1.Storage.loadCallback(callbackID);
|
|
160
|
-
if (eventCallback) {
|
|
161
|
-
log.debug(`Calling ${eventCallback.event} callback ${callbackID}`);
|
|
162
|
-
eventCallback.callback(callbackValue);
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
log.error(`Callback ${callbackID} timeout`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else if (key === 'AdaptiveLightingController' &&
|
|
169
|
-
node.adaptiveLightingController) {
|
|
170
|
-
const value = (_b = msg.payload) === null || _b === void 0 ? void 0 : _b[key];
|
|
171
|
-
const event = value === null || value === void 0 ? void 0 : value.event;
|
|
172
|
-
if (event === 'disable') {
|
|
173
|
-
(_c = node.adaptiveLightingController) === null || _c === void 0 ? void 0 : _c.disableAdaptiveLighting();
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
log.error(`Instead of '${key}' try one of these characteristics: '${node.supported.join("', '")}'`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
const value = (_d = msg.payload) === null || _d === void 0 ? void 0 : _d[key];
|
|
182
|
-
const parentNode = (_e = node.parentNode) !== null && _e !== void 0 ? _e : node;
|
|
183
|
-
parentNode.reachable = value !== NO_RESPONSE_MSG;
|
|
184
|
-
const characteristic = node.service.getCharacteristic(Characteristic[key]);
|
|
185
|
-
if (context !== null) {
|
|
186
|
-
characteristic.setValue(value, context);
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
characteristic.setValue(value);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
};
|
|
194
|
-
const onClose = function (removed, done) {
|
|
195
|
-
const characteristics = node.service.characteristics.concat(node.service.optionalCharacteristics);
|
|
196
|
-
characteristics.forEach(function (characteristic) {
|
|
197
|
-
characteristic.removeListener('get', node.onCharacteristicGet);
|
|
198
|
-
characteristic.removeListener('set', node.onCharacteristicSet);
|
|
199
|
-
characteristic.removeListener('change', node.onCharacteristicChange);
|
|
110
|
+
if (context) {
|
|
111
|
+
msg.hap.context = context;
|
|
112
|
+
}
|
|
113
|
+
const key = this.constructor.name;
|
|
114
|
+
msg.hap.reachable = node.reachable ?? node.parentNode?.reachable;
|
|
115
|
+
if (msg.hap.reachable === false) {
|
|
116
|
+
;
|
|
117
|
+
[node, ...node.childNodes ?? []].forEach((n) => {
|
|
118
|
+
n.nodeStatusUtils.setStatus({
|
|
119
|
+
fill: "red",
|
|
120
|
+
shape: "ring",
|
|
121
|
+
text: "Not reachable",
|
|
122
|
+
type: "NO_RESPONSE"
|
|
200
123
|
});
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
msg.hap.newValue = newValue;
|
|
127
|
+
node.nodeStatusUtils.setStatus(
|
|
128
|
+
{
|
|
129
|
+
fill: "yellow",
|
|
130
|
+
shape: "dot",
|
|
131
|
+
text: `[${eventObject.name}] ${key}${newValue !== void 0 ? `: ${newValue}` : ""}`
|
|
132
|
+
},
|
|
133
|
+
3e3
|
|
134
|
+
);
|
|
135
|
+
node.childNodes?.forEach((n) => {
|
|
136
|
+
n.nodeStatusUtils.clearStatusByType("NO_RESPONSE");
|
|
137
|
+
});
|
|
138
|
+
node.parentNode?.nodeStatusUtils.clearStatusByType("NO_RESPONSE");
|
|
139
|
+
}
|
|
140
|
+
msg.payload[key] = newValue;
|
|
141
|
+
if (connection) {
|
|
142
|
+
msg.hap.session = {
|
|
143
|
+
sessionID: connection.sessionID,
|
|
144
|
+
username: connection.username,
|
|
145
|
+
remoteAddress: connection.remoteAddress,
|
|
146
|
+
localAddress: connection.localAddress,
|
|
147
|
+
httpPort: connection.remotePort
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
log.debug(
|
|
151
|
+
`${node.name} received ${eventObject.name} ${key}: ${newValue}`
|
|
152
|
+
);
|
|
153
|
+
if (connection || context || node.hostNode.config.allowMessagePassthrough) {
|
|
154
|
+
node.send(msg);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
const onCharacteristicGet = (allCharacteristics) => function(callback, _context, connection) {
|
|
158
|
+
if (isLegacyOutputMode()) {
|
|
159
|
+
ServiceUtilsLegacy.onCharacteristicGet.call(
|
|
160
|
+
this,
|
|
161
|
+
callback,
|
|
162
|
+
_context,
|
|
163
|
+
connection
|
|
164
|
+
);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const oldValue = this.value;
|
|
168
|
+
const delayedCallback = (value) => {
|
|
169
|
+
const newValue = value ?? this.value;
|
|
170
|
+
if (callback) {
|
|
171
|
+
try {
|
|
172
|
+
callback(
|
|
173
|
+
(node.parentNode ?? node).reachable ? null : new import_hap_nodejs.HapStatusError(
|
|
174
|
+
import_hap_nodejs.HAPStatus.SERVICE_COMMUNICATION_FAILURE
|
|
175
|
+
),
|
|
176
|
+
newValue
|
|
177
|
+
);
|
|
178
|
+
} catch (_) {
|
|
213
179
|
}
|
|
214
|
-
|
|
180
|
+
}
|
|
181
|
+
output.call(
|
|
182
|
+
this,
|
|
183
|
+
allCharacteristics,
|
|
184
|
+
{
|
|
185
|
+
name: import_hap_nodejs.CharacteristicEventTypes.GET,
|
|
186
|
+
context: { key: this.displayName }
|
|
187
|
+
},
|
|
188
|
+
{ oldValue, newValue },
|
|
189
|
+
connection
|
|
190
|
+
);
|
|
215
191
|
};
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
192
|
+
if (node.config.useEventCallback) {
|
|
193
|
+
const callbackID = import_Storage.Storage.saveCallback({
|
|
194
|
+
event: import_hap_nodejs.CharacteristicEventTypes.GET,
|
|
195
|
+
callback: delayedCallback
|
|
196
|
+
});
|
|
197
|
+
log.debug(
|
|
198
|
+
`Registered callback ${callbackID} for Characteristic ${this.displayName}`
|
|
199
|
+
);
|
|
200
|
+
output.call(
|
|
201
|
+
this,
|
|
202
|
+
allCharacteristics,
|
|
203
|
+
{
|
|
204
|
+
name: import_hap_nodejs.CharacteristicEventTypes.GET,
|
|
205
|
+
context: { callbackID, key: this.displayName }
|
|
206
|
+
},
|
|
207
|
+
{ oldValue },
|
|
208
|
+
connection
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
delayedCallback();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const onCharacteristicSet = (allCharacteristics) => function(newValue, callback, _context, connection) {
|
|
215
|
+
if (isLegacyOutputMode()) {
|
|
216
|
+
ServiceUtilsLegacy.onCharacteristicSet(allCharacteristics).call(
|
|
217
|
+
this,
|
|
218
|
+
newValue,
|
|
219
|
+
callback,
|
|
220
|
+
_context,
|
|
221
|
+
connection
|
|
222
|
+
);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
if (callback) {
|
|
227
|
+
callback(
|
|
228
|
+
(node.parentNode ?? node).reachable ? null : new import_hap_nodejs.HapStatusError(
|
|
229
|
+
import_hap_nodejs.HAPStatus.SERVICE_COMMUNICATION_FAILURE
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
} catch (_) {
|
|
234
|
+
}
|
|
235
|
+
output.call(
|
|
236
|
+
this,
|
|
237
|
+
allCharacteristics,
|
|
238
|
+
{
|
|
239
|
+
name: import_hap_nodejs.CharacteristicEventTypes.SET,
|
|
240
|
+
context: { key: this.displayName }
|
|
241
|
+
},
|
|
242
|
+
{ newValue },
|
|
243
|
+
connection
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
const onCharacteristicChange = (allCharacteristics) => function(change) {
|
|
247
|
+
if (isLegacyOutputMode()) {
|
|
248
|
+
ServiceUtilsLegacy.onCharacteristicChange(
|
|
249
|
+
allCharacteristics
|
|
250
|
+
).call(this, change);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const { oldValue, newValue, context, originator, reason } = change;
|
|
254
|
+
log.debug(
|
|
255
|
+
`onCharacteristicChange with reason: ${reason}, oldValue: ${oldValue}, newValue: ${newValue}, context ${describeContext(context)} on connection ${originator?.sessionID}`
|
|
256
|
+
);
|
|
257
|
+
if (oldValue !== newValue) {
|
|
258
|
+
output.call(
|
|
259
|
+
this,
|
|
260
|
+
allCharacteristics,
|
|
261
|
+
{
|
|
262
|
+
name: import_hap_nodejs.CharacteristicEventTypes.CHANGE,
|
|
263
|
+
context: { reason, key: this.displayName }
|
|
264
|
+
},
|
|
265
|
+
{ oldValue, newValue, context },
|
|
266
|
+
originator
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const onInput = (msg) => {
|
|
271
|
+
if (msg.payload) {
|
|
272
|
+
const type = typeof msg.payload;
|
|
273
|
+
if (type !== "object") {
|
|
274
|
+
log.error(`Invalid payload type: ${type}`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
log.error("Invalid message (payload missing)");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const topic = node.config.topic ?? node.name;
|
|
282
|
+
if (node.config.filter && msg.topic !== topic) {
|
|
283
|
+
log.debug(
|
|
284
|
+
"msg.topic doesn't match configured value and filter is enabled. Dropping message."
|
|
285
|
+
);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
let context = null;
|
|
289
|
+
if (msg.payload.Context) {
|
|
290
|
+
context = msg.payload.Context;
|
|
291
|
+
delete msg.payload.Context;
|
|
292
|
+
}
|
|
293
|
+
node.topic_in = msg.topic ?? "";
|
|
294
|
+
for (const key in msg.payload) {
|
|
295
|
+
if (!Object.hasOwn(msg.payload, key)) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!node.supported.has(key)) {
|
|
299
|
+
if (!isLegacyOutputMode() && node.config.useEventCallback && import_Storage.Storage.uuid4Validate(key)) {
|
|
300
|
+
const callbackID = key;
|
|
301
|
+
const callbackValue = msg.payload?.[key];
|
|
302
|
+
const eventCallback = import_Storage.Storage.loadCallback(callbackID);
|
|
303
|
+
if (eventCallback) {
|
|
304
|
+
log.debug(
|
|
305
|
+
`Calling ${eventCallback.event} callback ${callbackID}`
|
|
306
|
+
);
|
|
307
|
+
eventCallback.callback(callbackValue);
|
|
308
|
+
} else {
|
|
309
|
+
log.error(`Callback ${callbackID} timeout`);
|
|
310
|
+
}
|
|
311
|
+
} else if (key === "AdaptiveLightingController" && node.adaptiveLightingController) {
|
|
312
|
+
const value = msg.payload?.[key];
|
|
313
|
+
const event = value?.event;
|
|
314
|
+
if (event === "disable") {
|
|
315
|
+
node.adaptiveLightingController?.disableAdaptiveLighting();
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
log.error(
|
|
319
|
+
`Instead of '${key}' try one of these characteristics: '${describeSupported(node.supported)}'`
|
|
320
|
+
);
|
|
242
321
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
322
|
+
} else {
|
|
323
|
+
const value = msg.payload?.[key];
|
|
324
|
+
const normalizedValue = value === NO_RESPONSE_MSG ? false : value;
|
|
325
|
+
const parentNode = node.parentNode ?? node;
|
|
326
|
+
parentNode.reachable = value !== NO_RESPONSE_MSG;
|
|
327
|
+
const characteristic = node.service.getCharacteristic(
|
|
328
|
+
Characteristic[key]
|
|
329
|
+
);
|
|
330
|
+
if (context !== null) {
|
|
331
|
+
characteristic.setValue(normalizedValue, context);
|
|
332
|
+
} else {
|
|
333
|
+
characteristic.setValue(normalizedValue);
|
|
251
334
|
}
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
const onClose = (removed, done) => {
|
|
339
|
+
node.nrchkbClosing = true;
|
|
340
|
+
if (node.waitForParentTimer) {
|
|
341
|
+
clearTimeout(node.waitForParentTimer);
|
|
342
|
+
node.waitForParentTimer = void 0;
|
|
343
|
+
}
|
|
344
|
+
Object.values(node.publishTimers).forEach((timer) => {
|
|
345
|
+
clearTimeout(timer);
|
|
346
|
+
});
|
|
347
|
+
node.publishTimers = {};
|
|
348
|
+
const characteristics = node.service ? node.service.characteristics.concat(
|
|
349
|
+
node.service.optionalCharacteristics
|
|
350
|
+
) : [];
|
|
351
|
+
characteristics.forEach((characteristic) => {
|
|
352
|
+
characteristic.removeListener("get", node.onCharacteristicGet);
|
|
353
|
+
characteristic.removeListener("set", node.onCharacteristicSet);
|
|
354
|
+
characteristic.removeListener("change", node.onCharacteristicChange);
|
|
355
|
+
});
|
|
356
|
+
if (node.config.isParent && node.accessory && node.onIdentify) {
|
|
357
|
+
node.accessory.removeListener("identify", node.onIdentify);
|
|
358
|
+
}
|
|
359
|
+
if (removed && node.accessory) {
|
|
360
|
+
if (node.config.isParent) {
|
|
361
|
+
node.hostNode.host.removeBridgedAccessories([node.accessory]);
|
|
362
|
+
node.accessory.destroy();
|
|
363
|
+
} else if (node.service && node.parentService) {
|
|
364
|
+
node.accessory.removeService(node.service);
|
|
365
|
+
node.parentService.removeLinkedService(node.service);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
node.nodeStatusUtils.cleanup();
|
|
369
|
+
done();
|
|
370
|
+
};
|
|
371
|
+
const getOrCreate = async (accessory, serviceInformation, parentService) => {
|
|
372
|
+
const createAttachContext = (config) => ({
|
|
373
|
+
accessory,
|
|
374
|
+
config,
|
|
375
|
+
node,
|
|
376
|
+
RED: node.RED,
|
|
377
|
+
serviceInformation: {
|
|
378
|
+
name: serviceInformation.name,
|
|
379
|
+
UUID: serviceInformation.UUID,
|
|
380
|
+
serviceName: serviceInformation.serviceName,
|
|
381
|
+
config: serviceInformation.config
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
let pluginService = void 0;
|
|
385
|
+
const pluginSlots = [
|
|
386
|
+
serviceInformation.config.plugin1,
|
|
387
|
+
serviceInformation.config.plugin2,
|
|
388
|
+
serviceInformation.config.plugin3,
|
|
389
|
+
serviceInformation.config.plugin4,
|
|
390
|
+
serviceInformation.config.plugin5,
|
|
391
|
+
serviceInformation.config.plugin6,
|
|
392
|
+
serviceInformation.config.plugin7,
|
|
393
|
+
serviceInformation.config.plugin8
|
|
394
|
+
].filter(
|
|
395
|
+
(pluginNodeId) => typeof pluginNodeId === "string" && pluginNodeId.trim() !== ""
|
|
396
|
+
);
|
|
397
|
+
for (const pluginNodeId of pluginSlots) {
|
|
398
|
+
const pluginNode = node.RED.nodes.getNode(pluginNodeId);
|
|
399
|
+
if (!pluginNode) {
|
|
400
|
+
throw new import_NRCHKBError.default(
|
|
401
|
+
`Plugin config node "${pluginNodeId}" was not found.`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
if (typeof pluginNode.attachNRCHKBPlugin !== "function") {
|
|
405
|
+
throw new import_NRCHKBError.default(
|
|
406
|
+
`Config node "${pluginNodeId}" is not an NRCHKB plugin instance.`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
const attachedService = await pluginNode.attachNRCHKBPlugin(
|
|
410
|
+
createAttachContext({})
|
|
411
|
+
);
|
|
412
|
+
pluginService ??= attachedService;
|
|
413
|
+
}
|
|
414
|
+
const pluginEntries = parsePluginEntries(
|
|
415
|
+
serviceInformation.config.plugins,
|
|
416
|
+
log
|
|
417
|
+
);
|
|
418
|
+
if (pluginEntries.length) {
|
|
419
|
+
const plugins = pluginEntries;
|
|
420
|
+
for (const pluginConfig of plugins) {
|
|
421
|
+
const pluginDefinition = (0, import_registry.getPlugin)(pluginConfig.id);
|
|
422
|
+
if (!pluginDefinition) {
|
|
423
|
+
throw new import_NRCHKBError.default(
|
|
424
|
+
`Camera plugin "${pluginConfig.id}" is not registered.`
|
|
425
|
+
);
|
|
265
426
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
log.debug('Waiting for Parent Service');
|
|
269
|
-
return new Promise((resolve) => {
|
|
270
|
-
node.nodeStatusUtils.setStatus({
|
|
271
|
-
fill: 'blue',
|
|
272
|
-
shape: 'dot',
|
|
273
|
-
text: 'Waiting for Parent Service'
|
|
274
|
-
});
|
|
275
|
-
const checkAndWait = () => {
|
|
276
|
-
const parentNode = node.RED.nodes.getNode(node.config.parentService);
|
|
277
|
-
if (parentNode && parentNode.configured) {
|
|
278
|
-
resolve(parentNode);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
setTimeout(checkAndWait, 1000);
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
checkAndWait();
|
|
285
|
-
}).catch((error) => {
|
|
286
|
-
log.error(`Waiting for Parent Service failed due to: ${error}`);
|
|
287
|
-
throw new NRCHKBError_1.default(error);
|
|
427
|
+
const attachedService = await pluginDefinition.factory.attach({
|
|
428
|
+
...createAttachContext(pluginConfig.config)
|
|
288
429
|
});
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
430
|
+
pluginService ??= attachedService;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (pluginService) {
|
|
434
|
+
return pluginService;
|
|
435
|
+
}
|
|
436
|
+
const serviceName = serviceInformation.serviceName === "Camera" ? "CameraRTPStreamManagement" : serviceInformation.serviceName;
|
|
437
|
+
const ServiceConstructor = Service[serviceName];
|
|
438
|
+
if (typeof ServiceConstructor !== "function") {
|
|
439
|
+
throw new import_NRCHKBError.default(
|
|
440
|
+
`Unknown HomeKit service "${serviceInformation.serviceName}".`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
const newService = new ServiceConstructor(
|
|
444
|
+
serviceInformation.name,
|
|
445
|
+
serviceInformation.UUID
|
|
446
|
+
);
|
|
447
|
+
log.debug(
|
|
448
|
+
`Looking for service with UUID ${serviceInformation.UUID} ...`
|
|
449
|
+
);
|
|
450
|
+
let service = accessory.services.find(
|
|
451
|
+
(service2) => {
|
|
452
|
+
return newService.subtype === service2.subtype;
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
if (service && newService.UUID !== service.UUID) {
|
|
456
|
+
log.debug("... service type changed! Removing the old service.");
|
|
457
|
+
accessory.removeService(service);
|
|
458
|
+
service = void 0;
|
|
459
|
+
}
|
|
460
|
+
if (!service) {
|
|
461
|
+
log.debug(
|
|
462
|
+
`... didn't find it. Adding new ${serviceInformation.serviceName} service.`
|
|
463
|
+
);
|
|
464
|
+
service = accessory.addService(newService);
|
|
465
|
+
} else {
|
|
466
|
+
log.debug("... found it! Updating it.");
|
|
467
|
+
service.getCharacteristic(Characteristic.Name).setValue(serviceInformation.name);
|
|
468
|
+
}
|
|
469
|
+
if (parentService) {
|
|
470
|
+
if (service) {
|
|
471
|
+
log.debug("... and linking service to parent.");
|
|
472
|
+
parentService.addLinkedService(service);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return service ?? newService;
|
|
476
|
+
};
|
|
477
|
+
const waitForParent = () => {
|
|
478
|
+
log.debug("Waiting for Parent Service");
|
|
479
|
+
return new Promise((resolve) => {
|
|
480
|
+
node.nodeStatusUtils.setStatus({
|
|
481
|
+
fill: "blue",
|
|
482
|
+
shape: "dot",
|
|
483
|
+
text: "Waiting for Parent Service"
|
|
484
|
+
});
|
|
485
|
+
const checkAndWait = () => {
|
|
486
|
+
const parentNode = node.RED.nodes.getNode(
|
|
487
|
+
node.config.parentService
|
|
488
|
+
);
|
|
489
|
+
if (parentNode?.configured) {
|
|
490
|
+
node.waitForParentTimer = void 0;
|
|
491
|
+
resolve(parentNode);
|
|
492
|
+
} else {
|
|
493
|
+
node.waitForParentTimer = setTimeout(checkAndWait, 1e3);
|
|
293
494
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
495
|
+
};
|
|
496
|
+
checkAndWait();
|
|
497
|
+
}).catch((error) => {
|
|
498
|
+
log.error(`Waiting for Parent Service failed due to: ${error}`);
|
|
499
|
+
throw new import_NRCHKBError.default(error);
|
|
500
|
+
});
|
|
501
|
+
};
|
|
502
|
+
const handleWaitForSetup = (config, msg, resolve) => {
|
|
503
|
+
if (node.setupDone) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (msg && Object.hasOwn(msg, "payload") && msg.payload && typeof msg.payload === "object" && Object.hasOwn(msg.payload, "nrchkb") && msg.payload.nrchkb && typeof msg.payload.nrchkb === "object" && Object.hasOwn(msg.payload.nrchkb, "setup")) {
|
|
507
|
+
node.setupDone = true;
|
|
508
|
+
const newConfig = {
|
|
509
|
+
...config,
|
|
510
|
+
...msg.payload.nrchkb.setup
|
|
511
|
+
};
|
|
512
|
+
node.removeListener("input", node.handleWaitForSetup);
|
|
513
|
+
resolve(newConfig);
|
|
514
|
+
} else {
|
|
515
|
+
log.error(
|
|
516
|
+
'Invalid message (required {"payload":{"nrchkb":{"setup":{}}}})'
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
return {
|
|
521
|
+
getOrCreate,
|
|
522
|
+
onCharacteristicGet,
|
|
523
|
+
onCharacteristicSet,
|
|
524
|
+
onCharacteristicChange,
|
|
525
|
+
onInput,
|
|
526
|
+
onClose,
|
|
527
|
+
waitForParent,
|
|
528
|
+
handleWaitForSetup,
|
|
529
|
+
configureAdaptiveLightning: ServiceUtilsLegacy.configureAdaptiveLightning
|
|
530
|
+
};
|
|
317
531
|
};
|
|
532
|
+
module.exports = buildServiceUtils2;
|