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